From 72916b99606b3ddb71ee5a8ace7e4914dd81efec Mon Sep 17 00:00:00 2001 From: chhsiao90 Date: Tue, 7 Feb 2017 15:10:48 +0800 Subject: [PATCH] Add unit test on DefaultHttp2FrameReader Motivation: DefaultHttp2FrameReader contains the logic for how it parsed the network traffic from http2 client, it also validate the content is legal or not. So keep high coverage rate on it will increase the stability of api. Modifications: Add unit test on DefaultHttp2FrameReader Result: Coverage rate increased --- .../http2/DefaultHttp2FrameReaderTest.java | 258 +++++++++++++++++- 1 file changed, 251 insertions(+), 7 deletions(-) diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2FrameReaderTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2FrameReaderTest.java index 8392ce9d3a..e4da48fd48 100644 --- a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2FrameReaderTest.java +++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2FrameReaderTest.java @@ -25,11 +25,10 @@ import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import static io.netty.handler.codec.http2.Http2FrameTypes.CONTINUATION; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static io.netty.handler.codec.http2.Http2CodecUtil.writeFrameHeader; -import static io.netty.handler.codec.http2.Http2FrameTypes.HEADERS; +import static io.netty.handler.codec.http2.Http2CodecUtil.*; +import static io.netty.handler.codec.http2.Http2FrameTypes.*; +import static org.mockito.Mockito.*; + /** * Tests for {@link DefaultHttp2FrameReader}. @@ -50,8 +49,6 @@ public class DefaultHttp2FrameReaderTest { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - // Currently, frameReader only used alloc method from ChannelHandlerContext to allocate buffer, - // and used it as a parameter when calling listener when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT); frameReader = new DefaultHttp2FrameReader(); @@ -108,6 +105,25 @@ public class DefaultHttp2FrameReaderTest { } } + @Test + public void readUnknownFrame() throws Http2Exception { + ByteBuf input = Unpooled.buffer(); + ByteBuf payload = Unpooled.buffer(); + try { + payload.writeByte(1); + + writeFrameHeader(input, payload.readableBytes(), (byte) 0xff, new Http2Flags(), 0); + input.writeBytes(payload); + frameReader.readFrame(ctx, input, listener); + + verify(listener).onUnknownFrame( + ctx, (byte) 0xff, 0, new Http2Flags(), payload.slice(0, 1)); + } finally { + payload.release(); + input.release(); + } + } + @Test(expected = Http2Exception.class) public void failedWhenUnknownFrameInMiddleOfHeaderBlock() throws Http2Exception { final int streamId = 1; @@ -128,6 +144,195 @@ public class DefaultHttp2FrameReaderTest { } } + @Test(expected = Http2Exception.class) + public void failedWhenContinuationFrameStreamIdMismatch() throws Http2Exception { + ByteBuf input = Unpooled.buffer(); + try { + Http2Headers headers = new DefaultHttp2Headers() + .authority("foo") + .method("get") + .path("/") + .scheme("https"); + writeHeaderFrame(input, 1, headers, + new Http2Flags().endOfHeaders(false).endOfStream(true)); + writeContinuationFrame(input, 3, new DefaultHttp2Headers().add("foo", "bar"), + new Http2Flags().endOfHeaders(true)); + frameReader.readFrame(ctx, input, listener); + } finally { + input.release(); + } + } + + @Test(expected = Http2Exception.class) + public void failedWhenContinuationFrameNotFollowHeaderFrame() throws Http2Exception { + ByteBuf input = Unpooled.buffer(); + try { + writeContinuationFrame(input, 1, new DefaultHttp2Headers().add("foo", "bar"), + new Http2Flags().endOfHeaders(true)); + frameReader.readFrame(ctx, input, listener); + } finally { + input.release(); + } + } + + @Test(expected = Http2Exception.class) + public void failedWhenHeaderFrameDependsOnItself() throws Http2Exception { + ByteBuf input = Unpooled.buffer(); + try { + Http2Headers headers = new DefaultHttp2Headers() + .authority("foo") + .method("get") + .path("/") + .scheme("https"); + writeHeaderFramePriorityPresent( + input, 1, headers, + new Http2Flags().endOfHeaders(true).endOfStream(true).priorityPresent(true), + 1, 10); + frameReader.readFrame(ctx, input, listener); + } finally { + input.release(); + } + } + + @Test + public void readHeaderAndData() throws Http2Exception { + ByteBuf input = Unpooled.buffer(); + ByteBuf dataPayload = Unpooled.buffer(); + try { + Http2Headers headers = new DefaultHttp2Headers() + .authority("foo") + .method("get") + .path("/") + .scheme("https"); + dataPayload.writeByte(1); + writeHeaderFrameWithData(input, 1, headers, dataPayload); + + frameReader.readFrame(ctx, input, listener); + + verify(listener).onHeadersRead(ctx, 1, headers, 0, false); + verify(listener).onDataRead(ctx, 1, dataPayload.slice(0, 1), 0, true); + } finally { + input.release(); + dataPayload.release(); + } + } + + @Test(expected = Http2Exception.class) + public void failedWhenDataFrameNotAssociateWithStream() throws Http2Exception { + ByteBuf input = Unpooled.buffer(); + ByteBuf payload = Unpooled.buffer(); + try { + payload.writeByte(1); + + writeFrameHeader(input, payload.readableBytes(), DATA, new Http2Flags().endOfStream(true), 0); + input.writeBytes(payload); + frameReader.readFrame(ctx, input, listener); + } finally { + payload.release(); + input.release(); + } + } + + @Test + public void readPriorityFrame() throws Http2Exception { + ByteBuf input = Unpooled.buffer(); + try { + writePriorityFrame(input, 1, 0, 10); + frameReader.readFrame(ctx, input, listener); + } finally { + input.release(); + } + } + + @Test(expected = Http2Exception.class) + public void failedWhenPriorityFrameDependsOnItself() throws Http2Exception { + ByteBuf input = Unpooled.buffer(); + try { + writePriorityFrame(input, 1, 1, 10); + frameReader.readFrame(ctx, input, listener); + } finally { + input.release(); + } + } + + @Test(expected = Http2Exception.class) + public void failedWhenWindowUpdateFrameWithZeroDelta() throws Http2Exception { + ByteBuf input = Unpooled.buffer(); + try { + writeFrameHeader(input, 4, WINDOW_UPDATE, new Http2Flags(), 0); + input.writeInt(0); + frameReader.readFrame(ctx, input, listener); + } finally { + input.release(); + } + } + + @Test + public void readSettingsFrame() throws Http2Exception { + ByteBuf input = Unpooled.buffer(); + try { + writeFrameHeader(input, 6, SETTINGS, new Http2Flags(), 0); + input.writeShort(SETTINGS_MAX_HEADER_LIST_SIZE); + input.writeInt(1024); + frameReader.readFrame(ctx, input, listener); + + listener.onSettingsRead(ctx, new Http2Settings().maxHeaderListSize(1024)); + } finally { + input.release(); + } + } + + @Test + public void readAckSettingsFrame() throws Http2Exception { + ByteBuf input = Unpooled.buffer(); + try { + writeFrameHeader(input, 0, SETTINGS, new Http2Flags().ack(true), 0); + frameReader.readFrame(ctx, input, listener); + + listener.onSettingsAckRead(ctx); + } finally { + input.release(); + } + } + + @Test(expected = Http2Exception.class) + public void failedWhenSettingsFrameOnNonZeroStream() throws Http2Exception { + ByteBuf input = Unpooled.buffer(); + try { + writeFrameHeader(input, 6, SETTINGS, new Http2Flags(), 1); + input.writeShort(SETTINGS_MAX_HEADER_LIST_SIZE); + input.writeInt(1024); + frameReader.readFrame(ctx, input, listener); + } finally { + input.release(); + } + } + + @Test(expected = Http2Exception.class) + public void failedWhenAckSettingsFrameWithPayload() throws Http2Exception { + ByteBuf input = Unpooled.buffer(); + try { + writeFrameHeader(input, 1, SETTINGS, new Http2Flags().ack(true), 0); + input.writeByte(1); + frameReader.readFrame(ctx, input, listener); + } finally { + input.release(); + } + } + + @Test(expected = Http2Exception.class) + public void failedWhenSettingsFrameWithWrongPayloadLength() throws Http2Exception { + ByteBuf input = Unpooled.buffer(); + try { + writeFrameHeader(input, 8, SETTINGS, new Http2Flags(), 0); + input.writeInt(SETTINGS_MAX_HEADER_LIST_SIZE); + input.writeInt(1024); + frameReader.readFrame(ctx, input, listener); + } finally { + input.release(); + } + } + private void writeHeaderFrame( ByteBuf output, int streamId, Http2Headers headers, Http2Flags flags) throws Http2Exception { @@ -141,6 +346,38 @@ public class DefaultHttp2FrameReaderTest { } } + private void writeHeaderFrameWithData( + ByteBuf output, int streamId, Http2Headers headers, + ByteBuf dataPayload) throws Http2Exception { + ByteBuf headerBlock = Unpooled.buffer(); + try { + encoder.encodeHeaders(streamId, headerBlock, headers, Http2HeadersEncoder.NEVER_SENSITIVE); + writeFrameHeader(output, headerBlock.readableBytes(), HEADERS, + new Http2Flags().endOfHeaders(true), streamId); + output.writeBytes(headerBlock, headerBlock.readableBytes()); + + writeFrameHeader(output, dataPayload.readableBytes(), DATA, new Http2Flags().endOfStream(true), streamId); + output.writeBytes(dataPayload); + } finally { + headerBlock.release(); + } + } + + private void writeHeaderFramePriorityPresent( + ByteBuf output, int streamId, Http2Headers headers, + Http2Flags flags, int streamDependency, int weight) throws Http2Exception { + ByteBuf headerBlock = Unpooled.buffer(); + try { + writeUnsignedInt(streamDependency, headerBlock); + headerBlock.writeByte(weight - 1); + encoder.encodeHeaders(streamId, headerBlock, headers, Http2HeadersEncoder.NEVER_SENSITIVE); + writeFrameHeader(output, headerBlock.readableBytes(), HEADERS, flags, streamId); + output.writeBytes(headerBlock, headerBlock.readableBytes()); + } finally { + headerBlock.release(); + } + } + private void writeContinuationFrame( ByteBuf output, int streamId, Http2Headers headers, Http2Flags flags) throws Http2Exception { @@ -153,4 +390,11 @@ public class DefaultHttp2FrameReaderTest { headerBlock.release(); } } + + private void writePriorityFrame( + ByteBuf output, int streamId, int streamDependency, int weight) { + writeFrameHeader(output, 5, PRIORITY, new Http2Flags(), streamId); + writeUnsignedInt(streamDependency, output); + output.writeByte(weight - 1); + } }