HTTP/2 Unit Test Leak Fixes
Motivation: The HTTP/2 tests do not always clean up ByteBuf resources reliably. There are issues with the refCnt, over allocating buffers, and potentially not waiting long enough to reclaim resources for stress tests. Modifications: Scrub the HTTP/2 unit tests for ByteBuf leaks. Result: Less leaks (hopefully none) in the HTTP/2 unit tests. No OOME from HTTP/2 unit tests.
This commit is contained in:
parent
96a044fabe
commit
2cf6ed9460
@ -182,7 +182,7 @@ public class DataCompressionHttp2Test {
|
||||
}
|
||||
});
|
||||
awaitServer();
|
||||
data.readerIndex(0);
|
||||
data.resetReaderIndex();
|
||||
ArgumentCaptor<ByteBuf> dataCaptor = ArgumentCaptor.forClass(ByteBuf.class);
|
||||
verify(serverListener).onDataRead(any(ChannelHandlerContext.class), eq(3), dataCaptor.capture(), eq(0),
|
||||
eq(true));
|
||||
@ -216,7 +216,7 @@ public class DataCompressionHttp2Test {
|
||||
}
|
||||
});
|
||||
awaitServer();
|
||||
data.readerIndex(0);
|
||||
data.resetReaderIndex();
|
||||
ArgumentCaptor<ByteBuf> dataCaptor = ArgumentCaptor.forClass(ByteBuf.class);
|
||||
verify(serverListener).onDataRead(any(ChannelHandlerContext.class), eq(3), dataCaptor.capture(), eq(0),
|
||||
eq(true));
|
||||
@ -254,8 +254,8 @@ public class DataCompressionHttp2Test {
|
||||
}
|
||||
});
|
||||
awaitServer();
|
||||
data1.readerIndex(0);
|
||||
data2.readerIndex(0);
|
||||
data1.resetReaderIndex();
|
||||
data2.resetReaderIndex();
|
||||
ArgumentCaptor<ByteBuf> dataCaptor = ArgumentCaptor.forClass(ByteBuf.class);
|
||||
ArgumentCaptor<Boolean> endStreamCaptor = ArgumentCaptor.forClass(Boolean.class);
|
||||
verify(serverListener, times(2)).onDataRead(any(ChannelHandlerContext.class), eq(3), dataCaptor.capture(),
|
||||
@ -297,7 +297,7 @@ public class DataCompressionHttp2Test {
|
||||
}
|
||||
});
|
||||
awaitServer();
|
||||
data.readerIndex(0);
|
||||
data.resetReaderIndex();
|
||||
ArgumentCaptor<ByteBuf> dataCaptor = ArgumentCaptor.forClass(ByteBuf.class);
|
||||
verify(serverListener).onDataRead(any(ChannelHandlerContext.class), eq(3), dataCaptor.capture(), eq(0),
|
||||
eq(true));
|
||||
|
@ -26,7 +26,6 @@ import io.netty.buffer.UnpooledByteBufAllocator;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@ -66,35 +65,56 @@ public class DefaultHttp2FrameIOTest {
|
||||
|
||||
@Test
|
||||
public void emptyDataShouldRoundtrip() throws Exception {
|
||||
ByteBuf data = Unpooled.EMPTY_BUFFER;
|
||||
final ByteBuf data = Unpooled.EMPTY_BUFFER;
|
||||
writer.writeData(ctx, 1000, data, 0, false, promise);
|
||||
|
||||
ByteBuf frame = captureWrite();
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onDataRead(eq(ctx), eq(1000), eq(data), eq(0), eq(false));
|
||||
frame.release();
|
||||
ByteBuf frame = null;
|
||||
try {
|
||||
frame = captureWrite();
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onDataRead(eq(ctx), eq(1000), eq(data), eq(0), eq(false));
|
||||
} finally {
|
||||
if (frame != null) {
|
||||
frame.release();
|
||||
}
|
||||
data.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dataShouldRoundtrip() throws Exception {
|
||||
ByteBuf data = dummyData();
|
||||
final ByteBuf data = dummyData();
|
||||
writer.writeData(ctx, 1000, data.retain().duplicate(), 0, false, promise);
|
||||
|
||||
ByteBuf frame = captureWrite();
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onDataRead(eq(ctx), eq(1000), eq(data), eq(0), eq(false));
|
||||
frame.release();
|
||||
ByteBuf frame = null;
|
||||
try {
|
||||
frame = captureWrite();
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onDataRead(eq(ctx), eq(1000), eq(data), eq(0), eq(false));
|
||||
} finally {
|
||||
if (frame != null) {
|
||||
frame.release();
|
||||
}
|
||||
data.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dataWithPaddingShouldRoundtrip() throws Exception {
|
||||
ByteBuf data = dummyData();
|
||||
final ByteBuf data = dummyData();
|
||||
writer.writeData(ctx, 1, data.retain().duplicate(), 0xFF, true, promise);
|
||||
|
||||
ByteBuf frame = captureWrite();
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onDataRead(eq(ctx), eq(1), eq(data), eq(0xFF), eq(true));
|
||||
frame.release();
|
||||
ByteBuf frame = null;
|
||||
try {
|
||||
frame = captureWrite();
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onDataRead(eq(ctx), eq(1), eq(data), eq(0xFF), eq(true));
|
||||
} finally {
|
||||
if (frame != null) {
|
||||
frame.release();
|
||||
}
|
||||
data.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -102,9 +122,12 @@ public class DefaultHttp2FrameIOTest {
|
||||
writer.writePriority(ctx, 1, 2, (short) 255, true, promise);
|
||||
|
||||
ByteBuf frame = captureWrite();
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onPriorityRead(eq(ctx), eq(1), eq(2), eq((short) 255), eq(true));
|
||||
frame.release();
|
||||
try {
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onPriorityRead(eq(ctx), eq(1), eq(2), eq((short) 255), eq(true));
|
||||
} finally {
|
||||
frame.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -112,9 +135,12 @@ public class DefaultHttp2FrameIOTest {
|
||||
writer.writeRstStream(ctx, 1, MAX_UNSIGNED_INT, promise);
|
||||
|
||||
ByteBuf frame = captureWrite();
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onRstStreamRead(eq(ctx), eq(1), eq(MAX_UNSIGNED_INT));
|
||||
frame.release();
|
||||
try {
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onRstStreamRead(eq(ctx), eq(1), eq(MAX_UNSIGNED_INT));
|
||||
} finally {
|
||||
frame.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -122,9 +148,12 @@ public class DefaultHttp2FrameIOTest {
|
||||
writer.writeSettings(ctx, new Http2Settings(), promise);
|
||||
|
||||
ByteBuf frame = captureWrite();
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onSettingsRead(eq(ctx), eq(new Http2Settings()));
|
||||
frame.release();
|
||||
try {
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onSettingsRead(eq(ctx), eq(new Http2Settings()));
|
||||
} finally {
|
||||
frame.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -138,9 +167,12 @@ public class DefaultHttp2FrameIOTest {
|
||||
writer.writeSettings(ctx, settings, promise);
|
||||
|
||||
ByteBuf frame = captureWrite();
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onSettingsRead(eq(ctx), eq(settings));
|
||||
frame.release();
|
||||
try {
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onSettingsRead(eq(ctx), eq(settings));
|
||||
} finally {
|
||||
frame.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -148,9 +180,12 @@ public class DefaultHttp2FrameIOTest {
|
||||
writer.writeSettingsAck(ctx, promise);
|
||||
|
||||
ByteBuf frame = captureWrite();
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onSettingsAckRead(eq(ctx));
|
||||
frame.release();
|
||||
try {
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onSettingsAckRead(eq(ctx));
|
||||
} finally {
|
||||
frame.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -158,10 +193,17 @@ public class DefaultHttp2FrameIOTest {
|
||||
ByteBuf data = dummyData();
|
||||
writer.writePing(ctx, false, data.retain().duplicate(), promise);
|
||||
|
||||
ByteBuf frame = captureWrite();
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onPingRead(eq(ctx), eq(data));
|
||||
frame.release();
|
||||
ByteBuf frame = null;
|
||||
try {
|
||||
frame = captureWrite();
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onPingRead(eq(ctx), eq(data));
|
||||
} finally {
|
||||
if (frame != null) {
|
||||
frame.release();
|
||||
}
|
||||
data.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -169,143 +211,206 @@ public class DefaultHttp2FrameIOTest {
|
||||
ByteBuf data = dummyData();
|
||||
writer.writePing(ctx, true, data.retain().duplicate(), promise);
|
||||
|
||||
ByteBuf frame = captureWrite();
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onPingAckRead(eq(ctx), eq(data));
|
||||
frame.release();
|
||||
ByteBuf frame = null;
|
||||
try {
|
||||
frame = captureWrite();
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onPingAckRead(eq(ctx), eq(data));
|
||||
} finally {
|
||||
if (frame != null) {
|
||||
frame.release();
|
||||
}
|
||||
data.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void goAwayShouldRoundtrip() throws Exception {
|
||||
ByteBuf data = dummyData();
|
||||
writer.writeGoAway(ctx, 1, MAX_UNSIGNED_INT, data.retain().duplicate(), promise);
|
||||
ByteBuf frame = captureWrite();
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onGoAwayRead(eq(ctx), eq(1), eq(MAX_UNSIGNED_INT), eq(data));
|
||||
frame.release();
|
||||
|
||||
ByteBuf frame = null;
|
||||
try {
|
||||
frame = captureWrite();
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onGoAwayRead(eq(ctx), eq(1), eq(MAX_UNSIGNED_INT), eq(data));
|
||||
} finally {
|
||||
if (frame != null) {
|
||||
frame.release();
|
||||
}
|
||||
data.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void windowUpdateShouldRoundtrip() throws Exception {
|
||||
writer.writeWindowUpdate(ctx, 1, Integer.MAX_VALUE, promise);
|
||||
|
||||
ByteBuf frame = captureWrite();
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onWindowUpdateRead(eq(ctx), eq(1), eq(Integer.MAX_VALUE));
|
||||
frame.release();
|
||||
try {
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onWindowUpdateRead(eq(ctx), eq(1), eq(Integer.MAX_VALUE));
|
||||
} finally {
|
||||
frame.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void emptyHeadersShouldRoundtrip() throws Exception {
|
||||
Http2Headers headers = Http2Headers.EMPTY_HEADERS;
|
||||
writer.writeHeaders(ctx, 1, headers, 0, true, promise);
|
||||
|
||||
ByteBuf frame = captureWrite();
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onHeadersRead(eq(ctx), eq(1), eq(headers), eq(0), eq(true));
|
||||
frame.release();
|
||||
try {
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onHeadersRead(eq(ctx), eq(1), eq(headers), eq(0), eq(true));
|
||||
} finally {
|
||||
frame.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void emptyHeadersWithPaddingShouldRoundtrip() throws Exception {
|
||||
Http2Headers headers = Http2Headers.EMPTY_HEADERS;
|
||||
writer.writeHeaders(ctx, 1, headers, 0xFF, true, promise);
|
||||
|
||||
ByteBuf frame = captureWrite();
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onHeadersRead(eq(ctx), eq(1), eq(headers), eq(0xFF), eq(true));
|
||||
frame.release();
|
||||
try {
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onHeadersRead(eq(ctx), eq(1), eq(headers), eq(0xFF), eq(true));
|
||||
} finally {
|
||||
frame.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void headersWithoutPriorityShouldRoundtrip() throws Exception {
|
||||
Http2Headers headers = dummyHeaders();
|
||||
writer.writeHeaders(ctx, 1, headers, 0, true, promise);
|
||||
|
||||
ByteBuf frame = captureWrite();
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onHeadersRead(eq(ctx), eq(1), eq(headers), eq(0), eq(true));
|
||||
frame.release();
|
||||
try {
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onHeadersRead(eq(ctx), eq(1), eq(headers), eq(0), eq(true));
|
||||
} finally {
|
||||
frame.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void headersWithPaddingWithoutPriorityShouldRoundtrip() throws Exception {
|
||||
Http2Headers headers = dummyHeaders();
|
||||
writer.writeHeaders(ctx, 1, headers, 0xFF, true, promise);
|
||||
|
||||
ByteBuf frame = captureWrite();
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onHeadersRead(eq(ctx), eq(1), eq(headers), eq(0xFF), eq(true));
|
||||
frame.release();
|
||||
try {
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onHeadersRead(eq(ctx), eq(1), eq(headers), eq(0xFF), eq(true));
|
||||
} finally {
|
||||
frame.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void headersWithPriorityShouldRoundtrip() throws Exception {
|
||||
Http2Headers headers = dummyHeaders();
|
||||
writer.writeHeaders(ctx, 1, headers, 2, (short) 3, true, 0, true, promise);
|
||||
|
||||
ByteBuf frame = captureWrite();
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onHeadersRead(eq(ctx), eq(1), eq(headers), eq(2), eq((short) 3), eq(true), eq(0),
|
||||
eq(true));
|
||||
frame.release();
|
||||
try {
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener)
|
||||
.onHeadersRead(eq(ctx), eq(1), eq(headers), eq(2), eq((short) 3), eq(true), eq(0), eq(true));
|
||||
} finally {
|
||||
frame.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void headersWithPaddingWithPriorityShouldRoundtrip() throws Exception {
|
||||
Http2Headers headers = dummyHeaders();
|
||||
writer.writeHeaders(ctx, 1, headers, 2, (short) 3, true, 0xFF, true, promise);
|
||||
|
||||
ByteBuf frame = captureWrite();
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onHeadersRead(eq(ctx), eq(1), eq(headers), eq(2), eq((short) 3), eq(true), eq(0xFF),
|
||||
eq(true));
|
||||
frame.release();
|
||||
try {
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onHeadersRead(eq(ctx), eq(1), eq(headers), eq(2), eq((short) 3), eq(true), eq(0xFF),
|
||||
eq(true));
|
||||
} finally {
|
||||
frame.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void continuedHeadersShouldRoundtrip() throws Exception {
|
||||
Http2Headers headers = largeHeaders();
|
||||
writer.writeHeaders(ctx, 1, headers, 2, (short) 3, true, 0, true, promise);
|
||||
|
||||
ByteBuf frame = captureWrite();
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onHeadersRead(eq(ctx), eq(1), eq(headers), eq(2), eq((short) 3), eq(true), eq(0),
|
||||
eq(true));
|
||||
frame.release();
|
||||
try {
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener)
|
||||
.onHeadersRead(eq(ctx), eq(1), eq(headers), eq(2), eq((short) 3), eq(true), eq(0), eq(true));
|
||||
} finally {
|
||||
frame.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void continuedHeadersWithPaddingShouldRoundtrip() throws Exception {
|
||||
Http2Headers headers = largeHeaders();
|
||||
writer.writeHeaders(ctx, 1, headers, 2, (short) 3, true, 0xFF, true, promise);
|
||||
|
||||
ByteBuf frame = captureWrite();
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onHeadersRead(eq(ctx), eq(1), eq(headers), eq(2), eq((short) 3), eq(true), eq(0xFF),
|
||||
eq(true));
|
||||
frame.release();
|
||||
try {
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onHeadersRead(eq(ctx), eq(1), eq(headers), eq(2), eq((short) 3), eq(true), eq(0xFF),
|
||||
eq(true));
|
||||
} finally {
|
||||
frame.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void emptypushPromiseShouldRoundtrip() throws Exception {
|
||||
Http2Headers headers = Http2Headers.EMPTY_HEADERS;
|
||||
writer.writePushPromise(ctx, 1, 2, headers, 0, promise);
|
||||
|
||||
ByteBuf frame = captureWrite();
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onPushPromiseRead(eq(ctx), eq(1), eq(2), eq(headers), eq(0));
|
||||
frame.release();
|
||||
try {
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onPushPromiseRead(eq(ctx), eq(1), eq(2), eq(headers), eq(0));
|
||||
} finally {
|
||||
frame.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pushPromiseShouldRoundtrip() throws Exception {
|
||||
Http2Headers headers = dummyHeaders();
|
||||
writer.writePushPromise(ctx, 1, 2, headers, 0, promise);
|
||||
|
||||
ByteBuf frame = captureWrite();
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onPushPromiseRead(eq(ctx), eq(1), eq(2), eq(headers), eq(0));
|
||||
frame.release();
|
||||
try {
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onPushPromiseRead(eq(ctx), eq(1), eq(2), eq(headers), eq(0));
|
||||
} finally {
|
||||
frame.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pushPromiseWithPaddingShouldRoundtrip() throws Exception {
|
||||
Http2Headers headers = dummyHeaders();
|
||||
writer.writePushPromise(ctx, 1, 2, headers, 0xFF, promise);
|
||||
|
||||
ByteBuf frame = captureWrite();
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onPushPromiseRead(eq(ctx), eq(1), eq(2), eq(headers), eq(0xFF));
|
||||
frame.release();
|
||||
try {
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onPushPromiseRead(eq(ctx), eq(1), eq(2), eq(headers), eq(0xFF));
|
||||
} finally {
|
||||
frame.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -322,10 +427,14 @@ public class DefaultHttp2FrameIOTest {
|
||||
public void continuedPushPromiseWithPaddingShouldRoundtrip() throws Exception {
|
||||
Http2Headers headers = largeHeaders();
|
||||
writer.writePushPromise(ctx, 1, 2, headers, 0xFF, promise);
|
||||
|
||||
ByteBuf frame = captureWrite();
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onPushPromiseRead(eq(ctx), eq(1), eq(2), eq(headers), eq(0xFF));
|
||||
frame.release();
|
||||
try {
|
||||
reader.readFrame(ctx, frame, listener);
|
||||
verify(listener).onPushPromiseRead(eq(ctx), eq(1), eq(2), eq(headers), eq(0xFF));
|
||||
} finally {
|
||||
frame.release();
|
||||
}
|
||||
}
|
||||
|
||||
private ByteBuf captureWrite() {
|
||||
@ -335,12 +444,12 @@ public class DefaultHttp2FrameIOTest {
|
||||
}
|
||||
|
||||
private ByteBuf dummyData() {
|
||||
return ReferenceCountUtil.releaseLater(alloc.buffer().writeBytes("abcdefgh".getBytes(CharsetUtil.UTF_8)));
|
||||
return alloc.buffer().writeBytes("abcdefgh".getBytes(CharsetUtil.UTF_8));
|
||||
}
|
||||
|
||||
private static Http2Headers dummyHeaders() {
|
||||
return DefaultHttp2Headers.newBuilder().method("GET").scheme("https")
|
||||
.authority("example.org").path("/some/path").add("accept", "*/*").build();
|
||||
return DefaultHttp2Headers.newBuilder().method("GET").scheme("https").authority("example.org")
|
||||
.path("/some/path").add("accept", "*/*").build();
|
||||
}
|
||||
|
||||
private static Http2Headers largeHeaders() {
|
||||
|
@ -27,7 +27,6 @@ import org.junit.Test;
|
||||
|
||||
import com.twitter.hpack.Encoder;
|
||||
|
||||
|
||||
/**
|
||||
* Tests for {@link DefaultHttp2HeadersDecoder}.
|
||||
*/
|
||||
@ -42,22 +41,30 @@ public class DefaultHttp2HeadersDecoderTest {
|
||||
|
||||
@Test
|
||||
public void decodeShouldSucceed() throws Exception {
|
||||
ByteBuf buf = encode(":method", "GET", "akey", "avalue");
|
||||
Http2Headers headers = decoder.decodeHeaders(buf).build();
|
||||
assertEquals(2, headers.size());
|
||||
assertEquals("GET", headers.method());
|
||||
assertEquals("avalue", headers.get("akey"));
|
||||
final ByteBuf buf = encode(":method", "GET", "akey", "avalue");
|
||||
try {
|
||||
Http2Headers headers = decoder.decodeHeaders(buf).build();
|
||||
assertEquals(2, headers.size());
|
||||
assertEquals("GET", headers.method());
|
||||
assertEquals("avalue", headers.get("akey"));
|
||||
} finally {
|
||||
buf.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = Http2Exception.class)
|
||||
public void decodeWithInvalidPseudoHeaderShouldFail() throws Exception {
|
||||
ByteBuf buf = encode(":invalid", "GET", "akey", "avalue");
|
||||
decoder.decodeHeaders(buf);
|
||||
final ByteBuf buf = encode(":invalid", "GET", "akey", "avalue");
|
||||
try {
|
||||
decoder.decodeHeaders(buf);
|
||||
} finally {
|
||||
buf.release();
|
||||
}
|
||||
}
|
||||
|
||||
private ByteBuf encode(String... entries) throws Exception {
|
||||
Encoder encoder = new Encoder();
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
final Encoder encoder = new Encoder();
|
||||
final ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
for (int ix = 0; ix < entries.length;) {
|
||||
String key = entries[ix++];
|
||||
String value = entries[ix++];
|
||||
|
@ -22,7 +22,6 @@ import io.netty.buffer.Unpooled;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
|
||||
/**
|
||||
* Tests for {@link DefaultHttp2HeadersEncoder}.
|
||||
*/
|
||||
@ -37,17 +36,21 @@ public class DefaultHttp2HeadersEncoderTest {
|
||||
|
||||
@Test
|
||||
public void encodeShouldSucceed() throws Http2Exception {
|
||||
DefaultHttp2Headers headers =
|
||||
DefaultHttp2Headers.newBuilder().method("GET").add("a", "1").add("a", "2").build();
|
||||
ByteBuf buf = Unpooled.buffer();
|
||||
encoder.encodeHeaders(headers, buf);
|
||||
assertTrue(buf.writerIndex() > 0);
|
||||
DefaultHttp2Headers headers = DefaultHttp2Headers.newBuilder().method("GET").add("a", "1").add("a", "2")
|
||||
.build();
|
||||
final ByteBuf buf = Unpooled.buffer();
|
||||
try {
|
||||
encoder.encodeHeaders(headers, buf);
|
||||
assertTrue(buf.writerIndex() > 0);
|
||||
} finally {
|
||||
buf.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = Http2Exception.class)
|
||||
public void headersExceedMaxSetSizeShouldFail() throws Http2Exception {
|
||||
DefaultHttp2Headers headers =
|
||||
DefaultHttp2Headers.newBuilder().method("GET").add("a", "1").add("a", "2").build();
|
||||
DefaultHttp2Headers headers = DefaultHttp2Headers.newBuilder().method("GET").add("a", "1").add("a", "2")
|
||||
.build();
|
||||
|
||||
encoder.maxHeaderListSize(2);
|
||||
encoder.encodeHeaders(headers, Unpooled.buffer());
|
||||
|
@ -153,19 +153,21 @@ public class DefaultHttp2InboundFlowControllerTest {
|
||||
}
|
||||
|
||||
private void applyFlowControl(int dataSize, int padding, boolean endOfStream) throws Http2Exception {
|
||||
ByteBuf buf = dummyData(dataSize);
|
||||
controller.onDataRead(ctx, STREAM_ID, buf, padding, endOfStream);
|
||||
buf.release();
|
||||
final ByteBuf buf = dummyData(dataSize);
|
||||
try {
|
||||
controller.onDataRead(ctx, STREAM_ID, buf, padding, endOfStream);
|
||||
} finally {
|
||||
buf.release();
|
||||
}
|
||||
}
|
||||
|
||||
private static ByteBuf dummyData(int size) {
|
||||
ByteBuf buffer = Unpooled.buffer(size);
|
||||
final ByteBuf buffer = Unpooled.buffer(size);
|
||||
buffer.writerIndex(size);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
private void verifyWindowUpdateSent(int streamId, int windowSizeIncrement)
|
||||
throws Http2Exception {
|
||||
private void verifyWindowUpdateSent(int streamId, int windowSizeIncrement) throws Http2Exception {
|
||||
verify(frameWriter).writeWindowUpdate(eq(ctx), eq(streamId), eq(windowSizeIncrement), eq(promise));
|
||||
}
|
||||
|
||||
@ -174,7 +176,7 @@ public class DefaultHttp2InboundFlowControllerTest {
|
||||
}
|
||||
|
||||
private void verifyWindowUpdateNotSent() throws Http2Exception {
|
||||
verify(frameWriter, never()).writeWindowUpdate(any(ChannelHandlerContext.class), anyInt(),
|
||||
anyInt(), any(ChannelPromise.class));
|
||||
verify(frameWriter, never()).writeWindowUpdate(any(ChannelHandlerContext.class), anyInt(), anyInt(),
|
||||
any(ChannelPromise.class));
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -47,6 +47,7 @@ import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.buffer.UnpooledByteBufAllocator;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
@ -56,6 +57,7 @@ import io.netty.channel.ChannelPromise;
|
||||
import io.netty.channel.DefaultChannelPromise;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
@ -65,8 +67,7 @@ import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
/**
|
||||
* Tests for {@link DelegatingHttp2ConnectionHandlerTest} and its base class
|
||||
* {@link AbstractHttp2ConnectionHandler}.
|
||||
* Tests for {@link DelegatingHttp2ConnectionHandlerTest} and its base class {@link AbstractHttp2ConnectionHandler}.
|
||||
*/
|
||||
public class DelegatingHttp2ConnectionHandlerTest {
|
||||
private static final int STREAM_ID = 1;
|
||||
@ -134,17 +135,13 @@ public class DelegatingHttp2ConnectionHandlerTest {
|
||||
when(local.reservePushStream(eq(PUSH_STREAM_ID), eq(stream))).thenReturn(pushStream);
|
||||
when(remote.createStream(eq(STREAM_ID), anyBoolean())).thenReturn(stream);
|
||||
when(remote.reservePushStream(eq(PUSH_STREAM_ID), eq(stream))).thenReturn(pushStream);
|
||||
when(writer.writeSettings(eq(ctx), any(Http2Settings.class), eq(promise))).thenReturn(
|
||||
future);
|
||||
when(writer.writeGoAway(eq(ctx), anyInt(), anyInt(), any(ByteBuf.class), eq(promise)))
|
||||
when(writer.writeSettings(eq(ctx), any(Http2Settings.class), eq(promise))).thenReturn(future);
|
||||
when(writer.writeGoAway(eq(ctx), anyInt(), anyInt(), any(ByteBuf.class), eq(promise))).thenReturn(future);
|
||||
when(outboundFlow.writeData(eq(ctx), anyInt(), any(ByteBuf.class), anyInt(), anyBoolean(), eq(promise)))
|
||||
.thenReturn(future);
|
||||
when(outboundFlow.writeData(eq(ctx), anyInt(), any(ByteBuf.class), anyInt(),
|
||||
anyBoolean(), eq(promise))) .thenReturn(future);
|
||||
mockContext();
|
||||
|
||||
handler =
|
||||
new DelegatingHttp2ConnectionHandler(connection, reader, writer, inboundFlow,
|
||||
outboundFlow, listener);
|
||||
handler = new DelegatingHttp2ConnectionHandler(connection, reader, writer, inboundFlow, outboundFlow, listener);
|
||||
|
||||
// Simulate activation of the handler to force writing the initial settings.
|
||||
Http2Settings settings = new Http2Settings();
|
||||
@ -186,8 +183,7 @@ public class DelegatingHttp2ConnectionHandlerTest {
|
||||
@Test
|
||||
public void clientShouldSendClientPrefaceStringWhenActive() throws Exception {
|
||||
when(connection.isServer()).thenReturn(false);
|
||||
handler = new DelegatingHttp2ConnectionHandler(connection, reader, writer, inboundFlow,
|
||||
outboundFlow, listener);
|
||||
handler = new DelegatingHttp2ConnectionHandler(connection, reader, writer, inboundFlow, outboundFlow, listener);
|
||||
handler.channelActive(ctx);
|
||||
verify(ctx).write(eq(connectionPrefaceBuf()));
|
||||
}
|
||||
@ -195,8 +191,7 @@ public class DelegatingHttp2ConnectionHandlerTest {
|
||||
@Test
|
||||
public void serverShouldNotSendClientPrefaceStringWhenActive() throws Exception {
|
||||
when(connection.isServer()).thenReturn(true);
|
||||
handler = new DelegatingHttp2ConnectionHandler(connection, reader, writer, inboundFlow,
|
||||
outboundFlow, listener);
|
||||
handler = new DelegatingHttp2ConnectionHandler(connection, reader, writer, inboundFlow, outboundFlow, listener);
|
||||
handler.channelActive(ctx);
|
||||
verify(ctx, never()).write(eq(connectionPrefaceBuf()));
|
||||
}
|
||||
@ -204,8 +199,7 @@ public class DelegatingHttp2ConnectionHandlerTest {
|
||||
@Test
|
||||
public void serverReceivingInvalidClientPrefaceStringShouldCloseConnection() throws Exception {
|
||||
when(connection.isServer()).thenReturn(true);
|
||||
handler = new DelegatingHttp2ConnectionHandler(connection, reader, writer, inboundFlow,
|
||||
outboundFlow, listener);
|
||||
handler = new DelegatingHttp2ConnectionHandler(connection, reader, writer, inboundFlow, outboundFlow, listener);
|
||||
handler.channelRead(ctx, copiedBuffer("BAD_PREFACE", UTF_8));
|
||||
verify(ctx).close();
|
||||
}
|
||||
@ -214,8 +208,7 @@ public class DelegatingHttp2ConnectionHandlerTest {
|
||||
public void serverReceivingValidClientPrefaceStringShouldContinueReadingFrames() throws Exception {
|
||||
reset(listener);
|
||||
when(connection.isServer()).thenReturn(true);
|
||||
handler = new DelegatingHttp2ConnectionHandler(connection, reader, writer, inboundFlow,
|
||||
outboundFlow, listener);
|
||||
handler = new DelegatingHttp2ConnectionHandler(connection, reader, writer, inboundFlow, outboundFlow, listener);
|
||||
handler.channelRead(ctx, connectionPrefaceBuf());
|
||||
verify(ctx, never()).close();
|
||||
decode().onSettingsRead(ctx, new Http2Settings());
|
||||
@ -225,8 +218,7 @@ public class DelegatingHttp2ConnectionHandlerTest {
|
||||
@Test
|
||||
public void closeShouldSendGoAway() throws Exception {
|
||||
handler.close(ctx, promise);
|
||||
verify(writer).writeGoAway(eq(ctx), eq(0), eq((long) NO_ERROR.code()),
|
||||
eq(EMPTY_BUFFER), eq(promise));
|
||||
verify(writer).writeGoAway(eq(ctx), eq(0), eq((long) NO_ERROR.code()), eq(EMPTY_BUFFER), eq(promise));
|
||||
verify(remote).goAwayReceived(0);
|
||||
}
|
||||
|
||||
@ -241,8 +233,7 @@ public class DelegatingHttp2ConnectionHandlerTest {
|
||||
Http2Exception e = new Http2StreamException(STREAM_ID, PROTOCOL_ERROR);
|
||||
handler.exceptionCaught(ctx, e);
|
||||
verify(stream).close();
|
||||
verify(writer).writeRstStream(eq(ctx), eq(STREAM_ID),
|
||||
eq((long) PROTOCOL_ERROR.code()), eq(promise));
|
||||
verify(writer).writeRstStream(eq(ctx), eq(STREAM_ID), eq((long) PROTOCOL_ERROR.code()), eq(promise));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -251,27 +242,36 @@ public class DelegatingHttp2ConnectionHandlerTest {
|
||||
when(remote.lastStreamCreated()).thenReturn(STREAM_ID);
|
||||
handler.exceptionCaught(ctx, e);
|
||||
verify(remote).goAwayReceived(STREAM_ID);
|
||||
verify(writer).writeGoAway(eq(ctx), eq(STREAM_ID), eq((long) PROTOCOL_ERROR.code()),
|
||||
eq(EMPTY_BUFFER), eq(promise));
|
||||
verify(writer).writeGoAway(eq(ctx), eq(STREAM_ID), eq((long) PROTOCOL_ERROR.code()), eq(EMPTY_BUFFER),
|
||||
eq(promise));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dataReadAfterGoAwayShouldApplyFlowControl() throws Exception {
|
||||
when(remote.isGoAwayReceived()).thenReturn(true);
|
||||
decode().onDataRead(ctx, STREAM_ID, dummyData(), 10, true);
|
||||
verify(inboundFlow).onDataRead(eq(ctx), eq(STREAM_ID), eq(dummyData()), eq(10), eq(true));
|
||||
final ByteBuf data = dummyData();
|
||||
try {
|
||||
decode().onDataRead(ctx, STREAM_ID, data, 10, true);
|
||||
verify(inboundFlow).onDataRead(eq(ctx), eq(STREAM_ID), eq(data), eq(10), eq(true));
|
||||
|
||||
// Verify that the event was absorbed and not propagated to the oberver.
|
||||
verify(listener, never()).onDataRead(eq(ctx), anyInt(), any(ByteBuf.class), anyInt(),
|
||||
anyBoolean());
|
||||
// Verify that the event was absorbed and not propagated to the oberver.
|
||||
verify(listener, never()).onDataRead(eq(ctx), anyInt(), any(ByteBuf.class), anyInt(), anyBoolean());
|
||||
} finally {
|
||||
data.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dataReadWithEndOfStreamShouldCloseRemoteSide() throws Exception {
|
||||
decode().onDataRead(ctx, STREAM_ID, dummyData(), 10, true);
|
||||
verify(inboundFlow).onDataRead(eq(ctx), eq(STREAM_ID), eq(dummyData()), eq(10), eq(true));
|
||||
verify(stream).closeRemoteSide();
|
||||
verify(listener).onDataRead(eq(ctx), eq(STREAM_ID), eq(dummyData()), eq(10), eq(true));
|
||||
final ByteBuf data = dummyData();
|
||||
try {
|
||||
decode().onDataRead(ctx, STREAM_ID, data, 10, true);
|
||||
verify(inboundFlow).onDataRead(eq(ctx), eq(STREAM_ID), eq(data), eq(10), eq(true));
|
||||
verify(stream).closeRemoteSide();
|
||||
verify(listener).onDataRead(eq(ctx), eq(STREAM_ID), eq(data), eq(10), eq(true));
|
||||
} finally {
|
||||
data.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -281,8 +281,7 @@ public class DelegatingHttp2ConnectionHandlerTest {
|
||||
verify(remote, never()).createStream(eq(STREAM_ID), eq(false));
|
||||
|
||||
// Verify that the event was absorbed and not propagated to the oberver.
|
||||
verify(listener, never()).onHeadersRead(eq(ctx), anyInt(), any(Http2Headers.class),
|
||||
anyInt(), anyBoolean());
|
||||
verify(listener, never()).onHeadersRead(eq(ctx), anyInt(), any(Http2Headers.class), anyInt(), anyBoolean());
|
||||
verify(remote, never()).createStream(anyInt(), anyBoolean());
|
||||
}
|
||||
|
||||
@ -291,8 +290,8 @@ public class DelegatingHttp2ConnectionHandlerTest {
|
||||
when(remote.createStream(eq(5), eq(false))).thenReturn(stream);
|
||||
decode().onHeadersRead(ctx, 5, EMPTY_HEADERS, 0, false);
|
||||
verify(remote).createStream(eq(5), eq(false));
|
||||
verify(listener).onHeadersRead(eq(ctx), eq(5), eq(EMPTY_HEADERS), eq(0),
|
||||
eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0), eq(false));
|
||||
verify(listener).onHeadersRead(eq(ctx), eq(5), eq(EMPTY_HEADERS), eq(0), eq(DEFAULT_PRIORITY_WEIGHT),
|
||||
eq(false), eq(0), eq(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -300,8 +299,8 @@ public class DelegatingHttp2ConnectionHandlerTest {
|
||||
when(remote.createStream(eq(5), eq(true))).thenReturn(stream);
|
||||
decode().onHeadersRead(ctx, 5, EMPTY_HEADERS, 0, true);
|
||||
verify(remote).createStream(eq(5), eq(true));
|
||||
verify(listener).onHeadersRead(eq(ctx), eq(5), eq(EMPTY_HEADERS), eq(0),
|
||||
eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0), eq(true));
|
||||
verify(listener).onHeadersRead(eq(ctx), eq(5), eq(EMPTY_HEADERS), eq(0), eq(DEFAULT_PRIORITY_WEIGHT),
|
||||
eq(false), eq(0), eq(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -309,8 +308,8 @@ public class DelegatingHttp2ConnectionHandlerTest {
|
||||
when(stream.state()).thenReturn(RESERVED_REMOTE);
|
||||
decode().onHeadersRead(ctx, STREAM_ID, EMPTY_HEADERS, 0, false);
|
||||
verify(stream).openForPush();
|
||||
verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(EMPTY_HEADERS), eq(0),
|
||||
eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0), eq(false));
|
||||
verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(EMPTY_HEADERS), eq(0), eq(DEFAULT_PRIORITY_WEIGHT),
|
||||
eq(false), eq(0), eq(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -319,8 +318,8 @@ public class DelegatingHttp2ConnectionHandlerTest {
|
||||
decode().onHeadersRead(ctx, STREAM_ID, EMPTY_HEADERS, 0, true);
|
||||
verify(stream).openForPush();
|
||||
verify(stream).close();
|
||||
verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(EMPTY_HEADERS), eq(0),
|
||||
eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0), eq(true));
|
||||
verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(EMPTY_HEADERS), eq(0), eq(DEFAULT_PRIORITY_WEIGHT),
|
||||
eq(false), eq(0), eq(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -328,16 +327,14 @@ public class DelegatingHttp2ConnectionHandlerTest {
|
||||
when(remote.isGoAwayReceived()).thenReturn(true);
|
||||
decode().onPushPromiseRead(ctx, STREAM_ID, PUSH_STREAM_ID, EMPTY_HEADERS, 0);
|
||||
verify(remote, never()).reservePushStream(anyInt(), any(Http2Stream.class));
|
||||
verify(listener, never()).onPushPromiseRead(eq(ctx), anyInt(), anyInt(),
|
||||
any(Http2Headers.class), anyInt());
|
||||
verify(listener, never()).onPushPromiseRead(eq(ctx), anyInt(), anyInt(), any(Http2Headers.class), anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pushPromiseReadShouldSucceed() throws Exception {
|
||||
decode().onPushPromiseRead(ctx, STREAM_ID, PUSH_STREAM_ID, EMPTY_HEADERS, 0);
|
||||
verify(remote).reservePushStream(eq(PUSH_STREAM_ID), eq(stream));
|
||||
verify(listener).onPushPromiseRead(eq(ctx), eq(STREAM_ID), eq(PUSH_STREAM_ID),
|
||||
eq(EMPTY_HEADERS), eq(0));
|
||||
verify(listener).onPushPromiseRead(eq(ctx), eq(STREAM_ID), eq(PUSH_STREAM_ID), eq(EMPTY_HEADERS), eq(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -453,60 +450,88 @@ public class DelegatingHttp2ConnectionHandlerTest {
|
||||
@Test
|
||||
public void dataWriteAfterGoAwayShouldFail() throws Exception {
|
||||
when(connection.isGoAway()).thenReturn(true);
|
||||
ChannelFuture future = handler.writeData(ctx, STREAM_ID, dummyData(), 0, false, promise);
|
||||
assertTrue(future.awaitUninterruptibly().cause() instanceof Http2Exception);
|
||||
final ByteBuf data = dummyData();
|
||||
try {
|
||||
ChannelFuture future = handler.writeData(ctx, STREAM_ID, data, 0, false, promise);
|
||||
assertTrue(future.awaitUninterruptibly().cause() instanceof Http2Exception);
|
||||
} finally {
|
||||
while (data.refCnt() > 0) {
|
||||
data.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dataWriteShouldSucceed() throws Exception {
|
||||
handler.writeData(ctx, STREAM_ID, dummyData(), 0, false, promise);
|
||||
verify(outboundFlow).writeData(eq(ctx), eq(STREAM_ID), eq(dummyData()), eq(0), eq(false), eq(promise));
|
||||
final ByteBuf data = dummyData();
|
||||
try {
|
||||
handler.writeData(ctx, STREAM_ID, data, 0, false, promise);
|
||||
verify(outboundFlow).writeData(eq(ctx), eq(STREAM_ID), eq(data), eq(0), eq(false), eq(promise));
|
||||
} finally {
|
||||
data.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dataWriteShouldHalfCloseStream() throws Exception {
|
||||
reset(future);
|
||||
handler.writeData(ctx, STREAM_ID, dummyData(), 0, true, promise);
|
||||
verify(outboundFlow).writeData(eq(ctx), eq(STREAM_ID), eq(dummyData()), eq(0), eq(true), eq(promise));
|
||||
final ByteBuf data = dummyData();
|
||||
try {
|
||||
handler.writeData(ctx, STREAM_ID, data, 0, true, promise);
|
||||
verify(outboundFlow).writeData(eq(ctx), eq(STREAM_ID), eq(data), eq(0), eq(true), eq(promise));
|
||||
|
||||
// Invoke the listener callback indicating that the write completed successfully.
|
||||
ArgumentCaptor<ChannelFutureListener> captor = ArgumentCaptor.forClass(ChannelFutureListener.class);
|
||||
verify(future).addListener(captor.capture());
|
||||
when(future.isSuccess()).thenReturn(true);
|
||||
captor.getValue().operationComplete(future);
|
||||
verify(stream).closeLocalSide();
|
||||
// Invoke the listener callback indicating that the write completed successfully.
|
||||
ArgumentCaptor<ChannelFutureListener> captor = ArgumentCaptor.forClass(ChannelFutureListener.class);
|
||||
verify(future).addListener(captor.capture());
|
||||
when(future.isSuccess()).thenReturn(true);
|
||||
captor.getValue().operationComplete(future);
|
||||
verify(stream).closeLocalSide();
|
||||
} finally {
|
||||
data.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dataWriteWithFailureShouldHandleException() throws Exception {
|
||||
reset(future);
|
||||
handler.writeData(ctx, STREAM_ID, dummyData(), 0, true, promise);
|
||||
verify(outboundFlow).writeData(eq(ctx), eq(STREAM_ID), eq(dummyData()), eq(0), eq(true), eq(promise));
|
||||
final String msg = "fake exception";
|
||||
final ByteBuf exceptionData = Unpooled.copiedBuffer(msg.getBytes(UTF_8));
|
||||
final ByteBuf data = dummyData();
|
||||
List<ByteBuf> goAwayDataCapture = null;
|
||||
try {
|
||||
handler.writeData(ctx, STREAM_ID, data, 0, true, promise);
|
||||
verify(outboundFlow).writeData(eq(ctx), eq(STREAM_ID), eq(data), eq(0), eq(true), eq(promise));
|
||||
|
||||
// Invoke the listener callback indicating that the write failed.
|
||||
String msg = "fake exception";
|
||||
ArgumentCaptor<ChannelFutureListener> captor = ArgumentCaptor.forClass(ChannelFutureListener.class);
|
||||
verify(future).addListener(captor.capture());
|
||||
when(future.isSuccess()).thenReturn(false);
|
||||
when(future.cause()).thenReturn(new RuntimeException(msg));
|
||||
captor.getValue().operationComplete(future);
|
||||
ArgumentCaptor<ByteBuf> bufferCaptor = ArgumentCaptor.forClass(ByteBuf.class);
|
||||
verify(writer).writeGoAway(eq(ctx), eq(0), eq((long) INTERNAL_ERROR.code()),
|
||||
bufferCaptor.capture(), eq(promise));
|
||||
ByteBuf writtenBuffer = bufferCaptor.getValue();
|
||||
assertEquals(wrappedBuffer(msg.getBytes(UTF_8)), writtenBuffer);
|
||||
writtenBuffer.release();
|
||||
verify(remote).goAwayReceived(0);
|
||||
// Invoke the listener callback indicating that the write failed.
|
||||
ArgumentCaptor<ChannelFutureListener> captor = ArgumentCaptor.forClass(ChannelFutureListener.class);
|
||||
verify(future).addListener(captor.capture());
|
||||
when(future.isSuccess()).thenReturn(false);
|
||||
when(future.cause()).thenReturn(new RuntimeException(msg));
|
||||
captor.getValue().operationComplete(future);
|
||||
final ArgumentCaptor<ByteBuf> bufferCaptor = ArgumentCaptor.forClass(ByteBuf.class);
|
||||
verify(writer).writeGoAway(eq(ctx), eq(0), eq((long) INTERNAL_ERROR.code()), bufferCaptor.capture(),
|
||||
eq(promise));
|
||||
goAwayDataCapture = bufferCaptor.getAllValues();
|
||||
assertEquals(exceptionData, goAwayDataCapture.get(0));
|
||||
verify(remote).goAwayReceived(0);
|
||||
} finally {
|
||||
data.release();
|
||||
exceptionData.release();
|
||||
if (goAwayDataCapture != null) {
|
||||
for (int i = 0; i < goAwayDataCapture.size(); ++i) {
|
||||
goAwayDataCapture.get(i).release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void headersWriteAfterGoAwayShouldFail() throws Exception {
|
||||
when(connection.isGoAway()).thenReturn(true);
|
||||
ChannelFuture future = handler.writeHeaders(
|
||||
ctx, 5, EMPTY_HEADERS, 0, (short) 255, false, 0, false, promise);
|
||||
ChannelFuture future = handler.writeHeaders(ctx, 5, EMPTY_HEADERS, 0, (short) 255, false, 0, false, promise);
|
||||
verify(local, never()).createStream(anyInt(), anyBoolean());
|
||||
verify(writer, never()).writeHeaders(eq(ctx), anyInt(),
|
||||
any(Http2Headers.class), anyInt(), anyBoolean(), eq(promise));
|
||||
verify(writer, never()).writeHeaders(eq(ctx), anyInt(), any(Http2Headers.class), anyInt(), anyBoolean(),
|
||||
eq(promise));
|
||||
assertTrue(future.awaitUninterruptibly().cause() instanceof Http2Exception);
|
||||
}
|
||||
|
||||
@ -515,8 +540,8 @@ public class DelegatingHttp2ConnectionHandlerTest {
|
||||
when(local.createStream(eq(5), eq(false))).thenReturn(stream);
|
||||
handler.writeHeaders(ctx, 5, EMPTY_HEADERS, 0, false, promise);
|
||||
verify(local).createStream(eq(5), eq(false));
|
||||
verify(writer).writeHeaders(eq(ctx), eq(5), eq(EMPTY_HEADERS), eq(0),
|
||||
eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0), eq(false), eq(promise));
|
||||
verify(writer).writeHeaders(eq(ctx), eq(5), eq(EMPTY_HEADERS), eq(0), eq(DEFAULT_PRIORITY_WEIGHT), eq(false),
|
||||
eq(0), eq(false), eq(promise));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -524,8 +549,8 @@ public class DelegatingHttp2ConnectionHandlerTest {
|
||||
when(local.createStream(eq(5), eq(true))).thenReturn(stream);
|
||||
handler.writeHeaders(ctx, 5, EMPTY_HEADERS, 0, true, promise);
|
||||
verify(local).createStream(eq(5), eq(true));
|
||||
verify(writer).writeHeaders(eq(ctx), eq(5), eq(EMPTY_HEADERS), eq(0),
|
||||
eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0), eq(true), eq(promise));
|
||||
verify(writer).writeHeaders(eq(ctx), eq(5), eq(EMPTY_HEADERS), eq(0), eq(DEFAULT_PRIORITY_WEIGHT), eq(false),
|
||||
eq(0), eq(true), eq(promise));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -534,8 +559,8 @@ public class DelegatingHttp2ConnectionHandlerTest {
|
||||
handler.writeHeaders(ctx, STREAM_ID, EMPTY_HEADERS, 0, false, promise);
|
||||
verify(stream).openForPush();
|
||||
verify(stream, never()).closeLocalSide();
|
||||
verify(writer).writeHeaders(eq(ctx), eq(STREAM_ID), eq(EMPTY_HEADERS), eq(0),
|
||||
eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0), eq(false), eq(promise));
|
||||
verify(writer).writeHeaders(eq(ctx), eq(STREAM_ID), eq(EMPTY_HEADERS), eq(0), eq(DEFAULT_PRIORITY_WEIGHT),
|
||||
eq(false), eq(0), eq(false), eq(promise));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -544,8 +569,8 @@ public class DelegatingHttp2ConnectionHandlerTest {
|
||||
handler.writeHeaders(ctx, STREAM_ID, EMPTY_HEADERS, 0, true, promise);
|
||||
verify(stream).openForPush();
|
||||
verify(stream).closeLocalSide();
|
||||
verify(writer).writeHeaders(eq(ctx), eq(STREAM_ID), eq(EMPTY_HEADERS), eq(0),
|
||||
eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0), eq(true), eq(promise));
|
||||
verify(writer).writeHeaders(eq(ctx), eq(STREAM_ID), eq(EMPTY_HEADERS), eq(0), eq(DEFAULT_PRIORITY_WEIGHT),
|
||||
eq(false), eq(0), eq(true), eq(promise));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -559,8 +584,8 @@ public class DelegatingHttp2ConnectionHandlerTest {
|
||||
public void pushPromiseWriteShouldReserveStream() throws Exception {
|
||||
handler.writePushPromise(ctx, STREAM_ID, PUSH_STREAM_ID, EMPTY_HEADERS, 0, promise);
|
||||
verify(local).reservePushStream(eq(PUSH_STREAM_ID), eq(stream));
|
||||
verify(writer).writePushPromise(eq(ctx), eq(STREAM_ID), eq(PUSH_STREAM_ID),
|
||||
eq(EMPTY_HEADERS), eq(0), eq(promise));
|
||||
verify(writer).writePushPromise(eq(ctx), eq(STREAM_ID), eq(PUSH_STREAM_ID), eq(EMPTY_HEADERS), eq(0),
|
||||
eq(promise));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -574,8 +599,7 @@ public class DelegatingHttp2ConnectionHandlerTest {
|
||||
public void priorityWriteShouldSetPriorityForStream() throws Exception {
|
||||
handler.writePriority(ctx, STREAM_ID, 0, (short) 255, true, promise);
|
||||
verify(stream).setPriority(eq(0), eq((short) 255), eq(true));
|
||||
verify(writer).writePriority(eq(ctx), eq(STREAM_ID), eq(0), eq((short) 255),
|
||||
eq(true), eq(promise));
|
||||
verify(writer).writePriority(eq(ctx), eq(STREAM_ID), eq(0), eq((short) 255), eq(true), eq(promise));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -588,8 +612,7 @@ public class DelegatingHttp2ConnectionHandlerTest {
|
||||
public void rstStreamWriteShouldCloseStream() throws Exception {
|
||||
handler.writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code(), promise);
|
||||
verify(stream).close();
|
||||
verify(writer).writeRstStream(eq(ctx), eq(STREAM_ID),
|
||||
eq((long) PROTOCOL_ERROR.code()), eq(promise));
|
||||
verify(writer).writeRstStream(eq(ctx), eq(STREAM_ID), eq((long) PROTOCOL_ERROR.code()), eq(promise));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -652,8 +675,7 @@ public class DelegatingHttp2ConnectionHandlerTest {
|
||||
* Calls the decode method on the handler and gets back the captured internal listener
|
||||
*/
|
||||
private Http2FrameListener decode() throws Exception {
|
||||
ArgumentCaptor<Http2FrameListener> internallistener =
|
||||
ArgumentCaptor.forClass(Http2FrameListener.class);
|
||||
ArgumentCaptor<Http2FrameListener> internallistener = ArgumentCaptor.forClass(Http2FrameListener.class);
|
||||
doNothing().when(reader).readFrame(eq(ctx), any(ByteBuf.class), internallistener.capture());
|
||||
handler.decode(ctx, EMPTY_BUFFER, Collections.emptyList());
|
||||
return internallistener.getValue();
|
||||
|
@ -20,6 +20,7 @@ import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
|
||||
import static io.netty.handler.codec.http2.Http2CodecUtil.ignoreSettingsHandler;
|
||||
import static io.netty.util.CharsetUtil.UTF_8;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyBoolean;
|
||||
@ -48,11 +49,13 @@ import io.netty.handler.codec.http.HttpRequest;
|
||||
import io.netty.util.NetUtil;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@ -60,6 +63,10 @@ import org.mockito.MockitoAnnotations;
|
||||
* Testing the {@link DelegatingHttp2HttpConnectionHandler} for {@link FullHttpRequest} objects into HTTP/2 frames
|
||||
*/
|
||||
public class DelegatingHttp2HttpConnectionHandlerTest {
|
||||
private static final int CONNECTION_SETUP_READ_COUNT = 2;
|
||||
|
||||
private List<ByteBuf> capturedData;
|
||||
|
||||
@Mock
|
||||
private Http2FrameListener clientListener;
|
||||
|
||||
@ -71,13 +78,13 @@ public class DelegatingHttp2HttpConnectionHandlerTest {
|
||||
private Channel serverChannel;
|
||||
private Channel clientChannel;
|
||||
private CountDownLatch requestLatch;
|
||||
private static final int CONNECTION_SETUP_READ_COUNT = 2;
|
||||
private Http2TestUtil.FrameCountDown serverFrameCountDown;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
requestLatch = new CountDownLatch(CONNECTION_SETUP_READ_COUNT + 1);
|
||||
requestLatch(new CountDownLatch(CONNECTION_SETUP_READ_COUNT + 1));
|
||||
|
||||
sb = new ServerBootstrap();
|
||||
cb = new Bootstrap();
|
||||
@ -88,7 +95,8 @@ public class DelegatingHttp2HttpConnectionHandlerTest {
|
||||
@Override
|
||||
protected void initChannel(Channel ch) throws Exception {
|
||||
ChannelPipeline p = ch.pipeline();
|
||||
p.addLast(new DelegatingHttp2ConnectionHandler(true, new FrameCountDown()));
|
||||
serverFrameCountDown = new Http2TestUtil.FrameCountDown(serverListener, requestLatch);
|
||||
p.addLast(new DelegatingHttp2ConnectionHandler(true, serverFrameCountDown));
|
||||
p.addLast(ignoreSettingsHandler());
|
||||
}
|
||||
});
|
||||
@ -114,6 +122,12 @@ public class DelegatingHttp2HttpConnectionHandlerTest {
|
||||
|
||||
@After
|
||||
public void teardown() throws Exception {
|
||||
if (capturedData != null) {
|
||||
for (int i = 0; i < capturedData.size(); ++i) {
|
||||
capturedData.get(i).release();
|
||||
}
|
||||
capturedData = null;
|
||||
}
|
||||
serverChannel.close().sync();
|
||||
sb.group().shutdownGracefully();
|
||||
sb.childGroup().shutdownGracefully();
|
||||
@ -122,58 +136,77 @@ public class DelegatingHttp2HttpConnectionHandlerTest {
|
||||
|
||||
@Test
|
||||
public void testJustHeadersRequest() throws Exception {
|
||||
final HttpRequest request = new DefaultFullHttpRequest(HTTP_1_1, GET, "/example");
|
||||
final HttpHeaders httpHeaders = request.headers();
|
||||
httpHeaders.set(HttpUtil.ExtensionHeaders.Names.STREAM_ID, 5);
|
||||
httpHeaders.set(HttpHeaders.Names.HOST, "http://my-user_name@www.example.org:5555/example");
|
||||
httpHeaders.set(HttpUtil.ExtensionHeaders.Names.AUTHORITY, "www.example.org:5555");
|
||||
httpHeaders.set(HttpUtil.ExtensionHeaders.Names.SCHEME, "http");
|
||||
httpHeaders.add("foo", "goo");
|
||||
httpHeaders.add("foo", "goo2");
|
||||
httpHeaders.add("foo2", "goo2");
|
||||
final Http2Headers http2Headers = new DefaultHttp2Headers.Builder()
|
||||
.method("GET").path("/example").authority("www.example.org:5555").scheme("http")
|
||||
.add("foo", "goo").add("foo", "goo2").add("foo2", "goo2").build();
|
||||
ChannelPromise writePromise = newPromise();
|
||||
ChannelFuture writeFuture = clientChannel.writeAndFlush(request, writePromise);
|
||||
final FullHttpRequest request = new DefaultFullHttpRequest(HTTP_1_1, GET, "/example");
|
||||
try {
|
||||
final HttpHeaders httpHeaders = request.headers();
|
||||
httpHeaders.set(HttpUtil.ExtensionHeaders.Names.STREAM_ID, 5);
|
||||
httpHeaders.set(HttpHeaders.Names.HOST, "http://my-user_name@www.example.org:5555/example");
|
||||
httpHeaders.set(HttpUtil.ExtensionHeaders.Names.AUTHORITY, "www.example.org:5555");
|
||||
httpHeaders.set(HttpUtil.ExtensionHeaders.Names.SCHEME, "http");
|
||||
httpHeaders.add("foo", "goo");
|
||||
httpHeaders.add("foo", "goo2");
|
||||
httpHeaders.add("foo2", "goo2");
|
||||
final Http2Headers http2Headers = new DefaultHttp2Headers.Builder().method("GET").path("/example")
|
||||
.authority("www.example.org:5555").scheme("http").add("foo", "goo").add("foo", "goo2")
|
||||
.add("foo2", "goo2").build();
|
||||
ChannelPromise writePromise = newPromise();
|
||||
ChannelFuture writeFuture = clientChannel.writeAndFlush(request, writePromise);
|
||||
|
||||
writePromise.awaitUninterruptibly(2, SECONDS);
|
||||
assertTrue(writePromise.isSuccess());
|
||||
writeFuture.awaitUninterruptibly(2, SECONDS);
|
||||
assertTrue(writeFuture.isSuccess());
|
||||
awaitRequests();
|
||||
verify(serverListener).onHeadersRead(any(ChannelHandlerContext.class), eq(5), eq(http2Headers), eq(0),
|
||||
anyShort(), anyBoolean(), eq(0), eq(true));
|
||||
verify(serverListener, never()).onDataRead(any(ChannelHandlerContext.class),
|
||||
anyInt(), any(ByteBuf.class), anyInt(), anyBoolean());
|
||||
writePromise.awaitUninterruptibly(2, SECONDS);
|
||||
assertTrue(writePromise.isSuccess());
|
||||
writeFuture.awaitUninterruptibly(2, SECONDS);
|
||||
assertTrue(writeFuture.isSuccess());
|
||||
awaitRequests();
|
||||
final ArgumentCaptor<ByteBuf> dataCaptor = ArgumentCaptor.forClass(ByteBuf.class);
|
||||
verify(serverListener).onHeadersRead(any(ChannelHandlerContext.class), eq(5), eq(http2Headers), eq(0),
|
||||
anyShort(), anyBoolean(), eq(0), eq(true));
|
||||
verify(serverListener, never()).onDataRead(any(ChannelHandlerContext.class), anyInt(),
|
||||
dataCaptor.capture(), anyInt(), anyBoolean());
|
||||
} finally {
|
||||
request.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequestWithBody() throws Exception {
|
||||
requestLatch = new CountDownLatch(CONNECTION_SETUP_READ_COUNT + 2);
|
||||
requestLatch(new CountDownLatch(CONNECTION_SETUP_READ_COUNT + 2));
|
||||
final String text = "foooooogoooo";
|
||||
final HttpRequest request = new DefaultFullHttpRequest(HTTP_1_1, POST, "/example",
|
||||
Unpooled.copiedBuffer(text, UTF_8));
|
||||
final HttpHeaders httpHeaders = request.headers();
|
||||
httpHeaders.set(HttpHeaders.Names.HOST, "http://your_user-name123@www.example.org:5555/example");
|
||||
httpHeaders.add("foo", "goo");
|
||||
httpHeaders.add("foo", "goo2");
|
||||
httpHeaders.add("foo2", "goo2");
|
||||
final Http2Headers http2Headers = new DefaultHttp2Headers.Builder()
|
||||
.method("POST").path("/example").authority("www.example.org:5555").scheme("http")
|
||||
.add("foo", "goo").add("foo", "goo2").add("foo2", "goo2").build();
|
||||
ChannelPromise writePromise = newPromise();
|
||||
ChannelFuture writeFuture = clientChannel.writeAndFlush(request, writePromise);
|
||||
final ByteBuf data = Unpooled.copiedBuffer(text, UTF_8);
|
||||
try {
|
||||
final HttpRequest request = new DefaultFullHttpRequest(HTTP_1_1, POST, "/example", data.retain());
|
||||
final HttpHeaders httpHeaders = request.headers();
|
||||
httpHeaders.set(HttpHeaders.Names.HOST, "http://your_user-name123@www.example.org:5555/example");
|
||||
httpHeaders.add("foo", "goo");
|
||||
httpHeaders.add("foo", "goo2");
|
||||
httpHeaders.add("foo2", "goo2");
|
||||
final Http2Headers http2Headers = new DefaultHttp2Headers.Builder().method("POST").path("/example")
|
||||
.authority("www.example.org:5555").scheme("http").add("foo", "goo").add("foo", "goo2")
|
||||
.add("foo2", "goo2").build();
|
||||
ChannelPromise writePromise = newPromise();
|
||||
ChannelFuture writeFuture = clientChannel.writeAndFlush(request, writePromise);
|
||||
|
||||
writePromise.awaitUninterruptibly(2, SECONDS);
|
||||
assertTrue(writePromise.isSuccess());
|
||||
writeFuture.awaitUninterruptibly(2, SECONDS);
|
||||
assertTrue(writeFuture.isSuccess());
|
||||
awaitRequests();
|
||||
verify(serverListener).onHeadersRead(any(ChannelHandlerContext.class), eq(3), eq(http2Headers), eq(0),
|
||||
anyShort(), anyBoolean(), eq(0), eq(false));
|
||||
verify(serverListener).onDataRead(any(ChannelHandlerContext.class),
|
||||
eq(3), eq(Unpooled.copiedBuffer(text.getBytes())), eq(0), eq(true));
|
||||
writePromise.awaitUninterruptibly(2, SECONDS);
|
||||
assertTrue(writePromise.isSuccess());
|
||||
writeFuture.awaitUninterruptibly(2, SECONDS);
|
||||
assertTrue(writeFuture.isSuccess());
|
||||
awaitRequests();
|
||||
final ArgumentCaptor<ByteBuf> dataCaptor = ArgumentCaptor.forClass(ByteBuf.class);
|
||||
verify(serverListener).onHeadersRead(any(ChannelHandlerContext.class), eq(3), eq(http2Headers), eq(0),
|
||||
anyShort(), anyBoolean(), eq(0), eq(false));
|
||||
verify(serverListener).onDataRead(any(ChannelHandlerContext.class), eq(3), dataCaptor.capture(), eq(0),
|
||||
eq(true));
|
||||
capturedData = dataCaptor.getAllValues();
|
||||
assertEquals(data, capturedData.get(0));
|
||||
} finally {
|
||||
data.release();
|
||||
}
|
||||
}
|
||||
|
||||
private void requestLatch(CountDownLatch latch) {
|
||||
requestLatch = latch;
|
||||
if (serverFrameCountDown != null) {
|
||||
serverFrameCountDown.messageLatch(latch);
|
||||
}
|
||||
}
|
||||
|
||||
private void awaitRequests() throws Exception {
|
||||
@ -187,105 +220,4 @@ public class DelegatingHttp2HttpConnectionHandlerTest {
|
||||
private ChannelPromise newPromise() {
|
||||
return ctx().newPromise();
|
||||
}
|
||||
|
||||
/**
|
||||
* A decorator around the serverObserver that counts down the latch so that we can await the
|
||||
* completion of the request.
|
||||
*/
|
||||
private final class FrameCountDown implements Http2FrameListener {
|
||||
|
||||
@Override
|
||||
public void onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding,
|
||||
boolean endOfStream)
|
||||
throws Http2Exception {
|
||||
serverListener.onDataRead(ctx, streamId, copy(data), padding, endOfStream);
|
||||
requestLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers,
|
||||
int padding, boolean endStream) throws Http2Exception {
|
||||
serverListener.onHeadersRead(ctx, streamId, headers, padding, endStream);
|
||||
requestLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers,
|
||||
int streamDependency, short weight, boolean exclusive, int padding,
|
||||
boolean endStream) throws Http2Exception {
|
||||
serverListener.onHeadersRead(ctx, streamId, headers, streamDependency, weight,
|
||||
exclusive, padding, endStream);
|
||||
requestLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPriorityRead(ChannelHandlerContext ctx, int streamId, int streamDependency,
|
||||
short weight, boolean exclusive) throws Http2Exception {
|
||||
serverListener.onPriorityRead(ctx, streamId, streamDependency, weight, exclusive);
|
||||
requestLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRstStreamRead(ChannelHandlerContext ctx, int streamId, long errorCode)
|
||||
throws Http2Exception {
|
||||
serverListener.onRstStreamRead(ctx, streamId, errorCode);
|
||||
requestLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSettingsAckRead(ChannelHandlerContext ctx) throws Http2Exception {
|
||||
serverListener.onSettingsAckRead(ctx);
|
||||
requestLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSettingsRead(ChannelHandlerContext ctx, Http2Settings settings) throws Http2Exception {
|
||||
serverListener.onSettingsRead(ctx, settings);
|
||||
requestLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPingRead(ChannelHandlerContext ctx, ByteBuf data) throws Http2Exception {
|
||||
serverListener.onPingRead(ctx, copy(data));
|
||||
requestLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPingAckRead(ChannelHandlerContext ctx, ByteBuf data) throws Http2Exception {
|
||||
serverListener.onPingAckRead(ctx, copy(data));
|
||||
requestLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPushPromiseRead(ChannelHandlerContext ctx, int streamId,
|
||||
int promisedStreamId, Http2Headers headers, int padding) throws Http2Exception {
|
||||
serverListener.onPushPromiseRead(ctx, streamId, promisedStreamId, headers, padding);
|
||||
requestLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGoAwayRead(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData)
|
||||
throws Http2Exception {
|
||||
serverListener.onGoAwayRead(ctx, lastStreamId, errorCode, copy(debugData));
|
||||
requestLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowUpdateRead(ChannelHandlerContext ctx, int streamId,
|
||||
int windowSizeIncrement) throws Http2Exception {
|
||||
serverListener.onWindowUpdateRead(ctx, streamId, windowSizeIncrement);
|
||||
requestLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnknownFrame(ChannelHandlerContext ctx, byte frameType, int streamId,
|
||||
Http2Flags flags, ByteBuf payload) {
|
||||
serverListener.onUnknownFrame(ctx, frameType, streamId, flags, payload);
|
||||
requestLatch.countDown();
|
||||
}
|
||||
|
||||
ByteBuf copy(ByteBuf buffer) {
|
||||
return Unpooled.copiedBuffer(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,10 +21,8 @@ import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyBoolean;
|
||||
import static org.mockito.Matchers.anyInt;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
@ -41,9 +39,11 @@ import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.handler.codec.http2.Http2TestUtil.Http2Runnable;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import io.netty.util.NetUtil;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@ -51,17 +51,16 @@ import java.util.concurrent.TimeUnit;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
/**
|
||||
* Tests the full HTTP/2 framing stack including the connection and preface handlers.
|
||||
*/
|
||||
public class Http2ConnectionRoundtripTest {
|
||||
|
||||
private static final int NUM_STREAMS = 1000;
|
||||
private static final int STRESS_TIMEOUT_SECONDS = 30;
|
||||
private static final int NUM_STREAMS = 5000;
|
||||
private final byte[] DATA_TEXT = "hello world".getBytes(UTF_8);
|
||||
|
||||
@Mock
|
||||
@ -75,13 +74,16 @@ public class Http2ConnectionRoundtripTest {
|
||||
private Bootstrap cb;
|
||||
private Channel serverChannel;
|
||||
private Channel clientChannel;
|
||||
private final CountDownLatch requestLatch = new CountDownLatch(NUM_STREAMS * 3);
|
||||
private CountDownLatch dataLatch = new CountDownLatch(NUM_STREAMS * DATA_TEXT.length);
|
||||
private Http2TestUtil.FrameCountDown serverFrameCountDown;
|
||||
private CountDownLatch requestLatch;
|
||||
private CountDownLatch dataLatch;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
requestLatch(new CountDownLatch(NUM_STREAMS * 3));
|
||||
dataLatch(new CountDownLatch(NUM_STREAMS * DATA_TEXT.length));
|
||||
sb = new ServerBootstrap();
|
||||
cb = new Bootstrap();
|
||||
|
||||
@ -91,7 +93,8 @@ public class Http2ConnectionRoundtripTest {
|
||||
@Override
|
||||
protected void initChannel(Channel ch) throws Exception {
|
||||
ChannelPipeline p = ch.pipeline();
|
||||
p.addLast(new DelegatingHttp2ConnectionHandler(true, new FrameCountDown()));
|
||||
serverFrameCountDown = new Http2TestUtil.FrameCountDown(serverListener, requestLatch, dataLatch);
|
||||
p.addLast(new DelegatingHttp2ConnectionHandler(true, serverFrameCountDown));
|
||||
p.addLast(Http2CodecUtil.ignoreSettingsHandler());
|
||||
}
|
||||
});
|
||||
@ -126,84 +129,113 @@ public class Http2ConnectionRoundtripTest {
|
||||
|
||||
@Test
|
||||
public void flowControlProperlyChunksLargeMessage() throws Exception {
|
||||
final Http2Headers headers =
|
||||
new DefaultHttp2Headers.Builder().method("GET").scheme("https")
|
||||
.authority("example.org").path("/some/path/resource2").build();
|
||||
final Http2Headers headers = new DefaultHttp2Headers.Builder().method("GET").scheme("https")
|
||||
.authority("example.org").path("/some/path/resource2").build();
|
||||
|
||||
// Create a large message to send.
|
||||
int length = 10485760; // 10MB
|
||||
final int length = 10485760; // 10MB
|
||||
|
||||
// Create a buffer filled with random bytes.
|
||||
byte[] bytes = new byte[length];
|
||||
final byte[] bytes = new byte[length];
|
||||
new Random().nextBytes(bytes);
|
||||
final ByteBuf data = Unpooled.wrappedBuffer(bytes);
|
||||
List<ByteBuf> capturedData = null;
|
||||
try {
|
||||
// Initialize the data latch based on the number of bytes expected.
|
||||
requestLatch(new CountDownLatch(2));
|
||||
dataLatch(new CountDownLatch(length));
|
||||
|
||||
// Prepare a receive buffer and populate it as DATA frames are received by the server.
|
||||
final ByteBuf receivedData = Unpooled.buffer(length);
|
||||
doAnswer(new Answer<Void>() {
|
||||
@Override
|
||||
public Void answer(InvocationOnMock invocation) throws Throwable {
|
||||
ByteBuf buf = (ByteBuf) invocation.getArguments()[2];
|
||||
receivedData.writeBytes(buf);
|
||||
return null;
|
||||
}
|
||||
}).when(serverListener).onDataRead(any(ChannelHandlerContext.class), eq(3),
|
||||
any(ByteBuf.class), eq(0), anyBoolean());
|
||||
// Create the stream and send all of the data at once.
|
||||
runInChannel(clientChannel, new Http2Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
http2Client.writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false, newPromise());
|
||||
http2Client.writeData(ctx(), 3, data.retain(), 0, true, newPromise());
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize the data latch based on the number of bytes expected.
|
||||
dataLatch = new CountDownLatch(length);
|
||||
// Wait for all DATA frames to be received at the server.
|
||||
assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
|
||||
|
||||
// Create the stream and send all of the data at once.
|
||||
runInChannel(clientChannel, new Http2Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
http2Client.writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false,
|
||||
newPromise());
|
||||
http2Client.writeData(ctx(), 3, data.copy(), 0, true, newPromise());
|
||||
}
|
||||
});
|
||||
// Verify that headers were received and only one DATA frame was received with endStream set.
|
||||
final ArgumentCaptor<ByteBuf> dataCaptor = ArgumentCaptor.forClass(ByteBuf.class);
|
||||
verify(serverListener).onHeadersRead(any(ChannelHandlerContext.class), eq(3), eq(headers), eq(0),
|
||||
eq((short) 16), eq(false), eq(0), eq(false));
|
||||
verify(serverListener).onDataRead(any(ChannelHandlerContext.class), eq(3), dataCaptor.capture(), eq(0),
|
||||
eq(true));
|
||||
|
||||
// Wait for all DATA frames to be received at the server.
|
||||
assertTrue(dataLatch.await(5, TimeUnit.SECONDS));
|
||||
|
||||
// Verify that headers were received and only one DATA frame was received with endStream set.
|
||||
verify(serverListener).onHeadersRead(any(ChannelHandlerContext.class), eq(3), eq(headers),
|
||||
eq(0), eq((short) 16), eq(false), eq(0), eq(false));
|
||||
verify(serverListener).onDataRead(any(ChannelHandlerContext.class), eq(3),
|
||||
any(ByteBuf.class), eq(0), eq(true));
|
||||
|
||||
// Verify we received all the bytes.
|
||||
assertEquals(data, receivedData);
|
||||
// Verify we received all the bytes.
|
||||
capturedData = dataCaptor.getAllValues();
|
||||
assertEquals(data, capturedData.get(0));
|
||||
} finally {
|
||||
data.release();
|
||||
release(capturedData);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stressTest() throws Exception {
|
||||
final Http2Headers headers =
|
||||
new DefaultHttp2Headers.Builder().method("GET").scheme("https")
|
||||
.authority("example.org").path("/some/path/resource2").build();
|
||||
final Http2Headers headers = new DefaultHttp2Headers.Builder().method("GET").scheme("https")
|
||||
.authority("example.org").path("/some/path/resource2").build();
|
||||
final String text = "hello world";
|
||||
final String pingMsg = "12345678";
|
||||
runInChannel(clientChannel, new Http2Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (int i = 0, nextStream = 3; i < NUM_STREAMS; ++i, nextStream += 2) {
|
||||
http2Client.writeHeaders(ctx(), nextStream, headers, 0, (short) 16, false, 0,
|
||||
false, newPromise());
|
||||
http2Client.writePing(ctx(), Unpooled.copiedBuffer(pingMsg.getBytes()),
|
||||
newPromise());
|
||||
http2Client.writeData(ctx(), nextStream,
|
||||
Unpooled.copiedBuffer(text.getBytes()), 0, true, newPromise());
|
||||
final ByteBuf data = Unpooled.copiedBuffer(text.getBytes());
|
||||
final ByteBuf pingData = Unpooled.copiedBuffer(pingMsg.getBytes());
|
||||
List<ByteBuf> capturedData = null;
|
||||
List<ByteBuf> capturedPingData = null;
|
||||
try {
|
||||
runInChannel(clientChannel, new Http2Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (int i = 0, nextStream = 3; i < NUM_STREAMS; ++i, nextStream += 2) {
|
||||
http2Client.writeHeaders(ctx(), nextStream, headers, 0, (short) 16, false, 0, false,
|
||||
newPromise());
|
||||
http2Client.writePing(ctx(), pingData.retain(), newPromise());
|
||||
http2Client.writeData(ctx(), nextStream, data.retain(), 0, true, newPromise());
|
||||
}
|
||||
}
|
||||
});
|
||||
// Wait for all frames to be received.
|
||||
assertTrue(requestLatch.await(STRESS_TIMEOUT_SECONDS, SECONDS));
|
||||
verify(serverListener, times(NUM_STREAMS)).onHeadersRead(any(ChannelHandlerContext.class), anyInt(),
|
||||
eq(headers), eq(0), eq((short) 16), eq(false), eq(0), eq(false));
|
||||
final ArgumentCaptor<ByteBuf> dataCaptor = ArgumentCaptor.forClass(ByteBuf.class);
|
||||
final ArgumentCaptor<ByteBuf> pingDataCaptor = ArgumentCaptor.forClass(ByteBuf.class);
|
||||
verify(serverListener, times(NUM_STREAMS)).onPingRead(any(ChannelHandlerContext.class),
|
||||
pingDataCaptor.capture());
|
||||
capturedPingData = pingDataCaptor.getAllValues();
|
||||
verify(serverListener, times(NUM_STREAMS)).onDataRead(any(ChannelHandlerContext.class), anyInt(),
|
||||
dataCaptor.capture(), eq(0), eq(true));
|
||||
capturedData = dataCaptor.getAllValues();
|
||||
data.resetReaderIndex();
|
||||
pingData.resetReaderIndex();
|
||||
int i;
|
||||
for (i = 0; i < capturedPingData.size(); ++i) {
|
||||
assertEquals(pingData, capturedPingData.get(i));
|
||||
}
|
||||
});
|
||||
// Wait for all frames to be received.
|
||||
assertTrue(requestLatch.await(5, SECONDS));
|
||||
verify(serverListener, times(NUM_STREAMS)).onHeadersRead(any(ChannelHandlerContext.class),
|
||||
anyInt(), eq(headers), eq(0), eq((short) 16), eq(false), eq(0), eq(false));
|
||||
verify(serverListener, times(NUM_STREAMS)).onPingRead(any(ChannelHandlerContext.class),
|
||||
eq(Unpooled.copiedBuffer(pingMsg.getBytes())));
|
||||
verify(serverListener, times(NUM_STREAMS)).onDataRead(any(ChannelHandlerContext.class),
|
||||
anyInt(), eq(Unpooled.copiedBuffer(text.getBytes())), eq(0), eq(true));
|
||||
for (i = 0; i < capturedData.size(); ++i) {
|
||||
assertEquals(capturedData.get(i).toString(CharsetUtil.UTF_8), data, capturedData.get(i));
|
||||
}
|
||||
} finally {
|
||||
data.release();
|
||||
pingData.release();
|
||||
release(capturedData);
|
||||
release(capturedPingData);
|
||||
}
|
||||
}
|
||||
|
||||
private void dataLatch(CountDownLatch latch) {
|
||||
dataLatch = latch;
|
||||
if (serverFrameCountDown != null) {
|
||||
serverFrameCountDown.dataLatch(latch);
|
||||
}
|
||||
}
|
||||
|
||||
private void requestLatch(CountDownLatch latch) {
|
||||
requestLatch = latch;
|
||||
if (serverFrameCountDown != null) {
|
||||
serverFrameCountDown.messageLatch(latch);
|
||||
}
|
||||
}
|
||||
|
||||
private ChannelHandlerContext ctx() {
|
||||
@ -214,107 +246,11 @@ public class Http2ConnectionRoundtripTest {
|
||||
return ctx().newPromise();
|
||||
}
|
||||
|
||||
/**
|
||||
* A decorator around the serverObserver that counts down the latch so that we can await the
|
||||
* completion of the request.
|
||||
*/
|
||||
private final class FrameCountDown implements Http2FrameListener {
|
||||
|
||||
@Override
|
||||
public void onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding,
|
||||
boolean endOfStream)
|
||||
throws Http2Exception {
|
||||
serverListener.onDataRead(ctx, streamId, copy(data), padding, endOfStream);
|
||||
requestLatch.countDown();
|
||||
for (int i = 0; i < data.readableBytes(); ++i) {
|
||||
dataLatch.countDown();
|
||||
private static void release(List<ByteBuf> capturedData) {
|
||||
if (capturedData != null) {
|
||||
for (int i = 0; i < capturedData.size(); ++i) {
|
||||
capturedData.get(i).release();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers,
|
||||
int padding, boolean endStream) throws Http2Exception {
|
||||
serverListener.onHeadersRead(ctx, streamId, headers, padding, endStream);
|
||||
requestLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers,
|
||||
int streamDependency, short weight, boolean exclusive, int padding,
|
||||
boolean endStream) throws Http2Exception {
|
||||
serverListener.onHeadersRead(ctx, streamId, headers, streamDependency, weight,
|
||||
exclusive, padding, endStream);
|
||||
requestLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPriorityRead(ChannelHandlerContext ctx, int streamId, int streamDependency,
|
||||
short weight, boolean exclusive) throws Http2Exception {
|
||||
serverListener.onPriorityRead(ctx, streamId, streamDependency, weight, exclusive);
|
||||
requestLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRstStreamRead(ChannelHandlerContext ctx, int streamId, long errorCode)
|
||||
throws Http2Exception {
|
||||
serverListener.onRstStreamRead(ctx, streamId, errorCode);
|
||||
requestLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSettingsAckRead(ChannelHandlerContext ctx) throws Http2Exception {
|
||||
serverListener.onSettingsAckRead(ctx);
|
||||
requestLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSettingsRead(ChannelHandlerContext ctx, Http2Settings settings) throws Http2Exception {
|
||||
serverListener.onSettingsRead(ctx, settings);
|
||||
requestLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPingRead(ChannelHandlerContext ctx, ByteBuf data) throws Http2Exception {
|
||||
serverListener.onPingRead(ctx, copy(data));
|
||||
requestLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPingAckRead(ChannelHandlerContext ctx, ByteBuf data) throws Http2Exception {
|
||||
serverListener.onPingAckRead(ctx, copy(data));
|
||||
requestLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPushPromiseRead(ChannelHandlerContext ctx, int streamId,
|
||||
int promisedStreamId, Http2Headers headers, int padding) throws Http2Exception {
|
||||
serverListener.onPushPromiseRead(ctx, streamId, promisedStreamId, headers, padding);
|
||||
requestLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGoAwayRead(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData)
|
||||
throws Http2Exception {
|
||||
serverListener.onGoAwayRead(ctx, lastStreamId, errorCode, copy(debugData));
|
||||
requestLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowUpdateRead(ChannelHandlerContext ctx, int streamId,
|
||||
int windowSizeIncrement) throws Http2Exception {
|
||||
serverListener.onWindowUpdateRead(ctx, streamId, windowSizeIncrement);
|
||||
requestLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnknownFrame(ChannelHandlerContext ctx, byte frameType, int streamId,
|
||||
Http2Flags flags, ByteBuf payload) {
|
||||
serverListener.onUnknownFrame(ctx, frameType, streamId, flags, payload);
|
||||
requestLatch.countDown();
|
||||
}
|
||||
|
||||
ByteBuf copy(ByteBuf buffer) {
|
||||
return Unpooled.copiedBuffer(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,6 @@
|
||||
package io.netty.handler.codec.http2;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||
@ -50,8 +49,7 @@ final class Http2TestUtil {
|
||||
});
|
||||
}
|
||||
|
||||
private Http2TestUtil() {
|
||||
}
|
||||
private Http2TestUtil() { }
|
||||
|
||||
static class FrameAdapter extends ByteToMessageDecoder {
|
||||
private final boolean copyBufs;
|
||||
@ -118,7 +116,7 @@ final class Http2TestUtil {
|
||||
public void onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding,
|
||||
boolean endOfStream) throws Http2Exception {
|
||||
Http2Stream stream = getOrCreateStream(streamId, endOfStream);
|
||||
listener.onDataRead(ctx, streamId, copyBufs ? copy(data) : data, padding, endOfStream);
|
||||
listener.onDataRead(ctx, streamId, copyBufs ? data.copy() : data, padding, endOfStream);
|
||||
if (endOfStream) {
|
||||
closeStream(stream, true);
|
||||
}
|
||||
@ -186,13 +184,13 @@ final class Http2TestUtil {
|
||||
|
||||
@Override
|
||||
public void onPingRead(ChannelHandlerContext ctx, ByteBuf data) throws Http2Exception {
|
||||
listener.onPingRead(ctx, copyBufs ? copy(data) : data);
|
||||
listener.onPingRead(ctx, copyBufs ? data.copy() : data);
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPingAckRead(ChannelHandlerContext ctx, ByteBuf data) throws Http2Exception {
|
||||
listener.onPingAckRead(ctx, copyBufs ? copy(data) : data);
|
||||
listener.onPingAckRead(ctx, copyBufs ? data.copy() : data);
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@ -207,7 +205,7 @@ final class Http2TestUtil {
|
||||
@Override
|
||||
public void onGoAwayRead(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData)
|
||||
throws Http2Exception {
|
||||
listener.onGoAwayRead(ctx, lastStreamId, errorCode, copyBufs ? copy(debugData) : debugData);
|
||||
listener.onGoAwayRead(ctx, lastStreamId, errorCode, copyBufs ? debugData.copy() : debugData);
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@ -227,9 +225,124 @@ final class Http2TestUtil {
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ByteBuf copy(ByteBuf buffer) {
|
||||
return Unpooled.copiedBuffer(buffer);
|
||||
/**
|
||||
* A decorator around a {@link Http2FrameListener} that counts down the latch so that we can await the completion of
|
||||
* the request.
|
||||
*/
|
||||
static class FrameCountDown implements Http2FrameListener {
|
||||
private final Http2FrameListener listener;
|
||||
private CountDownLatch messageLatch;
|
||||
private CountDownLatch dataLatch;
|
||||
|
||||
public FrameCountDown(Http2FrameListener listener, CountDownLatch messageLatch) {
|
||||
this(listener, messageLatch, null);
|
||||
}
|
||||
|
||||
public FrameCountDown(Http2FrameListener listener, CountDownLatch messageLatch, CountDownLatch dataLatch) {
|
||||
this.listener = listener;
|
||||
this.messageLatch = messageLatch;
|
||||
this.dataLatch = dataLatch;
|
||||
}
|
||||
|
||||
public void messageLatch(CountDownLatch latch) {
|
||||
messageLatch = latch;
|
||||
}
|
||||
|
||||
public void dataLatch(CountDownLatch latch) {
|
||||
dataLatch = latch;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding, boolean endOfStream)
|
||||
throws Http2Exception {
|
||||
listener.onDataRead(ctx, streamId, data.copy(), padding, endOfStream);
|
||||
messageLatch.countDown();
|
||||
if (dataLatch != null) {
|
||||
for (int i = 0; i < data.readableBytes(); ++i) {
|
||||
dataLatch.countDown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding,
|
||||
boolean endStream) throws Http2Exception {
|
||||
listener.onHeadersRead(ctx, streamId, headers, padding, endStream);
|
||||
messageLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHeadersRead(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int streamDependency,
|
||||
short weight, boolean exclusive, int padding, boolean endStream) throws Http2Exception {
|
||||
listener.onHeadersRead(ctx, streamId, headers, streamDependency, weight, exclusive, padding, endStream);
|
||||
messageLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPriorityRead(ChannelHandlerContext ctx, int streamId, int streamDependency, short weight,
|
||||
boolean exclusive) throws Http2Exception {
|
||||
listener.onPriorityRead(ctx, streamId, streamDependency, weight, exclusive);
|
||||
messageLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRstStreamRead(ChannelHandlerContext ctx, int streamId, long errorCode) throws Http2Exception {
|
||||
listener.onRstStreamRead(ctx, streamId, errorCode);
|
||||
messageLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSettingsAckRead(ChannelHandlerContext ctx) throws Http2Exception {
|
||||
listener.onSettingsAckRead(ctx);
|
||||
messageLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSettingsRead(ChannelHandlerContext ctx, Http2Settings settings) throws Http2Exception {
|
||||
listener.onSettingsRead(ctx, settings);
|
||||
messageLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPingRead(ChannelHandlerContext ctx, ByteBuf data) throws Http2Exception {
|
||||
listener.onPingRead(ctx, data.copy());
|
||||
messageLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPingAckRead(ChannelHandlerContext ctx, ByteBuf data) throws Http2Exception {
|
||||
listener.onPingAckRead(ctx, data.copy());
|
||||
messageLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPushPromiseRead(ChannelHandlerContext ctx, int streamId, int promisedStreamId,
|
||||
Http2Headers headers, int padding) throws Http2Exception {
|
||||
listener.onPushPromiseRead(ctx, streamId, promisedStreamId, headers, padding);
|
||||
messageLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGoAwayRead(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData)
|
||||
throws Http2Exception {
|
||||
listener.onGoAwayRead(ctx, lastStreamId, errorCode, debugData.copy());
|
||||
messageLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowUpdateRead(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement)
|
||||
throws Http2Exception {
|
||||
listener.onWindowUpdateRead(ctx, streamId, windowSizeIncrement);
|
||||
messageLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnknownFrame(ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags,
|
||||
ByteBuf payload) {
|
||||
listener.onUnknownFrame(ctx, frameType, streamId, flags, payload);
|
||||
messageLatch.countDown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user