fix(service, routing): sanitize auth errors, fix response header order, and pass parent ctx - #4709
Conversation
…r, and pass parent ctx
|
Geekatplay Dev seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account. You have signed the CLA already but the status is still pending? Let us recheck it. |
| func (r *RedisRouter) ClearRoomState(ctx context.Context, roomName livekit.RoomName) error { | ||
| if err := r.rc.HDel(ctx, NodeRoomKey, string(roomName)).Err(); err != nil { | ||
| return errors.Wrap(err, "could not clear room state") |
There was a problem hiding this comment.
🔴 Room-to-server routing records may stop being cleaned up when a room closes
The routing cleanup for a closed room now runs with the session's request context (ClearRoomState(ctx, ...) at pkg/routing/redisrouter.go:116-117) which is typically already cancelled by the time the room actually closes, so the leftover room-to-server mapping is never deleted.
Impact: Stale routing entries linger in Redis and can misdirect later attempts to create or join a room with the same name to a node that no longer hosts it.
Why the passed context is usually already cancelled at cleanup time
ClearRoomState is only ever invoked from RoomManager.deleteRoom (pkg/service/roommanager.go:204), which itself only runs inside the room's OnClose callback (pkg/service/roommanager.go:700). The ctx captured by that closure is the context of the participant/session that first created the room via getOrCreateRoom (pkg/service/roommanager.go:642, called from StartSession/CreateRoom with request-scoped contexts originating from HandleSession at pkg/service/signal.go:116). A room's OnClose fires when it becomes empty (all participants gone), by which point the creating session's context is almost always already done. Previously the code deliberately used context.Background() so the Redis HDel on NodeRoomKey always executed; with the passed ctx cancelled, HDel returns context canceled and the room_node_map entry is left behind. This mirrors the reasoning already documented for UnregisterNode at pkg/routing/redisrouter.go:81-82 ("could be called after Stop(), so we'd want to use an unrelated context").
Prompt for agents
ClearRoomState in pkg/routing/redisrouter.go was changed to use the passed ctx instead of context.Background() for the Redis HDel on NodeRoomKey. However, the only caller path is RoomManager.deleteRoom (pkg/service/roommanager.go:204), which runs exclusively inside the room's OnClose callback (pkg/service/roommanager.go:700). The ctx captured there is the request/session context of the participant that first created the room (via getOrCreateRoom / StartSession / CreateRoom). By the time the room closes (becomes empty), that context is typically already cancelled, so HDel fails with context canceled and the room->node routing entry is never removed, leaving stale state in Redis. Consider either keeping context.Background() for the Redis HDel here (as UnregisterNode does), or having the caller pass a non-cancellable context (e.g. context.WithoutCancel(ctx)) for the cleanup path so that cancellation of the originating session does not prevent routing cleanup. If tracing/timeout propagation is desired, context.WithoutCancel combined with a fresh timeout would preserve trace values without inheriting cancellation.
Was this helpful? React with 👍 or 👎 to provide feedback.
Fix
Content-Typeheader ordering inHandleErrorJson(pkg/service/utils.go)Right now
w.Header().Add("Content-type", ...)is called afterhandleError()(which callsw.WriteHeader()) and after writing the response body. In Go'snet/http, any header modifications afterWriteHeaderor writing the body get ignored by the runtime. Movedw.Header().Set("Content-Type", "application/json")to before headers are written to the connection.Sanitize HTTP 401 error payloads in
APIKeyAuthMiddleware(pkg/service/auth.go)Currently when token parsing or verification fails in auth middleware, the raw
authTokenstring and API key are concatenated directly into the error payload returned in the 401 body (errors.New("invalid token: "+authToken...)). This can reflect raw tokens back to clients or proxy logs unnecessarily. Swapped these to use the already defined package errors (ErrInvalidAPIKeyandErrInvalidAuthorizationToken).Pass caller
ctxinRedisRouter.ClearRoomState(pkg/routing/redisrouter.go)ClearRoomStatewas accepting_ context.Contextbut hardcodingcontext.Background()for the RedisHDelcall. Replacedcontext.Background()with the passedctxso parent cancellation, timeouts, and tracing spans are preserved.Let me know if anything looks off or if you'd prefer any of these split out into separate PRs. Thanks!