Rem Rem - 1 year ago 52
Java Question

How to write an aynchronous HttpServer using Netty

I am trying to create a simple asynchronous Netty HTTP server. The key feature I need to support is the ability to send a HttpRequest wrapped in an event to an event driven component (in the same JVM) that will process the event and generate the response. This event driven component runs in a separate thread to the Netty component.

I have followed a number of examples to build something that should work for me, and what I have works when the response is generated in the same thread as the handler

The custom handler I have written / adapted is:

private class WebServerHandler extends SimpleChannelInboundHandler<Object> {
String uri;
public void messageReceived(final ChannelHandlerContext ctx, final Object msg) {
if (!(msg instanceof FullHttpRequest)) return;

final FullHttpRequest request = (FullHttpRequest) msg;

uri = request.uri();
System.out.println("Handling: " + uri);
if (HttpUtil.is100ContinueExpected(request)) send100Continue(ctx);

final Handler handler = WebServer.this.getHandler(request.uri());
if (handler == null) {
writeNotFound(ctx, request);
} else {
try {
handler.handle(ctx, request);
} catch (final Throwable ex) {
writeInternalServerError(ctx, request);

public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) {

public void channelReadComplete(final ChannelHandlerContext ctx) {
System.out.println("Completed: " + uri);
// ctx.flush();

The problem arises when I send the event to the event-driven component (which again is running in a separate thread). In this case, the above
method is called BEFORE the event-driven component handles the event. In the web browser I am using to test the solution, the connection never terminates.

The pipeline I am using is:

final ChannelPipeline p = ch.pipeline();
p.addLast("decoder", new HttpRequestDecoder(4096, 8192, 8192, false));
p.addLast("aggregator", new HttpObjectAggregator(100 * 1024 * 1024));
p.addLast("encoder", new HttpResponseEncoder());
p.addLast("handler", new WebServerHandler());

I have not included any information of the event driven component because it is a large and complex system (it is actually a programming language), but I know that the relevant code is being invoked successfully. The actual code I am trying to execute to generate the response is:

File file = new File(file_uri);
System.out.println("file: " + file.getAbsolutePath());
RandomAccessFile raf = new RandomAccessFile(file, "r");
HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
HttpUtil.setContentLength(response, raf.length());

ctx.write(new DefaultFileRegion(raf.getChannel(), 0, file.length()));


So, my problem is - how to stop Netty invoking the
method until I have sent the response?

Answer Source

I think your raf.close call should be in a listener attached to the write of your file. As written I would expect you to close the file before your write call has a chance to read it all. Your response correctly sets the content-length, but if you don't write that many bytes, because you close the file early, then your client will hang waiting for the rest.

You should move your encoder before the aggregator in your pipeline. The aggregator can send responses that the encoder needs to encode. Look into using HttpServerCodec as it can replace your encoder/Decoder with one handler.

You extend the SimpleChannelInboundHandler using Object as the type, but then reject anything except FullHttpRequest messages. If you change the type in your extends then the parent class will take care of that check and casting for you (are you using Netty 5? Why?).


... extends SimpleChannelInboundHandler<FullHttpRequest> {
public void messageReceived(ChannelHandlerContext ctx, FullHttpRequest request) {

I don't think channelReadComplete being called early matters for your scenario. Since you are already flushing in your read/response handler the flush in channelReadComplete isn't necessary.