From 4a5d587d00d22899671790832df945adf526f80c Mon Sep 17 00:00:00 2001 From: jfarcand Date: Fri, 6 May 2022 15:29:50 -0400 Subject: [PATCH] Fixes #153 --- .../HttpStaticFileServerHandler.java | 42 +++++++++---------- .../nettosphere/NettyChannelInitializer.java | 10 ++++- .../atmosphere/nettosphere/util/Utils.java | 24 ++++++++--- 3 files changed, 47 insertions(+), 29 deletions(-) diff --git a/server/src/main/java/org/atmosphere/nettosphere/HttpStaticFileServerHandler.java b/server/src/main/java/org/atmosphere/nettosphere/HttpStaticFileServerHandler.java index 1276d52..05e0d9c 100644 --- a/server/src/main/java/org/atmosphere/nettosphere/HttpStaticFileServerHandler.java +++ b/server/src/main/java/org/atmosphere/nettosphere/HttpStaticFileServerHandler.java @@ -46,16 +46,19 @@ import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.HttpChunkedInput; import io.netty.handler.codec.http.HttpHeaderNames; +import io.netty.handler.codec.http.HttpHeaderValues; import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.HttpResponseEncoder; import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.handler.codec.http.HttpUtil; import io.netty.handler.codec.http.LastHttpContent; import io.netty.handler.ssl.SslHandler; import io.netty.handler.stream.ChunkedFile; import io.netty.util.AttributeKey; import io.netty.util.CharsetUtil; import org.atmosphere.nettosphere.util.MimeType; +import org.atmosphere.nettosphere.util.Utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -75,7 +78,7 @@ import java.util.regex.Pattern; import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE; -import static io.netty.handler.codec.http.HttpHeaders.Names.IF_MODIFIED_SINCE; +import static io.netty.handler.codec.http.HttpHeaders.Names.IF_MODIFIED_SINCE; import static io.netty.handler.codec.http.HttpHeaders.Names.LOCATION; import static io.netty.handler.codec.http.HttpMethod.GET; import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST; @@ -85,6 +88,7 @@ import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND; import static io.netty.handler.codec.http.HttpResponseStatus.NOT_MODIFIED; import static io.netty.handler.codec.http.HttpResponseStatus.OK; +import static io.netty.handler.codec.http.HttpVersion.HTTP_1_0; import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; /** @@ -163,6 +167,7 @@ public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) thr sendError(ctx, METHOD_NOT_ALLOWED, request); return; } + final boolean keepAlive = HttpUtil.isKeepAlive(request); File file = null; RandomAccessFile raf = null; @@ -180,15 +185,6 @@ public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) thr continue; } -// if (file.isDirectory()) { -// if (uri.endsWith("/")) { -// sendListing(ctx, file); -// } else { -// sendRedirect(ctx, uri + '/'); -// } -// return; -// } - try { raf = new RandomAccessFile(file, "r"); found = true; @@ -205,7 +201,9 @@ public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) thr } request.headers().add(SERVICED, "true"); - ctx.pipeline().addBefore(BridgeRuntime.class.getName(), "encoder", new HttpResponseEncoder()); + if (Utils.isJersey()) { + ctx.pipeline().addBefore(BridgeRuntime.class.getName(), "encoder", new HttpResponseEncoder()); + } // Cache Validation String ifModifiedSince = request.headers().get(IF_MODIFIED_SINCE); @@ -223,17 +221,17 @@ public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) thr } } - long fileLength = raf.length(); - - HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK); - HttpHeaders.setContentLength(response, fileLength); - contentType(request, response, file); + HttpUtil.setContentLength(response, fileLength); + setContentTypeHeader(response, file); setDateAndCacheHeaders(response, file); -// if (HttpHeaders.isKeepAlive(request)) { -// response.headers().set(CONNECTION, HttpHeaders.Values.KEEP_ALIVE); -// } + + if (!keepAlive) { + response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE); + } else if (request.protocolVersion().equals(HTTP_1_0)) { + response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE); + } // Write the initial line and the header. ctx.write(response); @@ -270,8 +268,10 @@ public void operationComplete(ChannelProgressiveFuture future) { } }); - // Close the connection when the whole content is written out. - lastContentFuture.addListener(ChannelFutureListener.CLOSE); + if (!keepAlive) { + // Close the connection when the whole content is written out. + lastContentFuture.addListener(ChannelFutureListener.CLOSE); + } } @Override diff --git a/server/src/main/java/org/atmosphere/nettosphere/NettyChannelInitializer.java b/server/src/main/java/org/atmosphere/nettosphere/NettyChannelInitializer.java index 05aa2bd..767f38e 100644 --- a/server/src/main/java/org/atmosphere/nettosphere/NettyChannelInitializer.java +++ b/server/src/main/java/org/atmosphere/nettosphere/NettyChannelInitializer.java @@ -21,10 +21,12 @@ import io.netty.channel.ChannelPipeline; import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpRequestDecoder; +import io.netty.handler.codec.http.HttpServerCodec; import io.netty.handler.codec.http.websocketx.WebSocketFrameAggregator; import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketServerCompressionHandler; import io.netty.handler.ssl.SslHandler; import io.netty.handler.stream.ChunkedWriteHandler; +import org.atmosphere.nettosphere.util.Utils; import javax.net.ssl.SSLEngine; @@ -52,8 +54,12 @@ protected void initChannel(Channel ch) throws Exception { pipeline.addLast("ssl", config.nettySslContext().newHandler(ch.alloc())); } - pipeline.addLast("decoder", new HttpRequestDecoder()); - + // For backward compatibility + if (Utils.isJersey()) { + pipeline.addLast("decoder", new HttpRequestDecoder()); + } else { + pipeline.addLast(new HttpServerCodec()); + } pipeline.addLast("aggregator", new HttpObjectAggregator(config.maxChunkContentLength())); if (config.supportChunking()) { diff --git a/server/src/main/java/org/atmosphere/nettosphere/util/Utils.java b/server/src/main/java/org/atmosphere/nettosphere/util/Utils.java index 9c36110..7e475f0 100644 --- a/server/src/main/java/org/atmosphere/nettosphere/util/Utils.java +++ b/server/src/main/java/org/atmosphere/nettosphere/util/Utils.java @@ -16,6 +16,7 @@ package org.atmosphere.nettosphere.util; import io.netty.channel.Channel; +import org.atmosphere.util.IOUtils; import java.io.BufferedOutputStream; import java.io.File; @@ -32,23 +33,25 @@ import java.util.jar.JarEntry; import java.util.jar.JarFile; +import static org.atmosphere.cpr.FrameworkConfig.JERSEY_CONTAINER; + public class Utils { - public static final IOException REMOTELY_CLOSED = new IOException("Connection remotely closed"); + public static IOException REMOTELY_CLOSED = new IOException("Connection remotely closed"); { REMOTELY_CLOSED.setStackTrace(new StackTraceElement[]{}); } - private final static NoAlloc NO_ALLOC = new NoAlloc(); + private static NoAlloc NO_ALLOC = new NoAlloc(); - public static final IOException ioExceptionForChannel(Channel channel, String uuid) { + public static IOException ioExceptionForChannel(Channel channel, String uuid) { IOException ioe = new IOException(channel + ": content already processed for " + uuid); ioe.setStackTrace(new StackTraceElement[]{}); return ioe; } - public final static URLClassLoader createURLClassLoader(String dirPath) throws IOException { + public static URLClassLoader createURLClassLoader(String dirPath) throws IOException { String path; File file; @@ -351,14 +354,23 @@ public static boolean copy(File src, File dest) { } - private final static class NoAlloc { + private static class NoAlloc { public String toString() { return "config.noInternalAlloc == true"; } } - public final static String id(Channel channel) { + public static String id(Channel channel) { InetSocketAddress addr = (InetSocketAddress) channel.localAddress(); return addr.getAddress().getHostAddress() + "::" + addr.getPort(); } + + public static boolean isJersey() { + try { + IOUtils.loadClass(Utils.class, JERSEY_CONTAINER); + return true; + } catch (Exception e) { + return false; + } + } }