Repository navigation
fix(streamable_http): close active sessions before Shutdown - #926
Conversation
StreamableHTTPServer.Shutdown() previously called http.Server.Shutdown while long-lived GET listeners were still blocked, so graceful shutdown could hang until clients disconnected. Mirror the SSE server behavior by tracking a per-session done signal, exposing CloseSessions(), and invoking it from Shutdown. Fixes mark3labs#922
|
Connected to Huly®: MCP_G-490 |
WalkthroughStreamable HTTP shutdown now signals active sessions, terminates their IDs, cleans transport state, and allows long-lived GET handlers to exit. Regression tests cover active-connection shutdown and explicit session cleanup. ChangesStreamable HTTP session shutdown
Estimated code review effort: 3 (Moderate) | ~25 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
server/streamable_http.go (1)
456-462: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winDrain late sessions during shutdown
CloseSessions()runs beforesrv.Shutdown(ctx), so a GET accepted in that gap can register after the snapshot and never receivecloseDone(). That long-lived handler will keepShutdownwaiting untilctxexpires. Re-runCloseSessions()while shutdown is in progress so newly-registered sessions are drained too.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@server/streamable_http.go` around lines 456 - 462, The Shutdown method closes sessions only once before the underlying server shutdown, allowing late-registered GET sessions to remain open. Update StreamableHTTPServer.Shutdown to invoke CloseSessions again while srv.Shutdown(ctx) is in progress, ensuring sessions accepted during the shutdown window are drained before completion or context expiry.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@server/streamable_http_test.go`:
- Around line 2988-3025: Update TestStreamableHTTP_ShutdownWithActiveConnection
to start the streamable HTTP server through its Start() path, or inject a real
*http.Server, instead of relying on httptest.NewServer(httpServer) with a nil
internal server. Keep the active GET connection and shutdown assertions,
ensuring Shutdown() reaches the underlying srv.Shutdown(ctx) code path.
In `@server/streamable_http.go`:
- Around line 473-501: Update CloseSessions to accept the caller’s context and
pass it to cleanupSessionState instead of creating context.Background(),
preserving shutdown deadlines and cancellation for unregister hooks. Update the
Shutdown call site and any related callers to propagate the same ctx; optionally
log failures returned by mgr.Terminate while retaining termination and cleanup
behavior.
---
Outside diff comments:
In `@server/streamable_http.go`:
- Around line 456-462: The Shutdown method closes sessions only once before the
underlying server shutdown, allowing late-registered GET sessions to remain
open. Update StreamableHTTPServer.Shutdown to invoke CloseSessions again while
srv.Shutdown(ctx) is in progress, ensuring sessions accepted during the shutdown
window are drained before completion or context expiry.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 57f1c5cc-e520-4954-a117-8034143ae327
📒 Files selected for processing (2)
server/streamable_http.goserver/streamable_http_test.go
|
@syf2211 can you please address the coderabbit comments? |
…utdown test - Propagate caller context through CloseSessions into cleanupSessionState - Drain late-registered sessions while srv.Shutdown is in progress - Log Terminate failures during session close - Exercise Shutdown via injected *http.Server in active-connection test
|
Addressed the CodeRabbit feedback in 660e25f:
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
server/streamable_http_test.go (1)
3085-3089: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueUse context-aware request for consistency.
http.Getat line 3085 doesn't use a context, while the earlier request at line 3062 useshttp.NewRequestWithContext(t.Context(), ...). Prefer the context-aware variant for consistency and to ensure the request is cancelled if the test times out.♻️ Proposed refactor
- resp2, err := http.Get(ts.URL) + req2, err := http.NewRequestWithContext(t.Context(), http.MethodGet, ts.URL, nil) require.NoError(t, err) + resp2, err := http.DefaultClient.Do(req2) + require.NoError(t, err) defer resp2.Body.Close() require.Equal(t, http.StatusOK, resp2.StatusCode)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@server/streamable_http_test.go` around lines 3085 - 3089, Replace the contextless http.Get call in the test with an http.NewRequestWithContext request using t.Context(), then execute it through the appropriate HTTP client while preserving the existing response status assertion and body cleanup.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@server/streamable_http_test.go`:
- Around line 3008-3019: Update the shutdown context creation in the t.Cleanup
closure to use t.Context() instead of context.Background(), and verify this
remains valid during cleanup under the project’s Go version. If t.Context() is
already cancelled when cleanup executes and prevents httpServer.Shutdown from
waiting, retain context.Background() and add the minimal usetesting suppression
for that line.
---
Nitpick comments:
In `@server/streamable_http_test.go`:
- Around line 3085-3089: Replace the contextless http.Get call in the test with
an http.NewRequestWithContext request using t.Context(), then execute it through
the appropriate HTTP client while preserving the existing response status
assertion and body cleanup.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 0ccb4812-5f2e-48b8-9489-c5d29e0b3ac2
📒 Files selected for processing (2)
server/streamable_http.goserver/streamable_http_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
- server/streamable_http.go
|
Addressed the CodeRabbit |
Summary
Close active streamable HTTP listening sessions before calling
http.Server.Shutdown, so graceful shutdown does not hang while clients remain connected.Motivation
Fixes #922.
StreamableHTTPServer.Shutdown()could block indefinitely when long-lived GET handlers were still open, becausehttp.Server.Shutdownwaits for active handlers to finish.Changes
donesignal tostreamableHttpSessionwithcloseDone()handleGetreturn when the session is closedCloseSessions()to terminate active listeners and clean up session stateCloseSessions()fromShutdown()before stopping the HTTP serverCloseSessions()Tests
go test ./server -run 'TestStreamableHTTP_ShutdownWithActiveConnection|TestStreamableHTTP_CloseSessions' -count=1— PASSgo test ./server -count=1— PASSNotes
Mirrors the existing SSE server shutdown pattern (
CloseSessions+Shutdown).Summary by CodeRabbit