Uh oh!
There was an error while loading. Please reload this page.
YARN-11845. WebAppProxy add Connection close header to prevent CLOSE_WAIT leaks - #8697
YARN-11845. WebAppProxy add Connection close header to prevent CLOSE_WAIT leaks#8697sohurdc 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.
Uh oh!
There was an error while loading. Please reload this page.
| * 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(), |
| 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)"); |
| 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)"); |
Uh oh!
There was an error while loading. Please reload this page.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
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