HttpObjectDecoder resetRequested not updated after reset

Motivation:
HttpObjectDecoder maintains a resetRequested flag which is used to determine if internal state should be reset when a decode occurs. However after a reset is done the resetRequested flag is not set to false. This leads to all data after this point being discarded.

Modifications:
- Set resetRequested to false when a reset is done

Result:
HttpObjectDecoder can still function after a reset.
This commit is contained in:
Scott Mitchell 2016-09-20 20:45:03 -07:00
parent 4145fae919
commit dd1ba2a252
2 changed files with 66 additions and 2 deletions

View File

@ -509,6 +509,7 @@ public abstract class HttpObjectDecoder extends ByteToMessageDecoder {
} }
} }
resetRequested = false;
currentState = State.SKIP_CONTROL_CHARS; currentState = State.SKIP_CONTROL_CHARS;
} }

View File

@ -18,6 +18,8 @@ package io.netty.handler.codec.http;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.embedded.EmbeddedChannel; import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.compression.ZlibCodecFactory; import io.netty.handler.codec.compression.ZlibCodecFactory;
import io.netty.handler.codec.compression.ZlibDecoder; import io.netty.handler.codec.compression.ZlibDecoder;
@ -30,9 +32,16 @@ import org.junit.Test;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Queue; import java.util.Queue;
import java.util.concurrent.atomic.AtomicReference;
import static org.hamcrest.CoreMatchers.*; import static org.hamcrest.CoreMatchers.instanceOf;
import static org.junit.Assert.*; import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
public class HttpContentDecoderTest { public class HttpContentDecoderTest {
private static final String HELLO_WORLD = "hello, world"; private static final String HELLO_WORLD = "hello, world";
@ -217,6 +226,60 @@ public class HttpContentDecoderTest {
assertFalse(channel.finish()); assertFalse(channel.finish());
} }
@Test
public void testExpectContinueResetHttpObjectDecoder() {
// request with header "Expect: 100-continue" must be replied with one "100 Continue" response
// case 5: Test that HttpObjectDecoder correctly resets its internal state after a failed expectation.
HttpRequestDecoder decoder = new HttpRequestDecoder();
final int maxBytes = 10;
HttpObjectAggregator aggregator = new HttpObjectAggregator(maxBytes);
final AtomicReference<FullHttpRequest> secondRequestRef = new AtomicReference<FullHttpRequest>();
EmbeddedChannel channel = new EmbeddedChannel(decoder, aggregator, new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof FullHttpRequest) {
if (!secondRequestRef.compareAndSet(null, (FullHttpRequest) msg)) {
((FullHttpRequest) msg).release();
}
} else {
ReferenceCountUtil.release(msg);
}
}
});
String req1 = "POST /1 HTTP/1.1\r\n" +
"Content-Length: " + (maxBytes + 1) + "\r\n" +
"Expect: 100-continue\r\n" +
"\r\n";
assertFalse(channel.writeInbound(Unpooled.wrappedBuffer(req1.getBytes(CharsetUtil.US_ASCII))));
FullHttpResponse resp = channel.readOutbound();
assertEquals(HttpStatusClass.CLIENT_ERROR, resp.status().codeClass());
resp.release();
String req2 = "POST /2 HTTP/1.1\r\n" +
"Content-Length: " + maxBytes + "\r\n" +
"Expect: 100-continue\r\n" +
"\r\n";
assertFalse(channel.writeInbound(Unpooled.wrappedBuffer(req2.getBytes(CharsetUtil.US_ASCII))));
resp = channel.readOutbound();
assertEquals(100, resp.status().code());
resp.release();
byte[] content = new byte[maxBytes];
assertFalse(channel.writeInbound(Unpooled.wrappedBuffer(content)));
FullHttpRequest req = secondRequestRef.get();
assertNotNull(req);
assertEquals("/2", req.uri());
assertEquals(10, req.content().readableBytes());
req.release();
assertHasInboundMessages(channel, false);
assertHasOutboundMessages(channel, false);
assertFalse(channel.finish());
}
@Test @Test
public void testRequestContentLength1() { public void testRequestContentLength1() {
// case 1: test that ContentDecompressor either sets the correct Content-Length header // case 1: test that ContentDecompressor either sets the correct Content-Length header