package com.webpieces.http2parser;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.webpieces.data.api.BufferCreationPool;
import org.webpieces.data.api.DataWrapper;
import org.webpieces.data.api.DataWrapperGenerator;
import org.webpieces.data.api.DataWrapperGeneratorFactory;
import com.webpieces.hpack.api.HpackParser;
import com.webpieces.hpack.api.HpackParserFactory;
import com.webpieces.hpack.api.UnmarshalState;
import com.webpieces.http2parser.api.Http2Memento;
import com.webpieces.http2parser.api.Http2Parser;
import com.webpieces.http2parser.api.Http2ParserFactory;
import com.webpieces.http2parser.api.dto.lib.Http2Header;
import com.webpieces.http2parser.api.dto.lib.Http2Msg;
import com.webpieces.http2parser.api.dto.lib.Http2Setting;
import com.webpieces.http2parser.api.dto.lib.SettingsParameter;
public class TestHttp2Parser {
private static String aBunchOfDataFrames =
"00 00 08" + // Length
"00" + // Type
"00" + // Flags
"00 00 00 01" + // R + streamid
"FF FF FF FF FF FF FF FF" + // payload
"00 00 08" + // Length
"00" + // Type
"01" + // Flags - endStream = true
"00 00 00 01" + // R + streamid
"FF FF FF FF FF FF FF FF" + // payload
"00 00 0B" + // Length
"00" + // Type
"08" + // Flags (padded = true)
"00 00 00 01" + // R + streamid
"02" + // padding length
"FF FF FF FF FF FF FF FF" + // data
"00 00" + // padding
"00 00 0B" + // Length
"00" + // Type
"09" + // Flags - endStream = true
"00 00 00 01" + // R + streamid
"02" + // padding length
"FF FF FF FF FF FF FF FF" + // payload
"00 00"; // padding
private static String dataFramesWithSomeLeftOverData = aBunchOfDataFrames +
"00 00";
private static String dataFramesWithABunchOfLeftOverData = aBunchOfDataFrames +
"00 00 08" + // length
"00"; // type
private static DataWrapperGenerator dataGen = DataWrapperGeneratorFactory.createDataWrapperGenerator();
private static HpackParser parser = HpackParserFactory.createParser(new BufferCreationPool(), true);
private static Http2Parser parser2 = Http2ParserFactory.createParser(new BufferCreationPool());
private static LinkedList<Http2Header> basicRequestHeaders = new LinkedList<>();
private static LinkedList<Http2Header> basicResponseHeaders = new LinkedList<>();
private static LinkedList<Http2Header> ngHttp2ExampleHeaders = new LinkedList<>();
private int maxFrameSize;
private int maxHeaderSize = 4096;
private int maxHeaderTableSize = 4096;
private static List<Http2Setting> settings = new ArrayList<>();
// https://github.com/http2jp/hpack-test-case/blob/master/nghttp2/story_00.json
private static String basicRequestSerializationNghttp2 = "82864188f439ce75c875fa5784";
private static String ngHttp2ExampleHeaderFragment =
"82 84 86 41 88 aa 69 d2 9a c4 b9 ec 9b 53 03 2a 2f" +
"2a 90 7a 8a aa 69 d2 9a c4 c0 57 0b 6b 83";
static {
basicRequestHeaders.add(new Http2Header(":method", "GET"));
basicRequestHeaders.add(new Http2Header(":scheme", "http"));
basicRequestHeaders.add(new Http2Header(":authority", "yahoo.co.jp"));
basicRequestHeaders.add(new Http2Header(":path", "/"));
ngHttp2ExampleHeaders.add(new Http2Header(":method", "GET"));
ngHttp2ExampleHeaders.add(new Http2Header(":path", "/"));
ngHttp2ExampleHeaders.add(new Http2Header(":scheme", "http"));
ngHttp2ExampleHeaders.add(new Http2Header(":authority", "nghttp2.org"));
ngHttp2ExampleHeaders.add(new Http2Header("accept", "*/*"));
ngHttp2ExampleHeaders.add(new Http2Header("accept-encoding", "gzip, deflate"));
ngHttp2ExampleHeaders.add(new Http2Header("user-agent", "nghttp2/1.15.0"));
basicResponseHeaders.add(new Http2Header(":status", "200"));
basicResponseHeaders.add(new Http2Header("date", "Tue, 27 Sep 2016 19:41:50 GMT"));
basicResponseHeaders.add(new Http2Header("content-type", "text/html"));
basicResponseHeaders.add(new Http2Header("set-cookie", "__cfduid=d8bfe297ef26ef6252ea3a822360a6f411475005310; expires=Wed, 27-Sep-17 19:41:50 GMT; path=/; domain=.cloudflare.com; HttpOnly"));
basicResponseHeaders.add(new Http2Header("last-modified", "Tue, 27 Sep 2016 17:39:01 GMT"));
basicResponseHeaders.add(new Http2Header("cache-control", "public, max-age=14400"));
basicResponseHeaders.add(new Http2Header("served-in-seconds", "0.001"));
basicResponseHeaders.add(new Http2Header("cf-cache-status", "REVALIDATED"));
basicResponseHeaders.add(new Http2Header("expires", "Tue, 27 Sep 2016 23:41:50 GMT"));
basicResponseHeaders.add(new Http2Header("server", "cloudflare-nginx"));
basicResponseHeaders.add(new Http2Header("cf-ray", "2e916f776c724fd5-DEN"));
settings.add(new Http2Setting(SettingsParameter.SETTINGS_MAX_FRAME_SIZE, 16384L));
}
@Before
public void setUp() {
maxFrameSize = Integer.MAX_VALUE;
}
@Test
public void testBasicParse() {
DataWrapper data = UtilsForTest2.dataWrapperFromHex(aBunchOfDataFrames);
UnmarshalState state = parser.prepareToUnmarshal(maxHeaderSize, maxHeaderTableSize, maxFrameSize);
UnmarshalState result = parser.unmarshal(state, data);
Assert.assertEquals(0, result.getLeftOverDataSize());
List<Http2Msg> frames = result.getParsedFrames();
Assert.assertTrue(frames.size() == 4);
}
@Test
public void testParseWithSplitFrame() {
DataWrapper fullFrames = UtilsForTest2.dataWrapperFromHex(aBunchOfDataFrames);
List<? extends DataWrapper> split = dataGen.split(fullFrames, 6);
UnmarshalState state = parser.prepareToUnmarshal(maxHeaderSize, maxHeaderTableSize, maxFrameSize);
state = parser.unmarshal(state, split.get(0));
state = parser.unmarshal(state, split.get(1));
Assert.assertEquals(0, state.getLeftOverDataSize());
List<Http2Msg> frames = state.getParsedFrames();
Assert.assertTrue(frames.size() == 4);
}
// @Test
// public void testHigherSplit() {
// DataWrapper fullFrames = UtilsForTest.dataWrapperFromHex(aBunchOfDataFrames);
// List<? extends DataWrapper> split = dataGen.split(fullFrames, 12);
//
// DataWrapper old = parser.prepareToParse();
// ParserResult result = parser.parse(old, split.get(0), decoder, settings);
// Assert.assertFalse(result.hasParsedFrames());
// Assert.assertFalse(result.hasMoreData());
//
// ParserResult nextResult = parser.parse(result.getMoreData(), split.get(1), decoder, settings);
// Assert.assertTrue(nextResult.hasParsedFrames());
// Assert.assertFalse(nextResult.hasMoreData());
// List<Http2Frame> frames = nextResult.getParsedFrames();
// Assert.assertTrue(frames.size() == 4);
// }
@Test
public void testHigherSplit2() {
DataWrapper fullFrames = UtilsForTest2.dataWrapperFromHex(aBunchOfDataFrames);
List<? extends DataWrapper> split = dataGen.split(fullFrames, 12);
Http2Memento state = parser2.prepareToParse(Integer.MAX_VALUE);
parser2.parse(state, split.get(0));
Assert.assertEquals(0, state.getParsedFrames().size());
Assert.assertTrue(state.getLeftOverData().getReadableSize() > 0);
parser2.parse(state, split.get(1));
Assert.assertEquals(4, state.getParsedFrames().size());
Assert.assertFalse(state.getLeftOverData().getReadableSize() > 0);
}
@Test
public void testBasicParseWithPriorData() {
UnmarshalState result = parser.prepareToUnmarshal(maxHeaderSize, maxHeaderTableSize, maxFrameSize);
result = parser.unmarshal(result,
UtilsForTest2.dataWrapperFromHex(aBunchOfDataFrames.subSequence(0, 8).toString()) // oldData
);
result = parser.unmarshal(result,
UtilsForTest2.dataWrapperFromHex(aBunchOfDataFrames.substring(8)) // newData
);
Assert.assertEquals(0, result.getLeftOverDataSize());
List<Http2Msg> frames = result.getParsedFrames();
Assert.assertTrue(frames.size() == 4);
}
@Test
public void testBasicParseWithSomeData() {
UnmarshalState result = parser.prepareToUnmarshal(maxHeaderSize, maxHeaderTableSize, maxFrameSize);
result = parser.unmarshal(result,
UtilsForTest2.dataWrapperFromHex(dataFramesWithSomeLeftOverData));
Assert.assertTrue(result.getLeftOverDataSize() > 0);
List<Http2Msg> frames = result.getParsedFrames();
Assert.assertTrue(frames.size() == 4);
//Assert.assertEquals(UtilsForTest2.toHexString(result.getLeftOverData().createByteArray()), "0000");
}
@Test
public void testBasicParseWithMoreData() {
UnmarshalState result = parser.prepareToUnmarshal(maxHeaderSize, maxHeaderTableSize, maxFrameSize);
result = parser.unmarshal(result,
UtilsForTest2.dataWrapperFromHex(dataFramesWithABunchOfLeftOverData));
Assert.assertTrue(result.getLeftOverDataSize() > 0);
List<Http2Msg> frames = result.getParsedFrames();
Assert.assertTrue(frames.size() == 4);
//Assert.assertEquals(UtilsForTest2.toHexString(result.getLeftOverData().createByteArray()), "00000800");
}
@Test
public void testBasicParseWithLittleData() {
UnmarshalState result = parser.prepareToUnmarshal(maxHeaderSize, maxHeaderTableSize, maxFrameSize);
result = parser.unmarshal(result,
UtilsForTest2.dataWrapperFromHex("00 00"));
Assert.assertTrue(result.getLeftOverDataSize() > 0);
List<Http2Msg> frames = result.getParsedFrames();
Assert.assertTrue(frames.size() == 0);
//Assert.assertEquals(UtilsForTest2.toHexString(result.getLeftOverData().createByteArray()), "0000");
}
@Test
public void testBasicParseWithNoData() {
UnmarshalState result = parser.prepareToUnmarshal(maxHeaderSize, maxHeaderTableSize, maxFrameSize);
result = parser.unmarshal(result,
dataGen.emptyWrapper());
Assert.assertEquals(0, result.getLeftOverDataSize());
List<Http2Msg> frames = result.getParsedFrames();
Assert.assertTrue(frames.size() == 0);
}
}