[
https://issues.apache.org/jira/browse/FLINK-39761?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Ferenc Csaky reassigned FLINK-39761:
------------------------------------
Assignee: Nagy Attila Bálint
> Missing 'Connection: close' header on '304 Not Modified' responses causes
> proxy connection pool poisoning
> ---------------------------------------------------------------------------------------------------------
>
> Key: FLINK-39761
> URL: https://issues.apache.org/jira/browse/FLINK-39761
> Project: Flink
> Issue Type: Bug
> Components: Runtime / REST, Runtime / Web Frontend
> Affects Versions: 1.20.4, 2.2.1
> Reporter: Nagy Attila Bálint
> Assignee: Nagy Attila Bálint
> Priority: Major
>
> *Overview:*
> When the Flink Web UI / History Server serves static files (e.g., .css, .js)
> and receives an If-Modified-Since request matching the file's modification
> time, it correctly generates a {{304 Not Modified}} response.
> However, the server immediately drops the TCP connection without including a
> {{Connection: close}} HTTP header in the response.
> This violates HTTP/1.1 keep-alive
> [expectations|https://www.rfc-editor.org/info/rfc2068/#section-19.7.1] and
> causes *connection pool poisoning* in downstream reverse proxies (such as
> Apache Knox or Nginx).
> *Impact:*
> Because HTTP/1.1 assumes persistent connections by default, reverse proxies
> receive the 304 response and place the connection back into their reusable
> connection pool.
> When the proxy attempts to reuse this connection for the very next request,
> it hits an unexpected end of stream because Flink has already severed the TCP
> connection.
> In Apache Knox, this manifests as a NoHttpResponseException and results in
> intermittent HTTP 500 Server Errors being served to the end user.
> *Root Cause:*
> The issue originates in
> {{org.apache.flink.runtime.rest.handler.legacy.files.StaticFileServerHandler}}.
> current master branch (2.2.1+)
> [link|https://github.com/apache/flink/blob/45295cf62608ca172b83ac42d9128d027a91d06a/flink-runtime/src/main/java/org/apache/flink/runtime/rest/handler/legacy/files/StaticFileServerHandler.java#L312]
> 1.20.4 branch
> [link|https://github.com/apache/flink/blob/release-1.20.4/flink-runtime/src/main/java/org/apache/flink/runtime/rest/handler/legacy/files/StaticFileServerHandler.java#L312]
> In the {{sendNotModified}} method, the code creates a
> {{DefaultFullHttpResponse}} and immediately attaches a
> {{ChannelFutureListener.CLOSE}} listener to the write operation.
> However, it fails to set the {{Connection: close}} header on the response
> object before flushing it to the client.
> {code:java}
> public static void sendNotModified(ChannelHandlerContext ctx) {
> FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1,
> NOT_MODIFIED);
> setDateHeader(response);
> // BUG: Missing explicit Connection: close header here before closing the
> channel.
> // Proxies assume the connection is kept alive.
> // close the connection as soon as the error message is sent.
> ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
> }
> {code}
> *Proposed Solution:*
> To comply with HTTP/1.1 specifications and prevent proxy connection pool
> poisoning, the Flink server must explicitly communicate that the connection
> is being closed.
> The fix is simply to add the Connection: close header before flushing the
> response:
> {code:java}
> public static void sendNotModified(ChannelHandlerContext ctx) {
> FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1,
> NOT_MODIFIED);
> setDateHeader(response);
>
> // Explicitly notify the client that the connection will be dropped
> response.headers().set(HttpHeaderNames.CONNECTION,
> HttpHeaderValues.CLOSE);
> // close the connection as soon as the error message is sent.
> ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
> }
> {code}
--
This message was sent by Atlassian Jira
(v8.20.10#820010)