Carefully manage Keep-Alive connections in HttpStaticFileServer (#8914)
Motivation: Simple rules: * close the connection when sending any error * specify "Connection: close" header when closing the connection * successful responses should keep the connection intact when otherwise is not requested by the client Modifications: * "send response and cleanup the connection" logic moved to a helper * for all successful responses set "Content-Lenght" header * do not specify "Connection: Keep-Alive" header as far it's a default for HTTP/1.1 * set "Connection: close" header when necessary Result: Keep-Alive connections management is inlined with RFCs.
This commit is contained in:
parent
39fcdb3e0d
commit
a651804f9d
@ -122,6 +122,7 @@ public class HttpStaticFileServerHandler extends SimpleChannelInboundHandler<Ful
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final boolean keepAlive = HttpUtil.isKeepAlive(request);
|
||||||
final String uri = request.uri();
|
final String uri = request.uri();
|
||||||
final String path = sanitizeUri(uri);
|
final String path = sanitizeUri(uri);
|
||||||
if (path == null) {
|
if (path == null) {
|
||||||
@ -137,9 +138,9 @@ public class HttpStaticFileServerHandler extends SimpleChannelInboundHandler<Ful
|
|||||||
|
|
||||||
if (file.isDirectory()) {
|
if (file.isDirectory()) {
|
||||||
if (uri.endsWith("/")) {
|
if (uri.endsWith("/")) {
|
||||||
sendListing(ctx, file, uri);
|
sendListing(ctx, file, uri, keepAlive);
|
||||||
} else {
|
} else {
|
||||||
sendRedirect(ctx, uri + '/');
|
sendRedirect(ctx, uri + '/', keepAlive);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -160,7 +161,7 @@ public class HttpStaticFileServerHandler extends SimpleChannelInboundHandler<Ful
|
|||||||
long ifModifiedSinceDateSeconds = ifModifiedSinceDate.getTime() / 1000;
|
long ifModifiedSinceDateSeconds = ifModifiedSinceDate.getTime() / 1000;
|
||||||
long fileLastModifiedSeconds = file.lastModified() / 1000;
|
long fileLastModifiedSeconds = file.lastModified() / 1000;
|
||||||
if (ifModifiedSinceDateSeconds == fileLastModifiedSeconds) {
|
if (ifModifiedSinceDateSeconds == fileLastModifiedSeconds) {
|
||||||
sendNotModified(ctx);
|
sendNotModified(ctx, keepAlive);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -178,8 +179,9 @@ public class HttpStaticFileServerHandler extends SimpleChannelInboundHandler<Ful
|
|||||||
HttpUtil.setContentLength(response, fileLength);
|
HttpUtil.setContentLength(response, fileLength);
|
||||||
setContentTypeHeader(response, file);
|
setContentTypeHeader(response, file);
|
||||||
setDateAndCacheHeaders(response, file);
|
setDateAndCacheHeaders(response, file);
|
||||||
if (HttpUtil.isKeepAlive(request)) {
|
|
||||||
response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
|
if (!keepAlive) {
|
||||||
|
response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the initial line and the header.
|
// Write the initial line and the header.
|
||||||
@ -218,7 +220,7 @@ public class HttpStaticFileServerHandler extends SimpleChannelInboundHandler<Ful
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Decide whether to close the connection or not.
|
// Decide whether to close the connection or not.
|
||||||
if (!HttpUtil.isKeepAlive(request)) {
|
if (!keepAlive) {
|
||||||
// Close the connection when the whole content is written out.
|
// Close the connection when the whole content is written out.
|
||||||
lastContentFuture.addListener(ChannelFutureListener.CLOSE);
|
lastContentFuture.addListener(ChannelFutureListener.CLOSE);
|
||||||
}
|
}
|
||||||
@ -264,7 +266,7 @@ public class HttpStaticFileServerHandler extends SimpleChannelInboundHandler<Ful
|
|||||||
|
|
||||||
private static final Pattern ALLOWED_FILE_NAME = Pattern.compile("[^-\\._]?[^<>&\\\"]*");
|
private static final Pattern ALLOWED_FILE_NAME = Pattern.compile("[^-\\._]?[^<>&\\\"]*");
|
||||||
|
|
||||||
private static void sendListing(ChannelHandlerContext ctx, File dir, String dirPath) {
|
private static void sendListing(ChannelHandlerContext ctx, File dir, String dirPath, boolean keepAlive) {
|
||||||
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK);
|
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK);
|
||||||
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html; charset=UTF-8");
|
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html; charset=UTF-8");
|
||||||
|
|
||||||
@ -304,16 +306,14 @@ public class HttpStaticFileServerHandler extends SimpleChannelInboundHandler<Ful
|
|||||||
response.content().writeBytes(buffer);
|
response.content().writeBytes(buffer);
|
||||||
buffer.release();
|
buffer.release();
|
||||||
|
|
||||||
// Close the connection as soon as the error message is sent.
|
sendAndCleanupConnection(ctx, response, keepAlive);
|
||||||
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void sendRedirect(ChannelHandlerContext ctx, String newUri) {
|
private static void sendRedirect(ChannelHandlerContext ctx, String newUri, boolean keepAlive) {
|
||||||
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, FOUND);
|
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, FOUND);
|
||||||
response.headers().set(HttpHeaderNames.LOCATION, newUri);
|
response.headers().set(HttpHeaderNames.LOCATION, newUri);
|
||||||
|
|
||||||
// Close the connection as soon as the error message is sent.
|
sendAndCleanupConnection(ctx, response, keepAlive);
|
||||||
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) {
|
private static void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) {
|
||||||
@ -321,8 +321,7 @@ public class HttpStaticFileServerHandler extends SimpleChannelInboundHandler<Ful
|
|||||||
HTTP_1_1, status, Unpooled.copiedBuffer("Failure: " + status + "\r\n", CharsetUtil.UTF_8));
|
HTTP_1_1, status, Unpooled.copiedBuffer("Failure: " + status + "\r\n", CharsetUtil.UTF_8));
|
||||||
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8");
|
response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8");
|
||||||
|
|
||||||
// Close the connection as soon as the error message is sent.
|
sendAndCleanupConnection(ctx, response, false);
|
||||||
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -331,12 +330,32 @@ public class HttpStaticFileServerHandler extends SimpleChannelInboundHandler<Ful
|
|||||||
* @param ctx
|
* @param ctx
|
||||||
* Context
|
* Context
|
||||||
*/
|
*/
|
||||||
private static void sendNotModified(ChannelHandlerContext ctx) {
|
private static void sendNotModified(ChannelHandlerContext ctx, boolean keepAlive) {
|
||||||
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, NOT_MODIFIED);
|
FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, NOT_MODIFIED);
|
||||||
setDateHeader(response);
|
setDateHeader(response);
|
||||||
|
|
||||||
// Close the connection as soon as the error message is sent.
|
sendAndCleanupConnection(ctx, response, keepAlive);
|
||||||
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If Keep-Alive is disabled, attaches "Connection: close" header to the response
|
||||||
|
* and closes the connection after the response being sent.
|
||||||
|
*/
|
||||||
|
private static void sendAndCleanupConnection(ChannelHandlerContext ctx, FullHttpResponse response,
|
||||||
|
boolean keepAlive) {
|
||||||
|
HttpUtil.setContentLength(response, response.content().readableBytes());
|
||||||
|
if (!keepAlive) {
|
||||||
|
// We're going to close the connection as soon as the response is sent,
|
||||||
|
// so we should also make it clear for the client.
|
||||||
|
response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
ChannelFuture flushPromise = ctx.writeAndFlush(response);
|
||||||
|
|
||||||
|
if (!keepAlive) {
|
||||||
|
// Close the connection as soon as the response is sent.
|
||||||
|
flushPromise.addListener(ChannelFutureListener.CLOSE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user