I'm trying to wrap my head around Netty 4 way of implementing a HTTP Server that serve HttpResponses bodies using chunked transfer-encoding when total data size is unknown.
As a starting point, I simply changed the HttpStaticFileServerHandler (found in https://github.com/netty/netty/tree/netty-4.0.0.CR1/example/src/main/java/io/netty/example/http/file) to use a ChunkedStream instead of a ChunkedFile (they both are ChunkedByteInputs).
I understand that it is not ideal in the original example use case to use a FileInputStream but I think it makes a good example reusing already known code.
So, here is the diff against the HttpStaticFileServerHandler class from the io.netty.example.http.file package (vs. 4.0.0.CR1):
diff --git a/example/src/main/java/io/netty/example/http/file/HttpStaticFileServerHandler.java b/example/src/main/java/io/netty/example/http/file/HttpStaticFileServerHandler.java
index 904579b..0d3592f 100644
--- a/example/src/main/java/io/netty/example/http/file/HttpStaticFileServerHandler.java
+++ b/example/src/main/java/io/netty/example/http/file/HttpStaticFileServerHandler.java
@@ -27,13 +27,14 @@ import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus;
-import io.netty.handler.stream.ChunkedFile;
+import io.netty.handler.stream.ChunkedStream;
import io.netty.util.CharsetUtil;
import javax.activation.MimetypesFileTypeMap;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
-import java.io.RandomAccessFile;
+import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.text.SimpleDateFormat;
@@ -159,17 +160,15 @@ public class HttpStaticFileServerHandler extends ChannelInboundMessageHandlerAda
}
}
- RandomAccessFile raf;
+ InputStream raf; // Use an InputStream instead of a RandomAccessFile
try {
- raf = new RandomAccessFile(file, "r");
+ raf = new FileInputStream(file);
} catch (FileNotFoundException fnfe) {
sendError(ctx, NOT_FOUND);
return;
}
- long fileLength = raf.length();
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
- setContentLength(response, fileLength);
setContentTypeHeader(response, file);
setDateAndCacheHeaders(response, file);
if (isKeepAlive(request)) {
@@ -180,7 +179,7 @@ public class HttpStaticFileServerHandler extends ChannelInboundMessageHandlerAda
ctx.write(response);
// Write the content.
- ChannelFuture writeFuture = ctx.write(new ChunkedFile(raf, 0, fileLength, 8192));
+ ChannelFuture writeFuture = ctx.write(new ChunkedStream(raf)); // Use a ChunkedStream instead of a ChunkedFile
// Decide whether to close the connection or not.
if (!isKeepAlive(request)) {
And here the complete changed file: https://gist.github.com/eskatos/5311587
Changes are minimal: use a FileInputStream instead of RandomAccessFile and ChunkedStream instead of ChunkedFile. The pipeline is untouched.
To reproduce, simply apply the changes to the Netty example, run it and try do download any file.
After this change, directory listing obviously works because reponses are not chunked but file downloads don't. The client download the file but never finish the download, hold the connection and wait forever. I've tried several from browsers to curl, wget etc.. I've also tried to add a ByteLoggingHandler to the pipeline and I can see an empty trailing chunk so I don't understand why the browser still wait for data.
Any clue?