Make sure to always close input streams bound to responses - #1970
Conversation
53d6891 to
ab86f9c
Compare
|
Some investigations ongoing on how to properly test for dangling connections: jetty/jetty.project#15436. |
35d80f9 to
4998677
Compare
Ensure all connections are closed when transporter is closed. Add PUT IT where response contains body. This closes #1964
4998677 to
99df910
Compare
gnodet
left a comment
There was a problem hiding this comment.
Verdict: ✅ Approve — excellent fix for a real connection leak that could cause deadlocks in production.
What's great
The production fixes are correct and well-targeted:
- JdkTransporter
implPut(): adds the missingfinally { closeBody(response); }— this was the core bug.closeBodyis already null-safe (checksstreamHttpResponse != null), so theresponse = nullinitialization is safe. - JdkTransporter
implGet(): clean simplification — removes redundant innercloseBody()calls since the outerfinallyalready handles all exit paths. - JettyTransporter
implPut(): wrappingInputStreamResponseListenerin try-with-resources and moving RFC 9457 handling inside ensures the listener is always closed. TheHttpTransporterExceptionstill propagates correctly (it's not caught by theExecutionExceptioncatch block). - Connection leak detection in
tearDown()is a force multiplier — every existing HTTP transporter test now doubles as a leak detector.
Suggestions
1. Null guard before Awaitility check (HttpTransporterTest.java, line ~309)
The Awaitility assertion runs before the if (httpServer != null) check. If setUp() fails before creating the server, @AfterEach still runs and the lambda will NPE, masking the real failure:
// Current:
Awaitility.await().atMost(5, TimeUnit.SECONDS).until(() -> httpServer.getNumConnectedEndPoints() == 0);
if (httpServer != null) {
// Suggested — move inside the null guard:
if (httpServer != null) {
Awaitility.await().atMost(5, TimeUnit.SECONDS).until(() -> httpServer.getNumConnectedEndPoints() == 0);
httpServer.stop();
httpServer = null;
}2. HTTP 204 with response body — may weaken the test (HttpServer.java, PUT handler)
The response body is written, then status is set to SC_NO_CONTENT (204). Per RFC 9110 §6.4.1, 204 responses should have no content. While Jetty will still put the bytes on the wire, some HTTP client implementations may skip draining the body for 204, which could make the test pass even without the fix. Consider using SC_OK (200) when responseBodyForPut is set so the test is unambiguous:
if (responseBodyForPut != null) {
writeResponseBodyMessage(req, response, responseBodyForPut);
response.setStatus(HttpServletResponse.SC_OK);
} else {
response.setStatus(HttpServletResponse.SC_NO_CONTENT);
}Minor observations (no action needed)
- Double closeBody in
implPut: The innertry { ... } finally { closeBody(response); }around error handling is now redundant with the new outerfinally. Harmless (idempotent close), but inimplGetthe equivalent was cleaned up — consistency could be improved. - Hardcoded Awaitility version:
4.3.0is hardcoded inmaven-resolver-test-http/pom.xmlrather than managed in the parent POM's<dependencyManagement>. - Pre-existing:
JettyTransporter.implGet()also createsInputStreamResponseListenerwithout try-with-resources — the same pattern this PR fixes inimplPut(). Might be worth a follow-up.
CI: ⏳ All pending (just un-drafted). Production code changes are straightforward enough to review without CI, but will want to confirm all tests pass.
Reviewed with AI assistance on behalf of @gnodet. This review was generated by an AI agent and may contain inaccuracies; please verify all suggestions before applying.
gnodet
left a comment
There was a problem hiding this comment.
Follow-up review (commit 2/2): ✅ All suggestions addressed — looks great.
- Null guard: Awaitility check correctly moved inside
if (httpServer != null)✓ - PUT response status: Using
SC_CREATED(201) instead ofSC_NO_CONTENT(204) when a body is present — even better than the 200 I suggested, since 201 is the semantically correct success status for PUT-creates-resource ✓ - Awaitility version: Centralized in parent POM
<dependencyManagement>, with a helpful note about the 4.3.1 release issue ✓ - Double closeBody in
implPut: Inner try/finally removed, now consistent withimplGetsimplification ✓
No further comments.
Reviewed with AI assistance on behalf of @gnodet. This review was generated by an AI agent and may contain inaccuracies; please verify all suggestions before applying.
Ensure all connections are closed when transporter is closed Add PUT IT where response contains body
This closes #1964
Following this checklist to help us incorporate your
contribution quickly and easily:
Note that commits might be squashed by a maintainer on merge.
This may not always be possible but is a best-practice.
mvn verifyto make sure basic checks pass.A more thorough check will be performed on your pull request automatically.
mvn -Prun-its verify).If your pull request is about ~20 lines of code you don't need to sign an
Individual Contributor License Agreement if you are unsure
please ask on the developers list.
To make clear that you license your contribution under
the Apache License Version 2.0, January 2004
you have to acknowledge this by using the following check-box.