Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit cc88b0f

Browse files
fix(coderd): ensure inbox WebSocket is closed when client disconnects… (#21685)
… (#21652) Relates to #19715 This is similar to #19711 This endpoint works by doing the following: - Subscribing to the database's with pubsub - Accepts a WebSocket upgrade - Starts a `httpapi.Heartbeat` - Creates a json encoder - **Infinitely loops waiting for notification until request context cancelled** The critical issue here is that `httpapi.Heartbeat` silently fails when the client has disconnected. This means we never cancel the request context, leaving the WebSocket alive until we receive a notification from the database and fail to write that down the pipe. By replacing usage of `httpapi.Heartbeat` with `httpapi.HeartbeatClose`, we cancel the context _when the heartbeat fails to write_ due to the client disconnecting. This allows us to cleanup without waiting for a notification to come through the pubsub channel. (cherry picked from commit 409360c) <!-- If you have used AI to produce some or all of this PR, please ensure you have read our [AI Contribution guidelines](https://coder.com/docs/about/contributing/AI_CONTRIBUTING) before submitting. --> Co-authored-by: Danielle Maywood <[email protected]>
1 parent 93ab5d4 commit cc88b0f

File tree

1 file changed

+15
-5
lines changed

1 file changed

+15
-5
lines changed

coderd/inboxnotifications.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121
"github.com/coder/coder/v2/coderd/pubsub"
2222
markdown "github.com/coder/coder/v2/coderd/render"
2323
"github.com/coder/coder/v2/codersdk"
24-
"github.com/coder/coder/v2/codersdk/wsjson"
2524
"github.com/coder/websocket"
2625
)
2726

@@ -127,6 +126,7 @@ func (api *API) watchInboxNotifications(rw http.ResponseWriter, r *http.Request)
127126
templates = p.UUIDs(vals, []uuid.UUID{}, "templates")
128127
readStatus = p.String(vals, "all", "read_status")
129128
format = p.String(vals, notificationFormatMarkdown, "format")
129+
logger = api.Logger.Named("inbox_notifications_watcher")
130130
)
131131
p.ErrorExcessParams(vals)
132132
if len(p.Errors) > 0 {
@@ -214,11 +214,17 @@ func (api *API) watchInboxNotifications(rw http.ResponseWriter, r *http.Request)
214214
return
215215
}
216216

217-
go httpapi.Heartbeat(ctx, conn)
218-
defer conn.Close(websocket.StatusNormalClosure, "connection closed")
217+
ctx, cancel := context.WithCancel(ctx)
218+
defer cancel()
219219

220-
encoder := wsjson.NewEncoder[codersdk.GetInboxNotificationResponse](conn, websocket.MessageText)
221-
defer encoder.Close(websocket.StatusNormalClosure)
220+
_ = conn.CloseRead(context.Background())
221+
222+
ctx, wsNetConn := codersdk.WebsocketNetConn(ctx, conn, websocket.MessageText)
223+
defer wsNetConn.Close()
224+
225+
go httpapi.HeartbeatClose(ctx, logger, cancel, conn)
226+
227+
encoder := json.NewEncoder(wsNetConn)
222228

223229
// Log the request immediately instead of after it completes.
224230
if rl := loggermw.RequestLoggerFromContext(ctx); rl != nil {
@@ -227,8 +233,12 @@ func (api *API) watchInboxNotifications(rw http.ResponseWriter, r *http.Request)
227233

228234
for {
229235
select {
236+
case <-api.ctx.Done():
237+
return
238+
230239
case <-ctx.Done():
231240
return
241+
232242
case notif := <-notificationCh:
233243
unreadCount, err := api.Database.CountUnreadInboxNotificationsByUserID(ctx, apikey.UserID)
234244
if err != nil {

0 commit comments

Comments
 (0)