/*
* 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.Unpooled;
import io.netty.util.ReferenceCountUtil;
import org.junit.Before;
import org.junit.Test;
import java.util.Random;
import static io.netty.handler.codec.spdy.SpdyCodecUtil.SPDY_HEADER_SIZE;
import static org.easymock.EasyMock.anyObject;
import static org.easymock.EasyMock.createStrictMock;
import static org.easymock.EasyMock.expectLastCall;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import static org.junit.Assert.*;
public class SpdyFrameDecoderTest {
private static final Random RANDOM = new Random();
private final SpdyFrameDecoderDelegate delegate = createStrictMock(SpdyFrameDecoderDelegate.class);
private SpdyFrameDecoder decoder;
@Before
public void createDecoder() {
decoder = new SpdyFrameDecoder(SpdyVersion.SPDY_3_1, new SpdyFrameDecoderDelegate() {
@Override
public void readDataFrame(int streamId, boolean last, ByteBuf data) {
try {
delegate.readDataFrame(streamId, last, data);
} finally {
// release the data after we delegate it and so checked it.
data.release();
}
}
@Override
public void readSynStreamFrame(int streamId, int associatedToStreamId,
byte priority, boolean last, boolean unidirectional) {
delegate.readSynStreamFrame(streamId, associatedToStreamId, priority, last, unidirectional);
}
@Override
public void readSynReplyFrame(int streamId, boolean last) {
delegate.readSynReplyFrame(streamId, last);
}
@Override
public void readRstStreamFrame(int streamId, int statusCode) {
delegate.readRstStreamFrame(streamId, statusCode);
}
@Override
public void readSettingsFrame(boolean clearPersisted) {
delegate.readSettingsFrame(clearPersisted);
}
@Override
public void readSetting(int id, int value, boolean persistValue, boolean persisted) {
delegate.readSetting(id, value, persistValue, persisted);
}
@Override
public void readSettingsEnd() {
delegate.readSettingsEnd();
}
@Override
public void readPingFrame(int id) {
delegate.readPingFrame(id);
}
@Override
public void readGoAwayFrame(int lastGoodStreamId, int statusCode) {
delegate.readGoAwayFrame(lastGoodStreamId, statusCode);
}
@Override
public void readHeadersFrame(int streamId, boolean last) {
delegate.readHeadersFrame(streamId, last);
}
@Override
public void readWindowUpdateFrame(int streamId, int deltaWindowSize) {
delegate.readWindowUpdateFrame(streamId, deltaWindowSize);
}
@Override
public void readHeaderBlock(ByteBuf headerBlock) {
try {
delegate.readHeaderBlock(headerBlock);
} finally {
// release the data after we delegate it and so checked it.
headerBlock.release();
}
}
@Override
public void readHeaderBlockEnd() {
delegate.readHeaderBlockEnd();
}
@Override
public void readFrameError(String message) {
delegate.readFrameError(message);
}
});
}
private static void encodeDataFrameHeader(ByteBuf buffer, int streamId, byte flags, int length) {
buffer.writeInt(streamId & 0x7FFFFFFF);
buffer.writeByte(flags);
buffer.writeMedium(length);
}
private static void encodeControlFrameHeader(ByteBuf buffer, short type, byte flags, int length) {
buffer.writeShort(0x8000 | SpdyVersion.SPDY_3_1.getVersion());
buffer.writeShort(type);
buffer.writeByte(flags);
buffer.writeMedium(length);
}
@Test
public void testSpdyDataFrame() throws Exception {
int streamId = RANDOM.nextInt() & 0x7FFFFFFF | 0x01;
byte flags = 0;
int length = 1024;
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeDataFrameHeader(buf, streamId, flags, length);
for (int i = 0; i < 256; i ++) {
buf.writeInt(RANDOM.nextInt());
}
delegate.readDataFrame(streamId, false, buf.slice(SPDY_HEADER_SIZE, length));
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testEmptySpdyDataFrame() throws Exception {
int streamId = RANDOM.nextInt() & 0x7FFFFFFF | 0x01;
byte flags = 0;
int length = 0;
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeDataFrameHeader(buf, streamId, flags, length);
delegate.readDataFrame(streamId, false, Unpooled.EMPTY_BUFFER);
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testLastSpdyDataFrame() throws Exception {
int streamId = RANDOM.nextInt() & 0x7FFFFFFF | 0x01;
byte flags = 0x01; // FLAG_FIN
int length = 0;
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeDataFrameHeader(buf, streamId, flags, length);
delegate.readDataFrame(streamId, true, Unpooled.EMPTY_BUFFER);
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testUnknownSpdyDataFrameFlags() throws Exception {
int streamId = RANDOM.nextInt() & 0x7FFFFFFF | 0x01;
byte flags = (byte) 0xFE; // should ignore any unknown flags
int length = 0;
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeDataFrameHeader(buf, streamId, flags, length);
delegate.readDataFrame(streamId, false, Unpooled.EMPTY_BUFFER);
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testIllegalSpdyDataFrameStreamId() throws Exception {
int streamId = 0; // illegal stream identifier
byte flags = 0;
int length = 0;
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeDataFrameHeader(buf, streamId, flags, length);
delegate.readFrameError((String) anyObject());
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testPipelinedSpdyDataFrames() throws Exception {
int streamId1 = RANDOM.nextInt() & 0x7FFFFFFF | 0x01;
int streamId2 = RANDOM.nextInt() & 0x7FFFFFFF | 0x01;
byte flags = 0;
int length = 0;
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(2 * (SPDY_HEADER_SIZE + length)));
encodeDataFrameHeader(buf, streamId1, flags, length);
encodeDataFrameHeader(buf, streamId2, flags, length);
delegate.readDataFrame(streamId1, false, Unpooled.EMPTY_BUFFER);
delegate.readDataFrame(streamId2, false, Unpooled.EMPTY_BUFFER);
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testSpdySynStreamFrame() throws Exception {
short type = 1;
byte flags = 0;
int length = 10;
int streamId = RANDOM.nextInt() & 0x7FFFFFFF | 0x01;
int associatedToStreamId = RANDOM.nextInt() & 0x7FFFFFFF;
byte priority = (byte) (RANDOM.nextInt() & 0x07);
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(streamId);
buf.writeInt(associatedToStreamId);
buf.writeByte(priority << 5);
buf.writeByte(0);
delegate.readSynStreamFrame(streamId, associatedToStreamId, priority, false, false);
delegate.readHeaderBlockEnd();
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testLastSpdySynStreamFrame() throws Exception {
short type = 1;
byte flags = 0x01; // FLAG_FIN
int length = 10;
int streamId = RANDOM.nextInt() & 0x7FFFFFFF | 0x01;
int associatedToStreamId = RANDOM.nextInt() & 0x7FFFFFFF;
byte priority = (byte) (RANDOM.nextInt() & 0x07);
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(streamId);
buf.writeInt(associatedToStreamId);
buf.writeByte(priority << 5);
buf.writeByte(0);
delegate.readSynStreamFrame(streamId, associatedToStreamId, priority, true, false);
delegate.readHeaderBlockEnd();
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testUnidirectionalSpdySynStreamFrame() throws Exception {
short type = 1;
byte flags = 0x02; // FLAG_UNIDIRECTIONAL
int length = 10;
int streamId = RANDOM.nextInt() & 0x7FFFFFFF | 0x01;
int associatedToStreamId = RANDOM.nextInt() & 0x7FFFFFFF;
byte priority = (byte) (RANDOM.nextInt() & 0x07);
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(streamId);
buf.writeInt(associatedToStreamId);
buf.writeByte(priority << 5);
buf.writeByte(0);
delegate.readSynStreamFrame(streamId, associatedToStreamId, priority, false, true);
delegate.readHeaderBlockEnd();
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testIndependentSpdySynStreamFrame() throws Exception {
short type = 1;
byte flags = 0;
int length = 10;
int streamId = RANDOM.nextInt() & 0x7FFFFFFF | 0x01;
int associatedToStreamId = 0; // independent of all other streams
byte priority = (byte) (RANDOM.nextInt() & 0x07);
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(streamId);
buf.writeInt(associatedToStreamId);
buf.writeByte(priority << 5);
buf.writeByte(0);
delegate.readSynStreamFrame(streamId, associatedToStreamId, priority, false, false);
delegate.readHeaderBlockEnd();
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testUnknownSpdySynStreamFrameFlags() throws Exception {
short type = 1;
byte flags = (byte) 0xFC; // undefined flags
int length = 10;
int streamId = RANDOM.nextInt() & 0x7FFFFFFF | 0x01;
int associatedToStreamId = RANDOM.nextInt() & 0x7FFFFFFF;
byte priority = (byte) (RANDOM.nextInt() & 0x07);
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(streamId);
buf.writeInt(associatedToStreamId);
buf.writeByte(priority << 5);
buf.writeByte(0);
delegate.readSynStreamFrame(streamId, associatedToStreamId, priority, false, false);
delegate.readHeaderBlockEnd();
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testReservedSpdySynStreamFrameBits() throws Exception {
short type = 1;
byte flags = 0;
int length = 10;
int streamId = RANDOM.nextInt() & 0x7FFFFFFF | 0x01;
int associatedToStreamId = RANDOM.nextInt() & 0x7FFFFFFF;
byte priority = (byte) (RANDOM.nextInt() & 0x07);
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(streamId | 0x80000000); // should ignore reserved bit
buf.writeInt(associatedToStreamId | 0x80000000); // should ignore reserved bit
buf.writeByte(priority << 5 | 0x1F); // should ignore reserved bits
buf.writeByte(0xFF); // should ignore reserved bits
delegate.readSynStreamFrame(streamId, associatedToStreamId, priority, false, false);
delegate.readHeaderBlockEnd();
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testInvalidSpdySynStreamFrameLength() throws Exception {
short type = 1;
byte flags = 0;
int length = 8; // invalid length
int streamId = RANDOM.nextInt() & 0x7FFFFFFF | 0x01;
int associatedToStreamId = RANDOM.nextInt() & 0x7FFFFFFF;
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(streamId);
buf.writeInt(associatedToStreamId);
delegate.readFrameError((String) anyObject());
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testIllegalSpdySynStreamFrameStreamId() throws Exception {
short type = 1;
byte flags = 0;
int length = 10;
int streamId = 0; // invalid stream identifier
int associatedToStreamId = RANDOM.nextInt() & 0x7FFFFFFF;
byte priority = (byte) (RANDOM.nextInt() & 0x07);
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(streamId);
buf.writeInt(associatedToStreamId);
buf.writeByte(priority << 5);
buf.writeByte(0);
delegate.readFrameError((String) anyObject());
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testSpdySynStreamFrameHeaderBlock() throws Exception {
short type = 1;
byte flags = 0;
int length = 10;
int headerBlockLength = 1024;
int streamId = RANDOM.nextInt() & 0x7FFFFFFF | 0x01;
int associatedToStreamId = RANDOM.nextInt() & 0x7FFFFFFF;
byte priority = (byte) (RANDOM.nextInt() & 0x07);
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length + headerBlockLength));
encodeControlFrameHeader(buf, type, flags, length + headerBlockLength);
buf.writeInt(streamId);
buf.writeInt(associatedToStreamId);
buf.writeByte(priority << 5);
buf.writeByte(0);
ByteBuf headerBlock = ReferenceCountUtil.releaseLater(Unpooled.buffer(headerBlockLength));
for (int i = 0; i < 256; i ++) {
headerBlock.writeInt(RANDOM.nextInt());
}
delegate.readSynStreamFrame(streamId, associatedToStreamId, priority, false, false);
delegate.readHeaderBlock(headerBlock.duplicate());
delegate.readHeaderBlockEnd();
replay(delegate);
decoder.decode(buf);
decoder.decode(headerBlock);
verify(delegate);
assertFalse(buf.isReadable());
assertFalse(headerBlock.isReadable());
}
@Test
public void testSpdySynReplyFrame() throws Exception {
short type = 2;
byte flags = 0;
int length = 4;
int streamId = RANDOM.nextInt() & 0x7FFFFFFF | 0x01;
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(streamId);
delegate.readSynReplyFrame(streamId, false);
delegate.readHeaderBlockEnd();
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testLastSpdySynReplyFrame() throws Exception {
short type = 2;
byte flags = 0x01; // FLAG_FIN
int length = 4;
int streamId = RANDOM.nextInt() & 0x7FFFFFFF | 0x01;
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(streamId);
delegate.readSynReplyFrame(streamId, true);
delegate.readHeaderBlockEnd();
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testUnknownSpdySynReplyFrameFlags() throws Exception {
short type = 2;
byte flags = (byte) 0xFE; // undefined flags
int length = 4;
int streamId = RANDOM.nextInt() & 0x7FFFFFFF | 0x01;
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(streamId);
delegate.readSynReplyFrame(streamId, false);
delegate.readHeaderBlockEnd();
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testReservedSpdySynReplyFrameBits() throws Exception {
short type = 2;
byte flags = 0;
int length = 4;
int streamId = RANDOM.nextInt() & 0x7FFFFFFF | 0x01;
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(streamId | 0x80000000); // should ignore reserved bit
delegate.readSynReplyFrame(streamId, false);
delegate.readHeaderBlockEnd();
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testInvalidSpdySynReplyFrameLength() throws Exception {
short type = 2;
byte flags = 0;
int length = 0; // invalid length
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
delegate.readFrameError((String) anyObject());
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testIllegalSpdySynReplyFrameStreamId() throws Exception {
short type = 2;
byte flags = 0;
int length = 4;
int streamId = 0; // invalid stream identifier
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(streamId);
delegate.readFrameError((String) anyObject());
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testSpdySynReplyFrameHeaderBlock() throws Exception {
short type = 2;
byte flags = 0;
int length = 4;
int headerBlockLength = 1024;
int streamId = RANDOM.nextInt() & 0x7FFFFFFF | 0x01;
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length + headerBlockLength));
encodeControlFrameHeader(buf, type, flags, length + headerBlockLength);
buf.writeInt(streamId);
ByteBuf headerBlock = Unpooled.buffer(headerBlockLength);
for (int i = 0; i < 256; i ++) {
headerBlock.writeInt(RANDOM.nextInt());
}
delegate.readSynReplyFrame(streamId, false);
delegate.readHeaderBlock(headerBlock.duplicate());
delegate.readHeaderBlockEnd();
replay(delegate);
decoder.decode(buf);
decoder.decode(headerBlock);
verify(delegate);
assertFalse(buf.isReadable());
assertFalse(headerBlock.isReadable());
}
@Test
public void testSpdyRstStreamFrame() throws Exception {
short type = 3;
byte flags = 0;
int length = 8;
int streamId = RANDOM.nextInt() & 0x7FFFFFFF | 0x01;
int statusCode = RANDOM.nextInt() | 0x01;
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(streamId);
buf.writeInt(statusCode);
delegate.readRstStreamFrame(streamId, statusCode);
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testReservedSpdyRstStreamFrameBits() throws Exception {
short type = 3;
byte flags = 0;
int length = 8;
int streamId = RANDOM.nextInt() & 0x7FFFFFFF | 0x01;
int statusCode = RANDOM.nextInt() | 0x01;
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(streamId | 0x80000000); // should ignore reserved bit
buf.writeInt(statusCode);
delegate.readRstStreamFrame(streamId, statusCode);
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testInvalidSpdyRstStreamFrameFlags() throws Exception {
short type = 3;
byte flags = (byte) 0xFF; // invalid flags
int length = 8;
int streamId = RANDOM.nextInt() & 0x7FFFFFFF | 0x01;
int statusCode = RANDOM.nextInt() | 0x01;
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(streamId);
buf.writeInt(statusCode);
delegate.readFrameError((String) anyObject());
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testInvalidSpdyRstStreamFrameLength() throws Exception {
short type = 3;
byte flags = 0;
int length = 12; // invalid length
int streamId = RANDOM.nextInt() & 0x7FFFFFFF | 0x01;
int statusCode = RANDOM.nextInt() | 0x01;
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(streamId);
buf.writeInt(statusCode);
delegate.readFrameError((String) anyObject());
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testIllegalSpdyRstStreamFrameStreamId() throws Exception {
short type = 3;
byte flags = 0;
int length = 8;
int streamId = 0; // invalid stream identifier
int statusCode = RANDOM.nextInt() | 0x01;
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(streamId);
buf.writeInt(statusCode);
delegate.readFrameError((String) anyObject());
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testIllegalSpdyRstStreamFrameStatusCode() throws Exception {
short type = 3;
byte flags = 0;
int length = 8;
int streamId = RANDOM.nextInt() & 0x7FFFFFFF | 0x01;
int statusCode = 0; // invalid status code
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(streamId);
buf.writeInt(statusCode);
delegate.readFrameError((String) anyObject());
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testSpdySettingsFrame() throws Exception {
short type = 4;
byte flags = 0;
int numSettings = 2;
int length = 8 * numSettings + 4;
byte idFlags = 0;
int id = RANDOM.nextInt() & 0x00FFFFFF;
int value = RANDOM.nextInt();
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(numSettings);
for (int i = 0; i < numSettings; i++) {
buf.writeByte(idFlags);
buf.writeMedium(id);
buf.writeInt(value);
}
delegate.readSettingsFrame(false);
delegate.readSetting(id, value, false, false);
expectLastCall().times(numSettings);
delegate.readSettingsEnd();
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testEmptySpdySettingsFrame() throws Exception {
short type = 4;
byte flags = 0;
int numSettings = 0;
int length = 8 * numSettings + 4;
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(numSettings);
delegate.readSettingsFrame(false);
delegate.readSettingsEnd();
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testSpdySettingsFrameClearFlag() throws Exception {
short type = 4;
byte flags = 0x01; // FLAG_SETTINGS_CLEAR_SETTINGS
int numSettings = 0;
int length = 8 * numSettings + 4;
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(numSettings);
delegate.readSettingsFrame(true);
delegate.readSettingsEnd();
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testSpdySettingsPersistValues() throws Exception {
short type = 4;
byte flags = 0;
int numSettings = 1;
int length = 8 * numSettings + 4;
byte idFlags = 0x01; // FLAG_SETTINGS_PERSIST_VALUE
int id = RANDOM.nextInt() & 0x00FFFFFF;
int value = RANDOM.nextInt();
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(numSettings);
for (int i = 0; i < numSettings; i++) {
buf.writeByte(idFlags);
buf.writeMedium(id);
buf.writeInt(value);
}
delegate.readSettingsFrame(false);
delegate.readSetting(id, value, true, false);
expectLastCall().times(numSettings);
delegate.readSettingsEnd();
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testSpdySettingsPersistedValues() throws Exception {
short type = 4;
byte flags = 0;
int numSettings = 1;
int length = 8 * numSettings + 4;
byte idFlags = 0x02; // FLAG_SETTINGS_PERSISTED
int id = RANDOM.nextInt() & 0x00FFFFFF;
int value = RANDOM.nextInt();
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(numSettings);
for (int i = 0; i < numSettings; i++) {
buf.writeByte(idFlags);
buf.writeMedium(id);
buf.writeInt(value);
}
delegate.readSettingsFrame(false);
delegate.readSetting(id, value, false, true);
expectLastCall().times(numSettings);
delegate.readSettingsEnd();
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testUnknownSpdySettingsFrameFlags() throws Exception {
short type = 4;
byte flags = (byte) 0xFE; // undefined flags
int numSettings = 0;
int length = 8 * numSettings + 4;
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(numSettings);
delegate.readSettingsFrame(false);
delegate.readSettingsEnd();
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testUnknownSpdySettingsFlags() throws Exception {
short type = 4;
byte flags = 0;
int numSettings = 1;
int length = 8 * numSettings + 4;
byte idFlags = (byte) 0xFC; // undefined flags
int id = RANDOM.nextInt() & 0x00FFFFFF;
int value = RANDOM.nextInt();
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(numSettings);
for (int i = 0; i < numSettings; i++) {
buf.writeByte(idFlags);
buf.writeMedium(id);
buf.writeInt(value);
}
delegate.readSettingsFrame(false);
delegate.readSetting(id, value, false, false);
expectLastCall().times(numSettings);
delegate.readSettingsEnd();
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testInvalidSpdySettingsFrameLength() throws Exception {
short type = 4;
byte flags = 0;
int numSettings = 2;
int length = 8 * numSettings + 8; // invalid length
byte idFlags = 0;
int id = RANDOM.nextInt() & 0x00FFFFFF;
int value = RANDOM.nextInt();
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(numSettings);
for (int i = 0; i < numSettings; i++) {
buf.writeByte(idFlags);
buf.writeMedium(id);
buf.writeInt(value);
}
delegate.readFrameError((String) anyObject());
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testInvalidSpdySettingsFrameNumSettings() throws Exception {
short type = 4;
byte flags = 0;
int numSettings = 2;
int length = 8 * numSettings + 4;
byte idFlags = 0;
int id = RANDOM.nextInt() & 0x00FFFFFF;
int value = RANDOM.nextInt();
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(0); // invalid num_settings
for (int i = 0; i < numSettings; i++) {
buf.writeByte(idFlags);
buf.writeMedium(id);
buf.writeInt(value);
}
delegate.readFrameError((String) anyObject());
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testDiscardUnknownFrame() throws Exception {
short type = 5;
byte flags = (byte) 0xFF;
int length = 8;
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeLong(RANDOM.nextLong());
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testDiscardUnknownEmptyFrame() throws Exception {
short type = 5;
byte flags = (byte) 0xFF;
int length = 0;
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testProgressivelyDiscardUnknownEmptyFrame() throws Exception {
short type = 5;
byte flags = (byte) 0xFF;
int segment = 4;
int length = 2 * segment;
ByteBuf header = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE));
ByteBuf segment1 = Unpooled.buffer(segment);
ByteBuf segment2 = Unpooled.buffer(segment);
encodeControlFrameHeader(header, type, flags, length);
segment1.writeInt(RANDOM.nextInt());
segment2.writeInt(RANDOM.nextInt());
replay(delegate);
decoder.decode(header);
decoder.decode(segment1);
decoder.decode(segment2);
verify(delegate);
assertFalse(header.isReadable());
assertFalse(segment1.isReadable());
assertFalse(segment2.isReadable());
}
@Test
public void testSpdyPingFrame() throws Exception {
short type = 6;
byte flags = 0;
int length = 4;
int id = RANDOM.nextInt();
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(id);
delegate.readPingFrame(id);
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testUnknownSpdyPingFrameFlags() throws Exception {
short type = 6;
byte flags = (byte) 0xFF; // undefined flags
int length = 4;
int id = RANDOM.nextInt();
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(id);
delegate.readPingFrame(id);
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testInvalidSpdyPingFrameLength() throws Exception {
short type = 6;
byte flags = 0;
int length = 8; // invalid length
int id = RANDOM.nextInt();
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(id);
delegate.readFrameError((String) anyObject());
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testSpdyGoAwayFrame() throws Exception {
short type = 7;
byte flags = 0;
int length = 8;
int lastGoodStreamId = RANDOM.nextInt() & 0x7FFFFFFF;
int statusCode = RANDOM.nextInt() | 0x01;
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(lastGoodStreamId);
buf.writeInt(statusCode);
delegate.readGoAwayFrame(lastGoodStreamId, statusCode);
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testUnknownSpdyGoAwayFrameFlags() throws Exception {
short type = 7;
byte flags = (byte) 0xFF; // undefined flags
int length = 8;
int lastGoodStreamId = RANDOM.nextInt() & 0x7FFFFFFF;
int statusCode = RANDOM.nextInt() | 0x01;
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(lastGoodStreamId);
buf.writeInt(statusCode);
delegate.readGoAwayFrame(lastGoodStreamId, statusCode);
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testReservedSpdyGoAwayFrameBits() throws Exception {
short type = 7;
byte flags = 0;
int length = 8;
int lastGoodStreamId = RANDOM.nextInt() & 0x7FFFFFFF;
int statusCode = RANDOM.nextInt() | 0x01;
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(lastGoodStreamId | 0x80000000); // should ignore reserved bit
buf.writeInt(statusCode);
delegate.readGoAwayFrame(lastGoodStreamId, statusCode);
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testInvalidSpdyGoAwayFrameLength() throws Exception {
short type = 7;
byte flags = 0;
int length = 12; // invalid length
int lastGoodStreamId = RANDOM.nextInt() & 0x7FFFFFFF;
int statusCode = RANDOM.nextInt() | 0x01;
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(lastGoodStreamId);
buf.writeInt(statusCode);
delegate.readFrameError((String) anyObject());
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testSpdyHeadersFrame() throws Exception {
short type = 8;
byte flags = 0;
int length = 4;
int streamId = RANDOM.nextInt() & 0x7FFFFFFF | 0x01;
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(streamId);
delegate.readHeadersFrame(streamId, false);
delegate.readHeaderBlockEnd();
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testLastSpdyHeadersFrame() throws Exception {
short type = 8;
byte flags = 0x01; // FLAG_FIN
int length = 4;
int streamId = RANDOM.nextInt() & 0x7FFFFFFF | 0x01;
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(streamId);
delegate.readHeadersFrame(streamId, true);
delegate.readHeaderBlockEnd();
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testUnknownSpdyHeadersFrameFlags() throws Exception {
short type = 8;
byte flags = (byte) 0xFE; // undefined flags
int length = 4;
int streamId = RANDOM.nextInt() & 0x7FFFFFFF | 0x01;
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(streamId);
delegate.readHeadersFrame(streamId, false);
delegate.readHeaderBlockEnd();
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testReservedSpdyHeadersFrameBits() throws Exception {
short type = 8;
byte flags = 0;
int length = 4;
int streamId = RANDOM.nextInt() & 0x7FFFFFFF | 0x01;
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(streamId | 0x80000000); // should ignore reserved bit
delegate.readHeadersFrame(streamId, false);
delegate.readHeaderBlockEnd();
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testInvalidSpdyHeadersFrameLength() throws Exception {
short type = 8;
byte flags = 0;
int length = 0; // invalid length
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
delegate.readFrameError((String) anyObject());
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testInvalidSpdyHeadersFrameStreamId() throws Exception {
short type = 8;
byte flags = 0;
int length = 4;
int streamId = 0; // invalid stream identifier
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(streamId);
delegate.readFrameError((String) anyObject());
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testSpdyHeadersFrameHeaderBlock() throws Exception {
short type = 8;
byte flags = 0;
int length = 4;
int headerBlockLength = 1024;
int streamId = RANDOM.nextInt() & 0x7FFFFFFF | 0x01;
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length + headerBlockLength);
buf.writeInt(streamId);
ByteBuf headerBlock = ReferenceCountUtil.releaseLater(Unpooled.buffer(headerBlockLength));
for (int i = 0; i < 256; i ++) {
headerBlock.writeInt(RANDOM.nextInt());
}
delegate.readHeadersFrame(streamId, false);
delegate.readHeaderBlock(headerBlock.duplicate());
delegate.readHeaderBlockEnd();
replay(delegate);
decoder.decode(buf);
decoder.decode(headerBlock);
verify(delegate);
assertFalse(buf.isReadable());
assertFalse(headerBlock.isReadable());
}
@Test
public void testSpdyWindowUpdateFrame() throws Exception {
short type = 9;
byte flags = 0;
int length = 8;
int streamId = RANDOM.nextInt() & 0x7FFFFFFF;
int deltaWindowSize = RANDOM.nextInt() & 0x7FFFFFFF | 0x01;
ByteBuf buf = Unpooled.buffer(SPDY_HEADER_SIZE + length);
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(streamId);
buf.writeInt(deltaWindowSize);
delegate.readWindowUpdateFrame(streamId, deltaWindowSize);
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testUnknownSpdyWindowUpdateFrameFlags() throws Exception {
short type = 9;
byte flags = (byte) 0xFF; // undefined flags
int length = 8;
int streamId = RANDOM.nextInt() & 0x7FFFFFFF;
int deltaWindowSize = RANDOM.nextInt() & 0x7FFFFFFF | 0x01;
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(streamId);
buf.writeInt(deltaWindowSize);
delegate.readWindowUpdateFrame(streamId, deltaWindowSize);
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testReservedSpdyWindowUpdateFrameBits() throws Exception {
short type = 9;
byte flags = 0;
int length = 8;
int streamId = RANDOM.nextInt() & 0x7FFFFFFF;
int deltaWindowSize = RANDOM.nextInt() & 0x7FFFFFFF | 0x01;
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(streamId | 0x80000000); // should ignore reserved bit
buf.writeInt(deltaWindowSize | 0x80000000); // should ignore reserved bit
delegate.readWindowUpdateFrame(streamId, deltaWindowSize);
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testInvalidSpdyWindowUpdateFrameLength() throws Exception {
short type = 9;
byte flags = 0;
int length = 12; // invalid length
int streamId = RANDOM.nextInt() & 0x7FFFFFFF;
int deltaWindowSize = RANDOM.nextInt() & 0x7FFFFFFF | 0x01;
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(streamId);
buf.writeInt(deltaWindowSize);
delegate.readFrameError((String) anyObject());
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
@Test
public void testIllegalSpdyWindowUpdateFrameDeltaWindowSize() throws Exception {
short type = 9;
byte flags = 0;
int length = 8;
int streamId = RANDOM.nextInt() & 0x7FFFFFFF;
int deltaWindowSize = 0; // invalid delta window size
ByteBuf buf = ReferenceCountUtil.releaseLater(Unpooled.buffer(SPDY_HEADER_SIZE + length));
encodeControlFrameHeader(buf, type, flags, length);
buf.writeInt(streamId);
buf.writeInt(deltaWindowSize);
delegate.readFrameError((String) anyObject());
replay(delegate);
decoder.decode(buf);
verify(delegate);
assertFalse(buf.isReadable());
}
}