YARN-11845. WebAppProxy add Connection close header to prevent CLOSE_WAIT leaks - #8697
Open
sohurdc wants to merge 3 commits into
Open
YARN-11845. WebAppProxy add Connection close header to prevent CLOSE_WAIT leaks#8697sohurdc wants to merge 3 commits into
sohurdc wants to merge 3 commits into
Conversation
…WAIT socket buildup
There was a problem hiding this comment.
Pull request overview
This PR aims to prevent socket accumulation in CLOSE_WAIT on the YARN WebAppProxy host by forcing proxied backend requests (AM/History Server) to close the connection after each response.
Changes:
- Add
Connection: closerequest header to proxied requests inWebAppProxyServlet.proxyLink(). - Extend
TestWebAppProxyServletto capture the backend-receivedConnectionheader and add a new test covering GET/PUT and clientkeep-aliveoverride behavior.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/main/java/org/apache/hadoop/yarn/server/webproxy/WebAppProxyServlet.java |
Sets Connection: close on outgoing proxied requests to avoid backend keep-alive behavior contributing to socket retention. |
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/test/java/org/apache/hadoop/yarn/server/webproxy/TestWebAppProxyServlet.java |
Adds a targeted test validating that the backend receives Connection: close and that client keep-alive is overridden; captures the header in the embedded backend servlet. |
Suppressed comments (1)
hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-web-proxy/src/main/java/org/apache/hadoop/yarn/server/webproxy/WebAppProxyServlet.java:320
HttpClientBuilder.build()returns a CloseableHttpClient, butproxyLink()never closes the client (or the response), onlybase.releaseConnection(). AddingConnection: closemay reduce socket retention on the backend side, but it doesn’t address the underlying client lifecycle leak and can still leave resources tied to the per-request connection manager until GC.
base.setHeader("Connection", "close");
String user = req.getRemoteUser();
if (user != null && !user.isEmpty()) {
base.setHeader("Cookie",
PROXY_USER_COOKIE_NAME + "=" + URLEncoder.encode(user, "ASCII"));
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+509
to
+513
| * HttpClient per request, so if the backend connection is kept alive the | ||
| * socket is left in CLOSE_WAIT state on the proxy host until GC reclaims | ||
| * it. Setting the 'Connection: close' request header makes the backend | ||
| * close the connection as soon as the response is sent (SOHU-HADOOP-11). | ||
| */ |
| assertEquals(HttpURLConnection.HTTP_OK, proxyConn.getResponseCode()); | ||
| assertNotNull(proxiedConnectionHeader, | ||
| "The proxied server did not receive a Connection header at all"); | ||
| assertEquals("close", proxiedConnectionHeader.trim().toLowerCase(), |
Comment on lines
+545
to
+553
| proxyConn = (HttpURLConnection) url.openConnection(); | ||
| proxyConn.setRequestProperty("Cookie", | ||
| "checked_application_0_0000=true"); | ||
| proxyConn.setRequestProperty("Connection", "keep-alive"); | ||
| proxyConn.connect(); | ||
| assertEquals(HttpURLConnection.HTTP_OK, proxyConn.getResponseCode()); | ||
| assertEquals("close", proxiedConnectionHeader.trim().toLowerCase(), | ||
| "The proxy must override a client 'Connection: keep-alive' " | ||
| + "request header with 'close' (SOHU-HADOOP-11)"); |
Comment on lines
+556
to
+569
| proxyConn = (HttpURLConnection) url.openConnection(); | ||
| proxyConn.setRequestMethod("PUT"); | ||
| proxyConn.setDoOutput(true); | ||
| proxyConn.setRequestProperty("Cookie", | ||
| "checked_application_0_0000=true"); | ||
| proxyConn.connect(); | ||
| byte[] body = "SOHU-HADOOP-11".getBytes(StandardCharsets.UTF_8); | ||
| try (OutputStream os = proxyConn.getOutputStream()) { | ||
| os.write(body); | ||
| } | ||
| assertEquals(HttpURLConnection.HTTP_OK, proxyConn.getResponseCode()); | ||
| assertEquals("close", proxiedConnectionHeader.trim().toLowerCase(), | ||
| "The proxy must send 'Connection: close' on PUT requests too " | ||
| + "(SOHU-HADOOP-11)"); |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
YARN-11845. WebAppProxy: add
Connection: closeheader to prevent CLOSE_WAIT leaksDescription of PR
This PR fixes an accumulation of sockets stuck in
CLOSE_WAITstate on theResourceManager host that runs the WebAppProxy.
WebAppProxyServlet.proxyLink()creates a brand-newHttpClientfor everyproxied request (
HttpClientBuilder.create()→build()), sends the request,and only calls
base.releaseConnection()afterward. It never closes theHttpClientitself.Apache HttpClient enables connection pooling / keep-alive by default, so the
backend (Application Master or History Server) keeps the connection open after
returning the response, waiting to reuse it. But since the proxy throws away
the
HttpClientright after each request, that pooled connection can never bereused. The corresponding socket on the proxy side therefore lingers in
CLOSE_WAITuntil theHttpClientis finally garbage collected.The fix is to explicitly ask the backend to close the connection once the
response has been sent, by setting a
Connection: closerequest header on theoutgoing request:
Because
Connectionis not inPASS_THROUGH_HEADERS, a client-suppliedConnection: keep-aliveheader is never forwarded, so the proxy always sendscloseand the backend closes the connection as soon as the response iswritten.
How was this patch tested?
testWebAppProxyConnectionCloseHeader()inTestWebAppProxyServlet, which verifies that the proxied backend receives aConnection: closeheader in three cases:GETrequest,GETrequest where the client explicitly sendsConnection: keep-alive(asserting the proxy overrides it with
close),PUTrequest (asserting the header is also set on the PUT path).testWebAppProxyPassThroughHeadersassertion (9 headersreceived by the backend) is unaffected: the
Connectionheader was alreadyamong the counted headers, only its value changes from
Keep-Alivetoclose.For code changes
return the same responses; it only instructs the backend to close the
underlying connection).
Additional notes
Connection: closedoes not change the response returned to the client; itonly affects the lifetime of the internal proxied connection, so it has no
user-visible impact.
Commit message