/* * Copyright 2014 The Netty Project * * The Netty Project licenses this file to you under the Apache License, * version 2.0 (the "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package io.netty.handler.codec.spdy; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.Unpooled; import io.netty.util.ReferenceCountUtil; import org.junit.After; import org.junit.Before; import org.junit.Test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; public class SpdyHeaderBlockRawDecoderTest { private static final int maxHeaderSize = 16; private static final String name = "name"; private static final String value = "value"; private static final byte[] nameBytes = name.getBytes(); private static final byte[] valueBytes = value.getBytes(); private SpdyHeaderBlockRawDecoder decoder; private SpdyHeadersFrame frame; @Before public void setUp() { decoder = new SpdyHeaderBlockRawDecoder(SpdyVersion.SPDY_3_1, maxHeaderSize); frame = new DefaultSpdyHeadersFrame(1); } @After public void tearDown() { decoder.end(); } @Test public void testEmptyHeaderBlock() throws Exception { ByteBuf headerBlock = ReferenceCountUtil.releaseLater(Unpooled.EMPTY_BUFFER); decoder.decode(ByteBufAllocator.DEFAULT, headerBlock, frame); decoder.endHeaderBlock(frame); assertFalse(headerBlock.isReadable()); assertTrue(frame.isInvalid()); assertEquals(0, frame.headers().names().size()); } @Test public void testZeroNameValuePairs() throws Exception { ByteBuf headerBlock = ReferenceCountUtil.releaseLater(Unpooled.buffer(4)); headerBlock.writeInt(0); decoder.decode(ByteBufAllocator.DEFAULT, headerBlock, frame); decoder.endHeaderBlock(frame); assertFalse(headerBlock.isReadable()); assertFalse(frame.isInvalid()); assertEquals(0, frame.headers().names().size()); } @Test public void testNegativeNameValuePairs() throws Exception { ByteBuf headerBlock = ReferenceCountUtil.releaseLater(Unpooled.buffer(4)); headerBlock.writeInt(-1); decoder.decode(ByteBufAllocator.DEFAULT, headerBlock, frame); assertFalse(headerBlock.isReadable()); assertTrue(frame.isInvalid()); assertEquals(0, frame.headers().names().size()); } @Test public void testOneNameValuePair() throws Exception { ByteBuf headerBlock = ReferenceCountUtil.releaseLater(Unpooled.buffer(21)); headerBlock.writeInt(1); headerBlock.writeInt(4); headerBlock.writeBytes(nameBytes); headerBlock.writeInt(5); headerBlock.writeBytes(valueBytes); decoder.decode(ByteBufAllocator.DEFAULT, headerBlock, frame); decoder.endHeaderBlock(frame); assertFalse(headerBlock.isReadable()); assertFalse(frame.isInvalid()); assertEquals(1, frame.headers().names().size()); assertTrue(frame.headers().contains(name)); assertEquals(1, frame.headers().getAll(name).size()); assertEquals(value, frame.headers().get(name)); } @Test public void testMissingNameLength() throws Exception { ByteBuf headerBlock = ReferenceCountUtil.releaseLater(Unpooled.buffer(4)); headerBlock.writeInt(1); decoder.decode(ByteBufAllocator.DEFAULT, headerBlock, frame); decoder.endHeaderBlock(frame); assertFalse(headerBlock.isReadable()); assertTrue(frame.isInvalid()); assertEquals(0, frame.headers().names().size()); } @Test public void testZeroNameLength() throws Exception { ByteBuf headerBlock = ReferenceCountUtil.releaseLater(Unpooled.buffer(8)); headerBlock.writeInt(1); headerBlock.writeInt(0); decoder.decode(ByteBufAllocator.DEFAULT, headerBlock, frame); assertFalse(headerBlock.isReadable()); assertTrue(frame.isInvalid()); assertEquals(0, frame.headers().names().size()); } @Test public void testNegativeNameLength() throws Exception { ByteBuf headerBlock = ReferenceCountUtil.releaseLater(Unpooled.buffer(8)); headerBlock.writeInt(1); headerBlock.writeInt(-1); decoder.decode(ByteBufAllocator.DEFAULT, headerBlock, frame); assertFalse(headerBlock.isReadable()); assertTrue(frame.isInvalid()); assertEquals(0, frame.headers().names().size()); } @Test public void testMissingName() throws Exception { ByteBuf headerBlock = ReferenceCountUtil.releaseLater(Unpooled.buffer(8)); headerBlock.writeInt(1); headerBlock.writeInt(4); decoder.decode(ByteBufAllocator.DEFAULT, headerBlock, frame); decoder.endHeaderBlock(frame); assertFalse(headerBlock.isReadable()); assertTrue(frame.isInvalid()); assertEquals(0, frame.headers().names().size()); } @Test public void testIllegalNameOnlyNull() throws Exception { ByteBuf headerBlock = ReferenceCountUtil.releaseLater(Unpooled.buffer(18)); headerBlock.writeInt(1); headerBlock.writeInt(1); headerBlock.writeByte(0); headerBlock.writeInt(5); headerBlock.writeBytes(valueBytes); decoder.decode(ByteBufAllocator.DEFAULT, headerBlock, frame); assertFalse(headerBlock.isReadable()); assertTrue(frame.isInvalid()); assertEquals(0, frame.headers().names().size()); } @Test public void testMissingValueLength() throws Exception { ByteBuf headerBlock = ReferenceCountUtil.releaseLater(Unpooled.buffer(12)); headerBlock.writeInt(1); headerBlock.writeInt(4); headerBlock.writeBytes(nameBytes); decoder.decode(ByteBufAllocator.DEFAULT, headerBlock, frame); decoder.endHeaderBlock(frame); assertFalse(headerBlock.isReadable()); assertTrue(frame.isInvalid()); assertEquals(0, frame.headers().names().size()); } @Test public void testZeroValueLength() throws Exception { ByteBuf headerBlock = ReferenceCountUtil.releaseLater(Unpooled.buffer(16)); headerBlock.writeInt(1); headerBlock.writeInt(4); headerBlock.writeBytes(nameBytes); headerBlock.writeInt(0); decoder.decode(ByteBufAllocator.DEFAULT, headerBlock, frame); decoder.endHeaderBlock(frame); assertFalse(headerBlock.isReadable()); assertFalse(frame.isInvalid()); assertEquals(1, frame.headers().names().size()); assertTrue(frame.headers().contains(name)); assertEquals(1, frame.headers().getAll(name).size()); assertEquals("", frame.headers().get(name)); } @Test public void testNegativeValueLength() throws Exception { ByteBuf headerBlock = ReferenceCountUtil.releaseLater(Unpooled.buffer(16)); headerBlock.writeInt(1); headerBlock.writeInt(4); headerBlock.writeBytes(nameBytes); headerBlock.writeInt(-1); decoder.decode(ByteBufAllocator.DEFAULT, headerBlock, frame); assertFalse(headerBlock.isReadable()); assertTrue(frame.isInvalid()); assertEquals(0, frame.headers().names().size()); } @Test public void testMissingValue() throws Exception { ByteBuf headerBlock = ReferenceCountUtil.releaseLater(Unpooled.buffer(16)); headerBlock.writeInt(1); headerBlock.writeInt(4); headerBlock.writeBytes(nameBytes); headerBlock.writeInt(5); decoder.decode(ByteBufAllocator.DEFAULT, headerBlock, frame); decoder.endHeaderBlock(frame); assertFalse(headerBlock.isReadable()); assertTrue(frame.isInvalid()); assertEquals(0, frame.headers().names().size()); } @Test public void testIllegalValueOnlyNull() throws Exception { ByteBuf headerBlock = ReferenceCountUtil.releaseLater(Unpooled.buffer(17)); headerBlock.writeInt(1); headerBlock.writeInt(4); headerBlock.writeBytes(nameBytes); headerBlock.writeInt(1); headerBlock.writeByte(0); decoder.decode(ByteBufAllocator.DEFAULT, headerBlock, frame); assertFalse(headerBlock.isReadable()); assertTrue(frame.isInvalid()); assertEquals(0, frame.headers().names().size()); } @Test public void testIllegalValueStartsWithNull() throws Exception { ByteBuf headerBlock = ReferenceCountUtil.releaseLater(Unpooled.buffer(22)); headerBlock.writeInt(1); headerBlock.writeInt(4); headerBlock.writeBytes(nameBytes); headerBlock.writeInt(6); headerBlock.writeByte(0); headerBlock.writeBytes(valueBytes); decoder.decode(ByteBufAllocator.DEFAULT, headerBlock, frame); assertFalse(headerBlock.isReadable()); assertTrue(frame.isInvalid()); assertEquals(0, frame.headers().names().size()); } @Test public void testIllegalValueEndsWithNull() throws Exception { ByteBuf headerBlock = ReferenceCountUtil.releaseLater(Unpooled.buffer(22)); headerBlock.writeInt(1); headerBlock.writeInt(4); headerBlock.writeBytes(nameBytes); headerBlock.writeInt(6); headerBlock.writeBytes(valueBytes); headerBlock.writeByte(0); decoder.decode(ByteBufAllocator.DEFAULT, headerBlock, frame); assertFalse(headerBlock.isReadable()); assertTrue(frame.isInvalid()); assertEquals(0, frame.headers().names().size()); } @Test public void testMultipleValues() throws Exception { ByteBuf headerBlock = ReferenceCountUtil.releaseLater(Unpooled.buffer(27)); headerBlock.writeInt(1); headerBlock.writeInt(4); headerBlock.writeBytes(nameBytes); headerBlock.writeInt(11); headerBlock.writeBytes(valueBytes); headerBlock.writeByte(0); headerBlock.writeBytes(valueBytes); decoder.decode(ByteBufAllocator.DEFAULT, headerBlock, frame); decoder.endHeaderBlock(frame); assertFalse(headerBlock.isReadable()); assertFalse(frame.isInvalid()); assertEquals(1, frame.headers().names().size()); assertTrue(frame.headers().contains(name)); assertEquals(2, frame.headers().getAll(name).size()); assertEquals(value, frame.headers().getAll(name).get(0)); assertEquals(value, frame.headers().getAll(name).get(1)); } @Test public void testMultipleValuesEndsWithNull() throws Exception { ByteBuf headerBlock = ReferenceCountUtil.releaseLater(Unpooled.buffer(28)); headerBlock.writeInt(1); headerBlock.writeInt(4); headerBlock.writeBytes(nameBytes); headerBlock.writeInt(12); headerBlock.writeBytes(valueBytes); headerBlock.writeByte(0); headerBlock.writeBytes(valueBytes); headerBlock.writeByte(0); decoder.decode(ByteBufAllocator.DEFAULT, headerBlock, frame); assertFalse(headerBlock.isReadable()); assertTrue(frame.isInvalid()); assertEquals(1, frame.headers().names().size()); assertTrue(frame.headers().contains(name)); assertEquals(1, frame.headers().getAll(name).size()); assertEquals(value, frame.headers().get(name)); } @Test public void testIllegalValueMultipleNulls() throws Exception { ByteBuf headerBlock = ReferenceCountUtil.releaseLater(Unpooled.buffer(28)); headerBlock.writeInt(1); headerBlock.writeInt(4); headerBlock.writeBytes(nameBytes); headerBlock.writeInt(12); headerBlock.writeBytes(valueBytes); headerBlock.writeByte(0); headerBlock.writeByte(0); headerBlock.writeBytes(valueBytes); decoder.decode(ByteBufAllocator.DEFAULT, headerBlock, frame); decoder.endHeaderBlock(frame); assertFalse(headerBlock.isReadable()); assertTrue(frame.isInvalid()); assertEquals(0, frame.headers().names().size()); } @Test public void testMissingNextNameValuePair() throws Exception { ByteBuf headerBlock = ReferenceCountUtil.releaseLater(Unpooled.buffer(21)); headerBlock.writeInt(2); headerBlock.writeInt(4); headerBlock.writeBytes(nameBytes); headerBlock.writeInt(5); headerBlock.writeBytes(valueBytes); decoder.decode(ByteBufAllocator.DEFAULT, headerBlock, frame); decoder.endHeaderBlock(frame); assertFalse(headerBlock.isReadable()); assertTrue(frame.isInvalid()); assertEquals(1, frame.headers().names().size()); assertTrue(frame.headers().contains(name)); assertEquals(1, frame.headers().getAll(name).size()); assertEquals(value, frame.headers().get(name)); } @Test public void testMultipleNames() throws Exception { ByteBuf headerBlock = ReferenceCountUtil.releaseLater(Unpooled.buffer(38)); headerBlock.writeInt(2); headerBlock.writeInt(4); headerBlock.writeBytes(nameBytes); headerBlock.writeInt(5); headerBlock.writeBytes(valueBytes); headerBlock.writeInt(4); headerBlock.writeBytes(nameBytes); headerBlock.writeInt(5); headerBlock.writeBytes(valueBytes); decoder.decode(ByteBufAllocator.DEFAULT, headerBlock, frame); assertFalse(headerBlock.isReadable()); assertTrue(frame.isInvalid()); assertEquals(1, frame.headers().names().size()); assertTrue(frame.headers().contains(name)); assertEquals(1, frame.headers().getAll(name).size()); assertEquals(value, frame.headers().get(name)); } @Test public void testExtraData() throws Exception { ByteBuf headerBlock = ReferenceCountUtil.releaseLater(Unpooled.buffer(22)); headerBlock.writeInt(1); headerBlock.writeInt(4); headerBlock.writeBytes(nameBytes); headerBlock.writeInt(5); headerBlock.writeBytes(valueBytes); headerBlock.writeByte(0); decoder.decode(ByteBufAllocator.DEFAULT, headerBlock, frame); assertFalse(headerBlock.isReadable()); assertTrue(frame.isInvalid()); assertEquals(1, frame.headers().names().size()); assertTrue(frame.headers().contains(name)); assertEquals(1, frame.headers().getAll(name).size()); assertEquals(value, frame.headers().get(name)); } @Test public void testMultipleDecodes() throws Exception { ByteBuf headerBlock = ReferenceCountUtil.releaseLater(Unpooled.buffer(21)); headerBlock.writeInt(1); headerBlock.writeInt(4); headerBlock.writeBytes(nameBytes); headerBlock.writeInt(5); headerBlock.writeBytes(valueBytes); int readableBytes = headerBlock.readableBytes(); for (int i = 0; i < readableBytes; i++) { ByteBuf headerBlockSegment = headerBlock.slice(i, 1); decoder.decode(ByteBufAllocator.DEFAULT, headerBlockSegment, frame); assertFalse(headerBlockSegment.isReadable()); } decoder.endHeaderBlock(frame); assertFalse(frame.isInvalid()); assertEquals(1, frame.headers().names().size()); assertTrue(frame.headers().contains(name)); assertEquals(1, frame.headers().getAll(name).size()); assertEquals(value, frame.headers().get(name)); } @Test public void testContinueAfterInvalidHeaders() throws Exception { ByteBuf numHeaders = ReferenceCountUtil.releaseLater(Unpooled.buffer(4)); numHeaders.writeInt(1); ByteBuf nameBlock = ReferenceCountUtil.releaseLater(Unpooled.buffer(8)); nameBlock.writeInt(4); nameBlock.writeBytes(nameBytes); ByteBuf valueBlock = ReferenceCountUtil.releaseLater(Unpooled.buffer(9)); valueBlock.writeInt(5); valueBlock.writeBytes(valueBytes); decoder.decode(ByteBufAllocator.DEFAULT, numHeaders, frame); decoder.decode(ByteBufAllocator.DEFAULT, nameBlock, frame); frame.setInvalid(); decoder.decode(ByteBufAllocator.DEFAULT, valueBlock, frame); decoder.endHeaderBlock(frame); assertFalse(numHeaders.isReadable()); assertFalse(nameBlock.isReadable()); assertFalse(valueBlock.isReadable()); assertEquals(1, frame.headers().names().size()); assertTrue(frame.headers().contains(name)); assertEquals(1, frame.headers().getAll(name).size()); assertEquals(value, frame.headers().get(name)); } @Test public void testTruncatedHeaderName() throws Exception { ByteBuf headerBlock = ReferenceCountUtil.releaseLater(Unpooled.buffer(maxHeaderSize + 18)); headerBlock.writeInt(1); headerBlock.writeInt(maxHeaderSize + 1); for (int i = 0; i < maxHeaderSize + 1; i++) { headerBlock.writeByte('a'); } headerBlock.writeInt(5); headerBlock.writeBytes(valueBytes); decoder.decode(ByteBufAllocator.DEFAULT, headerBlock, frame); decoder.endHeaderBlock(frame); assertFalse(headerBlock.isReadable()); assertTrue(frame.isTruncated()); assertFalse(frame.isInvalid()); assertEquals(0, frame.headers().names().size()); } @Test public void testTruncatedHeaderValue() throws Exception { ByteBuf headerBlock = ReferenceCountUtil.releaseLater(Unpooled.buffer(maxHeaderSize + 13)); headerBlock.writeInt(1); headerBlock.writeInt(4); headerBlock.writeBytes(nameBytes); headerBlock.writeInt(13); for (int i = 0; i < maxHeaderSize - 3; i++) { headerBlock.writeByte('a'); } decoder.decode(ByteBufAllocator.DEFAULT, headerBlock, frame); decoder.endHeaderBlock(frame); assertFalse(headerBlock.isReadable()); assertTrue(frame.isTruncated()); assertFalse(frame.isInvalid()); assertEquals(0, frame.headers().names().size()); } }