Skip to content

Commit 72f3a0d

Browse files
silverwindclaude
andcommitted
Retry logout event delivery briefly for racing client connections
When a user has multiple tabs open and one triggers logout while another is still establishing its SSE connection, the previous SendMessageBlocking would silently drop the event because no messenger was registered yet. Add SendMessageBlockingWithRetry which polls up to a deadline for a messenger to appear, and use it from SignOut with a 500ms budget. Co-Authored-By: Claude (Opus 4.7) <noreply@anthropic.com>
1 parent aa0707c commit 72f3a0d

2 files changed

Lines changed: 26 additions & 2 deletions

File tree

modules/eventsource/manager.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package eventsource
55

66
import (
77
"sync"
8+
"time"
89
)
910

1011
// Manager manages the eventsource Messengers
@@ -87,3 +88,23 @@ func (m *Manager) SendMessageBlocking(uid int64, message *Event) {
8788
messenger.SendMessageBlocking(message)
8889
}
8990
}
91+
92+
// SendMessageBlockingWithRetry sends a message, retrying for up to retry duration
93+
// if no messenger is registered yet. Useful for events like logout where a client
94+
// tab may still be establishing its SSE connection when another tab triggers the event.
95+
func (m *Manager) SendMessageBlockingWithRetry(uid int64, message *Event, retry time.Duration) {
96+
deadline := time.Now().Add(retry)
97+
for {
98+
m.mutex.Lock()
99+
messenger, ok := m.messengers[uid]
100+
m.mutex.Unlock()
101+
if ok {
102+
messenger.SendMessageBlocking(message)
103+
return
104+
}
105+
if time.Now().After(deadline) {
106+
return
107+
}
108+
time.Sleep(20 * time.Millisecond)
109+
}
110+
}

routers/web/auth/auth.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"net/http"
1212
"net/url"
1313
"strings"
14+
"time"
1415

1516
"code.gitea.io/gitea/models/auth"
1617
"code.gitea.io/gitea/models/db"
@@ -471,10 +472,12 @@ func HandleSignOut(ctx *context.Context) {
471472
// SignOut sign out from login status
472473
func SignOut(ctx *context.Context) {
473474
if ctx.Doer != nil {
474-
eventsource.GetManager().SendMessageBlocking(ctx.Doer.ID, &eventsource.Event{
475+
// Retry briefly in case another tab is still establishing its SSE connection
476+
// when this logout fires — otherwise the event would be silently dropped.
477+
eventsource.GetManager().SendMessageBlockingWithRetry(ctx.Doer.ID, &eventsource.Event{
475478
Name: "logout",
476479
Data: ctx.Session.ID(),
477-
})
480+
}, 500*time.Millisecond)
478481
}
479482

480483
// prepare the sign-out URL before destroying the session

0 commit comments

Comments
 (0)