mirror of
https://github.com/binwiederhier/ntfy.git
synced 2026-05-09 08:26:00 +02:00
[PR #1598] [MERGED] Fix panic in handleSubscribeHTTP when client disconnects during publish #1656
Labels
No labels
ai-generated
android-app
android-app
android-app
🪲 bug
build
build
dependencies
docs
enhancement
enhancement
🔥 HOT
in-progress 🏃
ios
prio:low
prio:low
pull-request
question
🔒 security
server
server
unified-push
web-app
website
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
starred/ntfy#1656
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
📋 Pull Request Information
Original PR: https://github.com/binwiederhier/ntfy/pull/1598
Author: @binwiederhier
Created: 2/8/2026
Status: ✅ Merged
Merged: 2/8/2026
Merged by: @binwiederhier
Base:
main← Head:crashfix📝 Commits (1)
3647d39Fix panic in handleSubscribeHTTP when client disconnects during publish📊 Changes
1 file changed (+11 additions, -5 deletions)
View changed files
📝
server/server.go(+11 -5)📄 Description
(This PR is AI generated, but a human [me] has verified that it is accurate)
Summary
handleSubscribeHTTPcaused by a race between atopic.Publishgoroutine flushing the HTTP response writer and the handler returning after client disconnect.wlock.TryLock()hack with a properwlock.Lock()+closedflag pattern that correctly waits for in-flight writes to finish and prevents future writes to a cleaned-up response writer.Root cause
topic.Publish()copies the subscribers map and calls each subscriber in its own goroutine. When a client disconnects:handleSubscribeHTTPreturns, triggering deferred cleanupPublishgoroutine may still be in-flight, callingFlush()on the now-invalid writerThe old
TryLock()approach had two failure modes:subrunning): futuresub()calls deadlock onwlock.Lock()(goroutine leak, since the lock is never released)subis running): the handler returns immediately without waiting, and Go cleans up the writer whilesubis still flushing — exactly the panicFix
The new approach:
wlock.Lock()to wait for any in-flightsub()call to finish, then setsclosed = truesub()function checks theclosedflag after acquiring the lock — if the connection has been closed, it returns without touching the response writerThis guarantees the response writer is never accessed after the handler returns.
Stack trace this fixes
Test plan
go vet ./server/passesmake check🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.