Skip to content

Conversation

@zibet27
Copy link
Collaborator

@zibet27 zibet27 commented Nov 12, 2025

Subsystem
Server Netty

Motivation
KTOR-8671 Netty: RejectedExecutionException during shutdown on MacOS when dev mode is enabled

Solution

Actually, the problem is reproducible even without dev mode. The problem is that the channels were closed after the workers:
Other small improvements that didn't cause problems yet, but can be dangerous:

  • Shouldn't we cancel the call coroutine when the handler is unregistered
  • Stop writing a response to an inactive channel
  • Don't allow the engine to stop multiple times
  • make sure all shutdown() methods are called and the exception is logged properly(.await() was ignoring any errors)

PS:
I can't reproduce the reported problem in unit tests.
I reproduced another RejectedExecutionException when trying to stop the server, which is writing a response, but the error is only logged (I suppose it's done by Netty's internal logger, which I can't mock properly).
That's why the PR doesn't contain any tests, verified by local Maven publication.
Do you have advice here?

@zibet27 zibet27 self-assigned this Nov 12, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 12, 2025

Walkthrough

This pull request modifies the Netty server engine to improve shutdown robustness and response-sending safety. Changes include adding exception handling during engine stop, introducing channel-active checks before responding, updating test infrastructure to use real-time aware test runners, and adjusting test mocks for shutdown completion handling.

Changes

Cohort / File(s) Summary
Engine Shutdown & Exception Handling
ktor-server/ktor-server-netty/jvm/src/io/ktor/server/netty/NettyApplicationEngine.kt
Adds private inline withStopException() helper to wrap and log exceptions during shutdown. Modifies stop() to perform per-channel closes, computes shutdown event groups upfront, and syncs channel and worker group closures within exception-safe blocks.
Response Sending Safety
ktor-server/ktor-server-netty/jvm/src/io/ktor/server/netty/NettyApplicationResponse.kt
Introduces private canRespond property that validates both response state and Netty channel activeness. Replaces responseMessageSent checks in respondFromBytes() and sendResponse() guards with canRespond checks.
Test Framework Updates
ktor-server/ktor-server-netty/jvm/test/io/ktor/tests/server/netty/NettyEngineTest.kt, NettyConfigurationTest.kt
Simplifies imports in NettyEngineTest (replaces specific HTTP/2 header classes with wildcard import), adjusts HTTP/1.1 header handling from substring to slice. Updates NettyConfigurationTest mock to use sync() instead of await() for shutdown completion.
Test Execution Migration
ktor-server/ktor-server-netty/jvm/test/io/ktor/tests/server/netty/NettySpecificTest.kt
Refactors multiple test methods (testNoLeakWithoutStartAndStop, testStartMultipleConnectorsOnUsedPort, contentLengthAndTransferEncodingAreSafelyRemoved, badRequestOnInvalidQueryString) to execute under runTestWithRealTime() wrapper and adds timeout handling around async operations.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Engine shutdown logic in NettyApplicationEngine.kt requires careful verification of exception handling, channel lifecycle, and event group synchronization order
  • Response state validation changes in NettyApplicationResponse.kt need scrutiny for race conditions and edge cases involving channel state transitions
  • Test refactoring scope across three test files with varying complexity—real-time test runner integration requires understanding test framework semantics and timing assumptions
  • Async/sync API changes in test mocking (await → sync) may affect shutdown timing verification reliability

Possibly related PRs

Suggested labels

bug

Suggested reviewers

  • osipxd
  • bjhham

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.11% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly summarizes the main change: fixing a rejected execution exception during engine stop in the Server Netty subsystem, referencing the specific issue (#8671).
Description check ✅ Passed The PR description follows the repository template with all required sections (Subsystem, Motivation, Solution) properly filled with detailed explanations of the problem, root cause, and changes made.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch zibet27/fix-netty-engine-stop

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0388470 and fb20e5c.

📒 Files selected for processing (1)
  • ktor-server/ktor-server-netty/jvm/test/io/ktor/tests/server/netty/NettyEngineTest.kt (2 hunks)
🧰 Additional context used
🧠 Learnings (6)
📚 Learning: 2025-05-30T06:45:52.309Z
Learnt from: rururux
Repo: ktorio/ktor PR: 4896
File: ktor-client/ktor-client-core/jvm/test/FileStorageTest.kt:1-12
Timestamp: 2025-05-30T06:45:52.309Z
Learning: The headersOf() function from io.ktor.http package is available through wildcard imports like `import io.ktor.http.*`, so no explicit import statement is needed when using wildcard imports from that package.

Applied to files:

  • ktor-server/ktor-server-netty/jvm/test/io/ktor/tests/server/netty/NettyEngineTest.kt
📚 Learning: 2025-05-30T06:45:52.309Z
Learnt from: rururux
Repo: ktorio/ktor PR: 4896
File: ktor-client/ktor-client-core/jvm/test/FileStorageTest.kt:1-12
Timestamp: 2025-05-30T06:45:52.309Z
Learning: In Ktor test files, particularly in the ktor-client/ktor-client-core/jvm/test/ directory, test files follow the convention of not including explicit package declarations. This is consistent across test files like CachingCacheStorageTest.kt and should be maintained for consistency.

Applied to files:

  • ktor-server/ktor-server-netty/jvm/test/io/ktor/tests/server/netty/NettyEngineTest.kt
📚 Learning: 2025-11-14T14:11:30.292Z
Learnt from: osipxd
Repo: ktorio/ktor PR: 5195
File: ktor-client/ktor-client-java/jvm/test/io/ktor/client/engine/java/JavaHttp2Test.kt:13-15
Timestamp: 2025-11-14T14:11:30.292Z
Learning: In ktor-client-java tests, Java's HttpClient does support h2c (HTTP/2 cleartext) when configured with `protocolVersion = HttpClient.Version.HTTP_2` on Java 11+. The JavaHttp2Test successfully passes with the default useH2c=true setting, connecting to http://localhost:8084.

Applied to files:

  • ktor-server/ktor-server-netty/jvm/test/io/ktor/tests/server/netty/NettyEngineTest.kt
📚 Learning: 2025-05-14T18:05:02.321Z
Learnt from: bjhham
Repo: ktorio/ktor PR: 4855
File: ktor-server/ktor-server-plugins/ktor-server-di/api/ktor-server-di.klib.api:334-336
Timestamp: 2025-05-14T18:05:02.321Z
Learning: Breaking changes in constructor parameter order are acceptable for the ktor-server-di module when the code hasn't been released yet, as confirmed by the development team.

Applied to files:

  • ktor-server/ktor-server-netty/jvm/test/io/ktor/tests/server/netty/NettyEngineTest.kt
📚 Learning: 2025-05-16T13:11:28.416Z
Learnt from: osipxd
Repo: ktorio/ktor PR: 4860
File: ktor-server/ktor-server-plugins/ktor-server-di/api/ktor-server-di.api:24-26
Timestamp: 2025-05-16T13:11:28.416Z
Learning: Binary incompatible changes (such as constructor signature changes) are acceptable in the ktor-server-di module when the version is not yet released, as confirmed by the development team.

Applied to files:

  • ktor-server/ktor-server-netty/jvm/test/io/ktor/tests/server/netty/NettyEngineTest.kt
📚 Learning: 2025-06-09T07:08:35.085Z
Learnt from: bjhham
Repo: ktorio/ktor PR: 4916
File: ktor-server/ktor-server-core/api/ktor-server-core.api:151-151
Timestamp: 2025-06-09T07:08:35.085Z
Learning: Breaking changes are acceptable in Ktor codebase when the code hasn't been released yet, as confirmed by bjhham from the development team.

Applied to files:

  • ktor-server/ktor-server-netty/jvm/test/io/ktor/tests/server/netty/NettyEngineTest.kt
🔇 Additional comments (2)
ktor-server/ktor-server-netty/jvm/test/io/ktor/tests/server/netty/NettyEngineTest.kt (2)

26-26: LGTM: Wildcard import simplifies dependencies.

The change from explicit imports to a wildcard import for io.netty.handler.codec.http2.* is acceptable in test code. All required classes (DefaultHttp2Headers, DefaultHttp2HeadersDecoder, DefaultHttp2HeadersEncoder) remain available.


380-380: LGTM: More idiomatic Kotlin.

The change from substring(0, end + 4) to take(end + 4) is functionally equivalent and follows Kotlin idioms for taking a prefix of a string.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (4)
ktor-server/ktor-server-netty/jvm/test/io/ktor/tests/server/netty/NettyEngineTest.kt (1)

26-26: Wildcard import is reasonable but unrelated to PR objectives.

The change from explicit imports to a wildcard import for io.netty.handler.codec.http2.* consolidates multiple imports and is acceptable. Note that the coding guidelines specify star imports for io.ktor.* packages, but this pattern is commonly extended to third-party packages when multiple classes are imported.

This change appears unrelated to the main PR objective of fixing shutdown-related RejectedExecutionException.

ktor-server/ktor-server-netty/jvm/src/io/ktor/server/netty/NettyApplicationCallHandler.kt (1)

38-41: LGTM! Proper cleanup on channel unregistration.

The implementation correctly cancels the in-flight request coroutine when the channel is unregistered, preventing potential writes to inactive channels and resource leaks. This aligns with the PR's objective to fix shutdown-related issues.

Optional: Consider nulling out currentJob after cancellation for minor memory optimization:

 override fun channelUnregistered(ctx: ChannelHandlerContext?) {
-    currentJob?.cancel()
+    currentJob?.cancel()
+    currentJob = null
     super.channelUnregistered(ctx)
 }

While not strictly necessary (the handler is typically discarded after unregistration), this ensures the reference is explicitly cleared.

ktor-server/ktor-server-netty/jvm/src/io/ktor/server/netty/NettyApplicationEngine.kt (2)

283-287: Consider removing unnecessary crossinline modifier.

The crossinline modifier prevents non-local returns from the lambda, but since the lambda is only passed directly to runCatching and not stored or passed to another execution context, this modifier is not necessary.

Apply this diff if you prefer:

-    private inline fun <R> withStopException(crossinline block: () -> R) {
+    private inline fun <R> withStopException(block: () -> R) {
         runCatching(block).onFailure {
             environment.log.error("Exception thrown during engine stop", it)
         }
     }

278-281: Consider shutting down callEventGroup in terminate() for consistency.

The terminate() method only shuts down connectionEventGroup and workerEventGroup, but not callEventGroup when shareWorkGroup is false. Although callEventGroup is lazy and may not be initialized during early bind failures, for completeness and consistency with the stop() method, consider adding conditional shutdown of callEventGroup.

Apply this diff to add the shutdown:

     private fun terminate() {
         connectionEventGroup.shutdownGracefully().sync()
         workerEventGroup.shutdownGracefully().sync()
+        if (!configuration.shareWorkGroup && ::callEventGroup.isInitialized) {
+            callEventGroup.shutdownGracefully().sync()
+        }
     }

Note: Use ::callEventGroup.isInitialized to check if the lazy property was accessed before shutting it down.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a741975 and 78c9b45.

📒 Files selected for processing (4)
  • ktor-server/ktor-server-netty/jvm/src/io/ktor/server/netty/NettyApplicationCallHandler.kt (1 hunks)
  • ktor-server/ktor-server-netty/jvm/src/io/ktor/server/netty/NettyApplicationEngine.kt (1 hunks)
  • ktor-server/ktor-server-netty/jvm/src/io/ktor/server/netty/NettyApplicationResponse.kt (2 hunks)
  • ktor-server/ktor-server-netty/jvm/test/io/ktor/tests/server/netty/NettyEngineTest.kt (2 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{kt,kts}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{kt,kts}: Follow Kotlin official style guide for all Kotlin source and build scripts
Use star imports for io.ktor.* packages
Max line length is 120 characters
Indent with 4 spaces in Kotlin code
Include a copyright header in new Kotlin files

Files:

  • ktor-server/ktor-server-netty/jvm/src/io/ktor/server/netty/NettyApplicationEngine.kt
  • ktor-server/ktor-server-netty/jvm/src/io/ktor/server/netty/NettyApplicationResponse.kt
  • ktor-server/ktor-server-netty/jvm/src/io/ktor/server/netty/NettyApplicationCallHandler.kt
  • ktor-server/ktor-server-netty/jvm/test/io/ktor/tests/server/netty/NettyEngineTest.kt
**/*.kt

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.kt: Document all public Kotlin APIs, including parameters, return types, and exceptions
Annotate internal APIs with @internalapi
Follow Kotlin error-handling conventions and use specific Ktor exceptions

Files:

  • ktor-server/ktor-server-netty/jvm/src/io/ktor/server/netty/NettyApplicationEngine.kt
  • ktor-server/ktor-server-netty/jvm/src/io/ktor/server/netty/NettyApplicationResponse.kt
  • ktor-server/ktor-server-netty/jvm/src/io/ktor/server/netty/NettyApplicationCallHandler.kt
  • ktor-server/ktor-server-netty/jvm/test/io/ktor/tests/server/netty/NettyEngineTest.kt
🧠 Learnings (8)
📚 Learning: 2025-10-22T07:21:51.263Z
Learnt from: bjhham
Repo: ktorio/ktor PR: 5139
File: ktor-server/ktor-server-plugins/ktor-server-di/common/src/io/ktor/server/plugins/di/DependencyInjection.kt:11-11
Timestamp: 2025-10-22T07:21:51.263Z
Learning: In Ktor, `io.ktor.utils.io.CancellationException` is a typealias for `kotlinx.coroutines.CancellationException`, which is a supertype of `JobCancellationException`. Therefore, checking `e !is io.ktor.utils.io.CancellationException` is sufficient to exclude all coroutine cancellation exceptions.

Applied to files:

  • ktor-server/ktor-server-netty/jvm/src/io/ktor/server/netty/NettyApplicationEngine.kt
📚 Learning: 2025-08-26T06:07:28.352Z
Learnt from: CR
Repo: ktorio/ktor PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-08-26T06:07:28.352Z
Learning: Applies to **/*.kt : Follow Kotlin error-handling conventions and use specific Ktor exceptions

Applied to files:

  • ktor-server/ktor-server-netty/jvm/src/io/ktor/server/netty/NettyApplicationEngine.kt
📚 Learning: 2025-05-14T18:17:03.059Z
Learnt from: bjhham
Repo: ktorio/ktor PR: 4855
File: ktor-server/ktor-server-plugins/ktor-server-di/common/src/io/ktor/server/plugins/di/DependencyRegistry.kt:37-38
Timestamp: 2025-05-14T18:17:03.059Z
Learning: In the Ktor DI plugin, when a DependencyRegistry implements CoroutineScope, it should be properly cancelled during application shutdown to prevent memory leaks and resource issues. This is handled by calling registry.cancel() in the ApplicationStopped event.

Applied to files:

  • ktor-server/ktor-server-netty/jvm/src/io/ktor/server/netty/NettyApplicationCallHandler.kt
📚 Learning: 2025-05-14T18:17:03.059Z
Learnt from: bjhham
Repo: ktorio/ktor PR: 4855
File: ktor-server/ktor-server-plugins/ktor-server-di/common/src/io/ktor/server/plugins/di/DependencyRegistry.kt:37-38
Timestamp: 2025-05-14T18:17:03.059Z
Learning: When implementing CoroutineScope in Ktor components like DependencyRegistry, always ensure the scope is properly cancelled during application shutdown to prevent memory leaks and resource issues. In the DI plugin, this is handled by calling registry.cancel() in the ApplicationStopped event handler after all dependency cleanup is complete.

Applied to files:

  • ktor-server/ktor-server-netty/jvm/src/io/ktor/server/netty/NettyApplicationCallHandler.kt
📚 Learning: 2025-05-30T06:45:52.309Z
Learnt from: rururux
Repo: ktorio/ktor PR: 4896
File: ktor-client/ktor-client-core/jvm/test/FileStorageTest.kt:1-12
Timestamp: 2025-05-30T06:45:52.309Z
Learning: The headersOf() function from io.ktor.http package is available through wildcard imports like `import io.ktor.http.*`, so no explicit import statement is needed when using wildcard imports from that package.

Applied to files:

  • ktor-server/ktor-server-netty/jvm/test/io/ktor/tests/server/netty/NettyEngineTest.kt
📚 Learning: 2025-08-26T06:07:28.352Z
Learnt from: CR
Repo: ktorio/ktor PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-08-26T06:07:28.352Z
Learning: Applies to **/*.{kt,kts} : Use star imports for io.ktor.* packages

Applied to files:

  • ktor-server/ktor-server-netty/jvm/test/io/ktor/tests/server/netty/NettyEngineTest.kt
📚 Learning: 2025-05-30T06:45:52.309Z
Learnt from: rururux
Repo: ktorio/ktor PR: 4896
File: ktor-client/ktor-client-core/jvm/test/FileStorageTest.kt:1-12
Timestamp: 2025-05-30T06:45:52.309Z
Learning: In Ktor test files, particularly in the ktor-client/ktor-client-core/jvm/test/ directory, test files follow the convention of not including explicit package declarations. This is consistent across test files like CachingCacheStorageTest.kt and should be maintained for consistency.

Applied to files:

  • ktor-server/ktor-server-netty/jvm/test/io/ktor/tests/server/netty/NettyEngineTest.kt
📚 Learning: 2025-05-14T18:05:02.321Z
Learnt from: bjhham
Repo: ktorio/ktor PR: 4855
File: ktor-server/ktor-server-plugins/ktor-server-di/api/ktor-server-di.klib.api:334-336
Timestamp: 2025-05-14T18:05:02.321Z
Learning: Breaking changes in constructor parameter order are acceptable for the ktor-server-di module when the code hasn't been released yet, as confirmed by the development team.

Applied to files:

  • ktor-server/ktor-server-netty/jvm/test/io/ktor/tests/server/netty/NettyEngineTest.kt
🔇 Additional comments (6)
ktor-server/ktor-server-netty/jvm/src/io/ktor/server/netty/NettyApplicationResponse.kt (2)

49-66: Good defensive guard against inactive channels.

The addition of !context.channel().isActive prevents attempts to send response bytes to an inactive channel, which directly addresses the PR objective to stop writing responses to inactive channels during shutdown. The early return is appropriate since there's no valid destination for the response when the channel is closed.


113-127: Consistent active-channel guard applied.

The !context.channel().isActive guard mirrors the change in respondFromBytes and ensures that response setup is skipped when the channel is inactive. This consistency across both response-sending entry points provides comprehensive protection against writing to closed channels during shutdown.

ktor-server/ktor-server-netty/jvm/test/io/ktor/tests/server/netty/NettyEngineTest.kt (1)

380-380: More idiomatic Kotlin.

The change from s.substring(0, end + 4) to s.take(end + 4) is functionally equivalent and more idiomatic Kotlin. Since end is the index where "\r\n\r\n" was found, end + 4 safely includes the complete header terminator.

Like the import change, this refactor is unrelated to the main PR objectives.

ktor-server/ktor-server-netty/jvm/src/io/ktor/server/netty/NettyApplicationEngine.kt (3)

290-293: LGTM!

The early guard correctly prevents duplicate stop sequences by checking if the cancellation job can be completed. This ensures idempotent stop behavior.


296-298: LGTM!

Wrapping each channel sync in withStopException ensures that if one channel fails to close properly, other channels still get processed. This improves shutdown resilience.


300-301: LGTM!

Computing shutdown futures upfront before syncing them allows both event loop groups to shut down concurrently, improving shutdown performance.

@zibet27 zibet27 force-pushed the zibet27/fix-netty-engine-stop branch from 78c9b45 to ee6655e Compare November 12, 2025 15:19
Copy link
Contributor

@bjhham bjhham left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, thanks for dealing with this! 🙏

Copy link
Member

@marychatte marychatte left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm 👍

@zibet27 zibet27 force-pushed the zibet27/fix-netty-engine-stop branch from ee6655e to bed0e8b Compare November 18, 2025 09:59
@zibet27 zibet27 force-pushed the zibet27/fix-netty-engine-stop branch from bed0e8b to 4f415e1 Compare November 18, 2025 10:00
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
ktor-server/ktor-server-netty/jvm/src/io/ktor/server/netty/NettyApplicationEngine.kt (1)

283-287: Stop exception wrapper is sound; consider reusing in terminate()

The helper cleanly centralizes logging of shutdown failures. As a minor follow‑up, you could also wrap the terminate() shutdown calls with withStopException for consistent logging on the bind‑failure path.

ktor-server/ktor-server-netty/jvm/test/io/ktor/tests/server/netty/NettySpecificTest.kt (2)

39-53: Optionally close the ServerSocket via use {} for cleanliness

testStartOnUsedPort leaves the ServerSocket open for the lifetime of the test method. Wrapping it in use { ... } (or a try/finally) would ensure the socket is always closed even if an assertion fails, without changing the behavior of the test.


89-172: Startup timeout and header assertions are solid; consider explicit server shutdown instead of relying on cancellation

The combination of runTestWithRealTime, withTimeout(10.seconds) { appStarted.await() }, and the ResponseSent hook with assertions in testScope.launch gives good coverage for stripping Content-Length and Transfer-Encoding across the different response types.

One small robustness tweak to consider: if server.start(wait = true) is implemented as a blocking call that doesn’t cooperate with coroutine cancellation, serverJob.cancel() alone might not always guarantee prompt shutdown. If that’s the case in Netty’s engine implementation, explicitly calling server.stop(...) from the test (e.g., after the client block, before cancelling the job) would make the lifecycle more explicit and reduce the risk of hanging tests or leaked threads.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4f415e1 and 0388470.

📒 Files selected for processing (3)
  • ktor-server/ktor-server-netty/jvm/src/io/ktor/server/netty/NettyApplicationEngine.kt (1 hunks)
  • ktor-server/ktor-server-netty/jvm/test/io/ktor/tests/server/netty/NettyConfigurationTest.kt (1 hunks)
  • ktor-server/ktor-server-netty/jvm/test/io/ktor/tests/server/netty/NettySpecificTest.kt (6 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-05-30T06:45:52.309Z
Learnt from: rururux
Repo: ktorio/ktor PR: 4896
File: ktor-client/ktor-client-core/jvm/test/FileStorageTest.kt:1-12
Timestamp: 2025-05-30T06:45:52.309Z
Learning: In Ktor test files, particularly in the ktor-client/ktor-client-core/jvm/test/ directory, test files follow the convention of not including explicit package declarations. This is consistent across test files like CachingCacheStorageTest.kt and should be maintained for consistency.

Applied to files:

  • ktor-server/ktor-server-netty/jvm/test/io/ktor/tests/server/netty/NettySpecificTest.kt
📚 Learning: 2025-06-23T12:49:56.883Z
Learnt from: CR
Repo: ktorio/ktor PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-06-23T12:49:56.883Z
Learning: Error handling should follow Kotlin conventions and use specific Ktor exceptions.

Applied to files:

  • ktor-server/ktor-server-netty/jvm/test/io/ktor/tests/server/netty/NettySpecificTest.kt
🧬 Code graph analysis (1)
ktor-server/ktor-server-netty/jvm/test/io/ktor/tests/server/netty/NettySpecificTest.kt (4)
ktor-test-dispatcher/common/src/TestCommon.kt (1)
  • runTestWithRealTime (35-44)
ktor-server/ktor-server-core/common/src/io/ktor/server/engine/EmbeddedServer.kt (5)
  • embeddedServer (106-113)
  • embeddedServer (151-172)
  • embeddedServer (209-229)
  • embeddedServer (254-264)
  • embeddedServer (271-277)
ktor-server/ktor-server-core/common/src/io/ktor/server/application/Application.kt (1)
  • serverConfig (107-112)
ktor-network/jvm/src/io/ktor/network/util/Utils.kt (1)
  • withTimeout (79-90)
🔇 Additional comments (5)
ktor-server/ktor-server-netty/jvm/test/io/ktor/tests/server/netty/NettyConfigurationTest.kt (1)

109-109: LGTM! Correct alignment with new shutdown semantics.

Changing the mock from await() to sync() properly reflects the updated shutdown synchronization pattern introduced in this PR. Using sync() ensures exceptions are propagated during shutdown, addressing the core issue described in the PR objectives.

ktor-server/ktor-server-netty/jvm/src/io/ktor/server/netty/NettyApplicationEngine.kt (1)

293-313: Channel close and event-loop shutdown ordering look correct

Closing server channels and syncing their futures before shutting down the connection/worker groups, and only shutting down callEventGroup when !configuration.shareWorkGroup, matches the intended ownership model and should avoid the previous RejectedExecutionExceptions during stop.

ktor-server/ktor-server-netty/jvm/test/io/ktor/tests/server/netty/NettySpecificTest.kt (3)

7-29: Imports and test harness wiring match usages

All newly added imports are exercised in the tests, and adopting runTestWithRealTime plus Duration.seconds keeps these Netty tests aligned with the real-time test harness without introducing dead code.


32-37: runTestWithRealTime usage in Netty lifecycle tests looks appropriate

Wrapping testNoLeakWithoutStartAndStop and testStartMultipleConnectorsOnUsedPort in runTestWithRealTime is consistent with the real I/O behavior of Netty and doesn’t change the semantics of these tests, since their bodies are synchronous and don’t depend on virtual time.

Also applies to: 55-87


174-228: Bad-request test migration to real-time harness looks good

Using runTestWithRealTime plus a bounded withTimeout(10.seconds) around appStarted.await() and the existing withTimeout(5000) for the read loop keeps this low-level TCP test from hanging while preserving the original 400-response expectations.

@zibet27 zibet27 merged commit 18a6e97 into main Nov 24, 2025
15 of 17 checks passed
@zibet27 zibet27 deleted the zibet27/fix-netty-engine-stop branch November 24, 2025 15:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants