Sason Ohanian Sason Ohanian - 14 days ago 6
HTTP Question

Handle HTTP client exception in netty

I'm relatively new with netty and unsure if I'm doing things right. I'll try to be as short as possible. Please ask for more info if anything is unclear.

So, I have a netty server serving HTTP requests where contents are expected to be protobuf messages serialized to Json strings.

The channel pipeline looks like this:

@Override protected void initChannel(final SocketChannel channel) throws Exception {
final ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast(new HttpServerCodec());
pipeline.addLast(new HttpObjectAggregator(1048576));
pipeline.addLast(new HttpProtobufServerCodec(charset, requestConverter, responseConverter));
pipeline.addLast(new ProtobufMessageHandler(mapping));
}


First two channel handlers are standard netty stuff,

HttpProtobufServerCodec looks like:

public class HttpProtobufServerCodec extends CombinedChannelDuplexHandler<HttpToProtobufDecoder, ProtobufToHttpEncoder>


and HttpToProtobufDecoder looks like:

public final class HttpToProtobufDecoder extends MessageToMessageDecoder<FullHttpRequest> {
private Charset charset;
private final Converter<byte[], ?> converter;

protected HttpToProtobufDecoder(final Charset charset, final Converter<byte[], ?> converter) {
this.charset = charset;
this.converter = converter;
}

@Override protected void decode(final ChannelHandlerContext ctx, final FullHttpRequest msg, final List<Object> out)
throws Exception {
byte[] payloadBytes = new byte[msg.content().readableBytes()];
msg.content().readBytes(payloadBytes);
Message message = (Message) converter.convert(payloadBytes);
out.add(message);
}

@Override public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) throws Exception {
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
HttpResponseStatus.BAD_REQUEST,
Unpooled.wrappedBuffer(charset.encode("Could not read request!").array()));

//ctx.writeAndFlush(response);
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
}


So, I recieve a FullHttpRequest in HttpToProtobufDecoder and try to decode the contents of the request to a protobuf message. This throws an exception if content can't be decoded which puts us in the exceptionCaught(...) method..

In Exception caught a HTTP 400 response is created and written to channelHandlerContext. This is where I have my question.

if comments on the following lines are switched:

//ctx.writeAndFlush(response);
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);


the client times out when reading the response body. But if I close the channel after writing the 400 everything seems fine. What happens is; reading of the input stream is blocked because there is no input data avaliable. I.e. we are stuck in in.read(...) below, somewhere far down in client code:

while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
byteCount += bytesRead;
}


So, the question is, do you have the close the channel after writing a http 400 response for some reason?

Am I even going about this the right way? Should I write the HTTP response message in exceptionCaught?

Sorry if the question is a bit unclear. Any help will be much appreciated!

/Thanks!

Answer

The client has no way of knowing when your message has been completely sent. Add a content length or chunked header and you won't need to close the connection anymore.