Motivation: I want to fix bug in vert.x project (eclipse-vertx/vert.x#2562) caused by ComposedLastHttpContent result being null. I don't know if it is intentional that this last decoded chuck in the issue returns null, but if not - I am providing fix for that. Modification: * Added new constructor in ComposedLastHttpContent allowing to pass DecoderResult * set DecoderResult.SUCCESS for created ComposedLastHttpContent in HttpContentEncoder * set DecoderResult.SUCCESS for created ComposedLastHttpContent in HttpContentDecoder Result: Fixes eclipse-vertx/vert.x#2562
This commit is contained in:
parent
7a613c2131
commit
8bcea35d2e
@ -28,6 +28,11 @@ final class ComposedLastHttpContent implements LastHttpContent {
|
||||
this.trailingHeaders = trailingHeaders;
|
||||
}
|
||||
|
||||
ComposedLastHttpContent(HttpHeaders trailingHeaders, DecoderResult result) {
|
||||
this(trailingHeaders);
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpHeaders trailingHeaders() {
|
||||
return trailingHeaders;
|
||||
|
@ -19,6 +19,7 @@ import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.embedded.EmbeddedChannel;
|
||||
import io.netty.handler.codec.CodecException;
|
||||
import io.netty.handler.codec.DecoderResult;
|
||||
import io.netty.handler.codec.MessageToMessageDecoder;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
|
||||
@ -164,7 +165,7 @@ public abstract class HttpContentDecoder extends MessageToMessageDecoder<HttpObj
|
||||
if (headers.isEmpty()) {
|
||||
out.add(LastHttpContent.EMPTY_LAST_CONTENT);
|
||||
} else {
|
||||
out.add(new ComposedLastHttpContent(headers));
|
||||
out.add(new ComposedLastHttpContent(headers, DecoderResult.SUCCESS));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufHolder;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.embedded.EmbeddedChannel;
|
||||
import io.netty.handler.codec.DecoderResult;
|
||||
import io.netty.handler.codec.MessageToMessageCodec;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
|
||||
@ -264,7 +265,7 @@ public abstract class HttpContentEncoder extends MessageToMessageCodec<HttpReque
|
||||
if (headers.isEmpty()) {
|
||||
out.add(LastHttpContent.EMPTY_LAST_CONTENT);
|
||||
} else {
|
||||
out.add(new ComposedLastHttpContent(headers));
|
||||
out.add(new ComposedLastHttpContent(headers, DecoderResult.SUCCESS));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ package io.netty.handler.codec.http;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.embedded.EmbeddedChannel;
|
||||
import io.netty.handler.codec.DecoderResult;
|
||||
import io.netty.handler.codec.EncoderException;
|
||||
import io.netty.handler.codec.compression.ZlibWrapper;
|
||||
import io.netty.util.CharsetUtil;
|
||||
@ -188,6 +189,7 @@ public class HttpContentCompressorTest {
|
||||
assertThat(chunk.content().isReadable(), is(false));
|
||||
assertThat(chunk, is(instanceOf(LastHttpContent.class)));
|
||||
assertEquals("Netty", ((LastHttpContent) chunk).trailingHeaders().get(of("X-Test")));
|
||||
assertEquals(DecoderResult.SUCCESS, chunk.decoderResult());
|
||||
chunk.release();
|
||||
|
||||
assertThat(ch.readOutbound(), is(nullValue()));
|
||||
@ -331,6 +333,7 @@ public class HttpContentCompressorTest {
|
||||
assertThat(res.content().readableBytes(), is(0));
|
||||
assertThat(res.content().toString(CharsetUtil.US_ASCII), is(""));
|
||||
assertEquals("Netty", res.trailingHeaders().get(of("X-Test")));
|
||||
assertEquals(DecoderResult.SUCCESS, res.decoderResult());
|
||||
assertThat(ch.readOutbound(), is(nullValue()));
|
||||
}
|
||||
|
||||
@ -370,6 +373,7 @@ public class HttpContentCompressorTest {
|
||||
assertThat(res.content().readableBytes(), is(0));
|
||||
assertThat(res.content().toString(CharsetUtil.US_ASCII), is(""));
|
||||
assertEquals("Netty", res.trailingHeaders().get(of("X-Test")));
|
||||
assertEquals(DecoderResult.SUCCESS, res.decoderResult());
|
||||
assertThat(ch.readOutbound(), is(nullValue()));
|
||||
}
|
||||
|
||||
|
@ -89,6 +89,48 @@ public class HttpContentDecoderTest {
|
||||
assertFalse(channel.finish()); // assert that no messages are left in channel
|
||||
}
|
||||
|
||||
@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)));
|
||||
|
||||
String chunkLength = Integer.toHexString(GZ_HELLO_WORLD.length);
|
||||
assertTrue(channel.writeInbound(Unpooled.copiedBuffer(chunkLength + "\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\r\n\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));
|
||||
content.release();
|
||||
|
||||
Object ob3 = channel.readInbound();
|
||||
assertThat(ob1, is(instanceOf(DefaultHttpResponse.class)));
|
||||
LastHttpContent lastContent = (LastHttpContent) ob3;
|
||||
assertNotNull(lastContent.decoderResult());
|
||||
assertTrue(lastContent.decoderResult().isSuccess());
|
||||
assertFalse(lastContent.trailingHeaders().isEmpty());
|
||||
assertEquals("42", lastContent.trailingHeaders().get("My-Trailer"));
|
||||
assertHasInboundMessages(channel, false);
|
||||
assertHasOutboundMessages(channel, false);
|
||||
assertFalse(channel.finish());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResponseDecompression() {
|
||||
// baseline test: response decoder, content decompressor && request aggregator work as expected
|
||||
|
@ -22,6 +22,7 @@ import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
import io.netty.channel.embedded.EmbeddedChannel;
|
||||
import io.netty.handler.codec.CodecException;
|
||||
import io.netty.handler.codec.DecoderResult;
|
||||
import io.netty.handler.codec.EncoderException;
|
||||
import io.netty.handler.codec.MessageToByteEncoder;
|
||||
import io.netty.util.CharsetUtil;
|
||||
@ -156,6 +157,7 @@ public class HttpContentEncoderTest {
|
||||
assertThat(chunk.content().isReadable(), is(false));
|
||||
assertThat(chunk, is(instanceOf(LastHttpContent.class)));
|
||||
assertEquals("Netty", ((LastHttpContent) chunk).trailingHeaders().get(of("X-Test")));
|
||||
assertEquals(DecoderResult.SUCCESS, res.decoderResult());
|
||||
chunk.release();
|
||||
|
||||
assertThat(ch.readOutbound(), is(nullValue()));
|
||||
@ -285,6 +287,7 @@ public class HttpContentEncoderTest {
|
||||
assertThat(res.content().readableBytes(), is(0));
|
||||
assertThat(res.content().toString(CharsetUtil.US_ASCII), is(""));
|
||||
assertEquals("Netty", res.trailingHeaders().get(of("X-Test")));
|
||||
assertEquals(DecoderResult.SUCCESS, res.decoderResult());
|
||||
assertThat(ch.readOutbound(), is(nullValue()));
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user