From 12911a0efe93788b4185d43b13fb73b0209551ca Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 7 Jun 2016 10:27:53 +0200 Subject: [PATCH] Ensure valid message sequence if channel is closed before receive headers. Motivation: When the channel is closed while we still decode the headers we currently not preserve correct message sequence. In this case we should generate an invalid message with a current cause. Modifications: Create an invalid message with a PrematureChannelClosureException as cause when the channel is closed while we decode the headers. Result: Correct message sequence preserved and correct DecoderResult if the channel is closed while decode headers. --- .../handler/codec/http/HttpObjectDecoder.java | 15 +++++++++++++-- .../codec/http/HttpResponseDecoderTest.java | 14 ++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java index bb39f9c1b4..5a2de5bc8a 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java @@ -22,6 +22,7 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPipeline; import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.handler.codec.DecoderResult; +import io.netty.handler.codec.PrematureChannelClosureException; import io.netty.handler.codec.TooLongFrameException; import io.netty.util.internal.AppendableCharSequence; @@ -413,9 +414,19 @@ public abstract class HttpObjectDecoder extends ByteToMessageDecoder { if (currentState == State.READ_VARIABLE_LENGTH_CONTENT && !in.isReadable() && !chunked) { // End of connection. out.add(LastHttpContent.EMPTY_LAST_CONTENT); - reset(); + resetNow(); return; } + + if (currentState == State.READ_HEADER) { + // If we are still in the state of reading headers we need to create a new invalid message that + // signals that the connection was closed before we received the headers. + out.add(invalidMessage(Unpooled.EMPTY_BUFFER, + new PrematureChannelClosureException("Connection closed before received headers"))); + resetNow(); + return; + } + // Check if the closure of the connection signifies the end of the content. boolean prematureClosure; if (isDecodingRequest() || chunked) { @@ -427,11 +438,11 @@ public abstract class HttpObjectDecoder extends ByteToMessageDecoder { // connection, so it is perfectly fine. prematureClosure = contentLength() > 0; } - resetNow(); if (!prematureClosure) { out.add(LastHttpContent.EMPTY_LAST_CONTENT); } + resetNow(); } } diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpResponseDecoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpResponseDecoderTest.java index 5706b06a34..5db91d6bc8 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpResponseDecoderTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/HttpResponseDecoderTest.java @@ -18,6 +18,7 @@ package io.netty.handler.codec.http; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.embedded.EmbeddedChannel; +import io.netty.handler.codec.PrematureChannelClosureException; import io.netty.handler.codec.TooLongFrameException; import io.netty.handler.codec.http.HttpHeaders.Names; import io.netty.util.CharsetUtil; @@ -611,4 +612,17 @@ public class HttpResponseDecoderTest { // .. even after the connection is closed. assertThat(channel.finish(), is(false)); } + + @Test + public void testConnectionClosedBeforeHeadersReceived() { + EmbeddedChannel channel = new EmbeddedChannel(new HttpResponseDecoder()); + String responseInitialLine = + "HTTP/1.1 200 OK\r\n"; + assertFalse(channel.writeInbound(Unpooled.copiedBuffer(responseInitialLine, CharsetUtil.US_ASCII))); + assertTrue(channel.finish()); + HttpMessage message = (HttpMessage) channel.readInbound(); + assertTrue(message.getDecoderResult().isFailure()); + assertThat(message.getDecoderResult().cause(), instanceOf(PrematureChannelClosureException.class)); + assertNull(channel.readInbound()); + } }