-
-
Notifications
You must be signed in to change notification settings - Fork 16.3k
HttpObjectDecoder ignores HTTP trailer header. #8736
Description
Hi Netty team !
Behavior
When I was writing test for #8721 in io.netty.handler.codec.http.HttpContentDecoderTest I encountered a problem with parsing trailer header using HttpObjectDecoder.
HttpObjectDecoder is not able to handle following bytes sequence
assertTrue(channel.writeInbound(Unpooled.copiedBuffer("My-Trailer: 42", CharsetUtil.US_ASCII)));
assertTrue(channel.writeInbound(Unpooled.copiedBuffer("\r\n", CharsetUtil.US_ASCII)));
assertTrue(channel.writeInbound(Unpooled.copiedBuffer("\r\n\r\n", CharsetUtil.US_ASCII)));but is able to deal with that
assertTrue(channel.writeInbound(Unpooled.copiedBuffer("My-Trailer: 42\r\n\r\n\r\n", CharsetUtil.US_ASCII)));After small debugging session, I noticed that HttpObjectDecoder is actually parsing trailer but that line of code (io.netty.handler.codec.http.HttpObjectDecoder:674)
line = headerParser.parse(buffer);
if (line == null) {
return null;
}cause to ignore my trailer, because headerParser.parse(buffer) on empty buffer will return null instead of parsed trailer.
Minimal yet complete reproducer code (or URL to code)
Didn't want to make pull-request (almost exact test is already pushed here #8721), but following test (class io.netty.handler.codec.http.HttpContentDecoderTest) will reproduce the problem
@Test
public void testChunkedRequestDecompression() {
HttpResponseDecoder decoder = new HttpResponseDecoder();
HttpContentDecoder decompressor = new HttpContentDecompressor();
EmbeddedChannel channel = new EmbeddedChannel(decoder, decompressor, null);
String headers = "HTTP/1.1 200 OK\r\n"
+ "Transfer-Encoding: chunked\r\n"
+ "Trailer: My-Trailer\r\n"
+ "Content-Encoding: gzip\r\n\r\n";
channel.writeInbound(Unpooled.copiedBuffer((headers.getBytes(CharsetUtil.US_ASCII))));
assertTrue(channel.writeInbound(Unpooled.copiedBuffer(Integer.toHexString(GZ_HELLO_WORLD.length) + "\r\n", CharsetUtil.US_ASCII)));
assertTrue(channel.writeInbound(Unpooled.copiedBuffer(GZ_HELLO_WORLD)));
assertTrue(channel.writeInbound(Unpooled.copiedBuffer("\r\n".getBytes(CharsetUtil.US_ASCII))));
assertTrue(channel.writeInbound(Unpooled.copiedBuffer("0\r\n", CharsetUtil.US_ASCII)));
assertTrue(channel.writeInbound(Unpooled.copiedBuffer("My-Trailer: 42", CharsetUtil.US_ASCII)));
assertTrue(channel.writeInbound(Unpooled.copiedBuffer("\r\n", CharsetUtil.US_ASCII)));
assertTrue(channel.writeInbound(Unpooled.copiedBuffer("\r\n\r\n", CharsetUtil.US_ASCII)));
Object ob1 = channel.readInbound();
assertThat(ob1, is(instanceOf(DefaultHttpResponse.class)));
Object ob2 = channel.readInbound();
assertThat(ob1, is(instanceOf(DefaultHttpResponse.class)));
HttpContent content = (HttpContent) ob2;
assertEquals(HELLO_WORLD, content.content().toString(CharsetUtil.US_ASCII));
Object ob3 = channel.readInbound();
assertThat(ob1, is(instanceOf(DefaultHttpResponse.class)));
LastHttpContent lastContent = (LastHttpContent) ob3;
assertFalse(lastContent.trailingHeaders().isEmpty());
assertEquals("42", lastContent.trailingHeaders().get("My-Trailer"));
assertHasInboundMessages(channel, false);
assertHasOutboundMessages(channel, false);
assertFalse(channel.finish());
}Netty version
4.1.32.Final