KTOR-8938 Inherit server coroutine context in WebSocket session - #5426
Conversation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The WebSocket session's coroutineContext was built solely from the engine dispatcher, dropping custom CoroutineContext elements from the server/call scope. Use call.coroutineContext as the base so user-provided elements are preserved. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughRecomposes coroutine contexts used during WebSocket and servlet upgrade flows (reorders and optionally includes parent Job), adds Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
📝 Coding Plan
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 |
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Strip the call's Job before merging its context, and restore the original Job from the engine's processing coroutine. This ensures custom context elements are inherited without changing the WebSocket session's Job parent-child relationship. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use call.coroutineContext directly (with its Job) so that the WebSocket session is a proper child of the server's coroutine scope. This ensures cancellation propagates correctly from server to WebSocket sessions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tomcat's servlet-based WebSocket upgrade creates an upgradeJob with no parent connection to the server scope, so server stop does not propagate cancellation to WebSocket sessions. This is a pre-existing Tomcat limitation unrelated to this fix. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Pass the servlet's SupervisorJob as parent for the upgrade handler's Job, ensuring cancellation propagates when the servlet is destroyed. Use onCancelling=true for WebConnection cleanup to break the deadlock between blocked IO reads and Job completion. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
server.stop() calls runBlocking internally via runBlockingBridge, which throws on JS and WASM platforms. Skip the test on those targets with a runtime platform check. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@ktor-server/ktor-server-servlet/jvm/src/io/ktor/server/servlet/ServletUpgrade.kt`:
- Around line 77-79: The class-level `@OptIn`(InternalCoroutinesApi::class) on
ServletUpgradeHandler is too broad; remove it from the class declaration and
instead annotate only the init override that calls
job.invokeOnCompletion(onCancelling = true) with
`@OptIn`(InternalCoroutinesApi::class). Update the ServletUpgradeHandler
declaration to keep `@InternalAPI` only, then add `@OptIn` on the override fun
init(session: HttpSession) (or the init method that contains
job.invokeOnCompletion(onCancelling = true)) so the opt-in scope is narrowed to
that specific usage.
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 94707925-b427-4e64-a9bf-9e2e76fd31bd
📒 Files selected for processing (6)
ktor-server/ktor-server-plugins/ktor-server-websockets/common/src/io/ktor/server/websocket/WebSocketUpgrade.ktktor-server/ktor-server-servlet-jakarta/jvm/src/io/ktor/server/servlet/jakarta/AsyncServlet.ktktor-server/ktor-server-servlet-jakarta/jvm/src/io/ktor/server/servlet/jakarta/ServletUpgrade.ktktor-server/ktor-server-servlet/jvm/src/io/ktor/server/servlet/AsyncServlet.ktktor-server/ktor-server-servlet/jvm/src/io/ktor/server/servlet/ServletUpgrade.ktktor-server/ktor-server-test-suites/common/src/io/ktor/server/testing/suites/WebSocketEngineSuite.kt
|
Addressed CI feedback (iteration 1):
Remaining failures (pre-existing, not caused by this PR):
|
…ethod Move the annotation from class-level to the specific method that uses invokeOnCompletion(onCancelling = true), in both servlet and servlet-jakarta upgrade handlers. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Addressed review feedback:
CI status after push:
|
PR Review Iteration SummaryReview CommentsAll review feedback has been addressed:
CI Failures AnalysisAll 6 CI failures are pre-existing on the
Status
|
- Replace server.stop() with server.stopSuspend() as suggested by reviewer - Remove JS/WASM platform check since stopSuspend doesn't use runBlocking - Remove open modifier from test function (no longer needed for overrides) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Addressed review feedback (iteration 2):
CI status after push:
|
|
Could you also check if this PR fixes this issue as well?
|
|
Osip Fatkullin (@osipxd), I checked and problem is different |
Summary
WebSocketUpgrade.upgrade()was building the WebSocket session's coroutine context from only the engine dispatcher, dropping customCoroutineContext.Elements from the server/call scopecall.coroutineContextas the base context so user-provided elements are preserved while the engine dispatcher still overrides the dispatcherTest plan
testWebSocketSessionInheritsServerCoroutineContextinWebSocketEngineSuitethat passes a custom context element viaparentCoroutineContextand verifies it's accessible inside thewebSockethandler'scoroutineContext🤖 Generated with Claude Code