Improve QPACK testing for instruction parsers.

Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
This commit is contained in:
Lachlan Roberts 2021-05-07 16:43:37 +10:00 committed by Simone Bordet
parent b2a91fdbaf
commit 684272e1ae
3 changed files with 87 additions and 52 deletions

View File

@ -16,8 +16,6 @@ package org.eclipse.jetty.http3.qpack;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import org.eclipse.jetty.http3.qpack.internal.parser.DecoderInstructionParser; import org.eclipse.jetty.http3.qpack.internal.parser.DecoderInstructionParser;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.TypeUtil;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -39,10 +37,30 @@ public class DecoderInstructionParserTest
} }
@Test @Test
public void testAddWithReferencedEntry() throws Exception public void testSetDynamicTableCapacityInstruction() throws Exception
{ {
String insertAuthorityEntry = "c00f7777772e6578616d706c652e636f6d"; // Set Dynamic Table Capacity=220.
ByteBuffer buffer = BufferUtil.toBuffer(TypeUtil.fromHexString(insertAuthorityEntry)); ByteBuffer buffer = QpackTestUtil.hexToBuffer("3fbd 01");
_instructionParser.parse(buffer);
assertThat(_handler.setCapacities.poll(), is(220));
assertTrue(_handler.isEmpty());
}
@Test
public void testDuplicateInstruction() throws Exception
{
// Duplicate (Relative Index = 2).
ByteBuffer buffer = QpackTestUtil.hexToBuffer("02");
_instructionParser.parse(buffer);
assertThat(_handler.duplicates.poll(), is(2));
assertTrue(_handler.isEmpty());
}
@Test
public void testInsertNameWithReferenceInstruction() throws Exception
{
// Insert With Name Reference to Static Table, Index=0 (:authority=www.example.com).
ByteBuffer buffer = QpackTestUtil.hexToBuffer("c00f 7777 772e 6578 616d 706c 652e 636f 6d");
_instructionParser.parse(buffer); _instructionParser.parse(buffer);
DecoderParserDebugHandler.ReferencedEntry entry = _handler.referencedNameEntries.poll(); DecoderParserDebugHandler.ReferencedEntry entry = _handler.referencedNameEntries.poll();
assertNotNull(entry); assertNotNull(entry);
@ -50,8 +68,8 @@ public class DecoderInstructionParserTest
assertThat(entry.dynamic, is(false)); assertThat(entry.dynamic, is(false));
assertThat(entry.value, is("www.example.com")); assertThat(entry.value, is("www.example.com"));
String insertPathEntry = "c10c2f73616d706c652f70617468"; // Insert With Name Reference to Static Table, Index=1 (:path=/sample/path).
buffer = BufferUtil.toBuffer(TypeUtil.fromHexString(insertPathEntry)); buffer = QpackTestUtil.hexToBuffer("c10c 2f73 616d 706c 652f 7061 7468");
_instructionParser.parse(buffer); _instructionParser.parse(buffer);
entry = _handler.referencedNameEntries.poll(); entry = _handler.referencedNameEntries.poll();
assertNotNull(entry); assertNotNull(entry);
@ -61,12 +79,19 @@ public class DecoderInstructionParserTest
} }
@Test @Test
public void testSetCapacity() throws Exception public void testInsertWithLiteralNameInstruction() throws Exception
{ {
String setCapacityString = "3fbd01"; // Insert With Literal Name (custom-key=custom-value).
ByteBuffer buffer = BufferUtil.toBuffer(TypeUtil.fromHexString(setCapacityString)); ByteBuffer buffer = QpackTestUtil.hexToBuffer("4a63 7573 746f 6d2d 6b65 790c 6375 7374 6f6d 2d76 616c 7565");
_instructionParser.parse(buffer); _instructionParser.parse(buffer);
assertThat(_handler.setCapacities.poll(), is(220));
// We received the instruction correctly.
DecoderParserDebugHandler.LiteralEntry entry = _handler.literalNameEntries.poll();
assertNotNull(entry);
assertThat(entry.name, is("custom-key"));
assertThat(entry.value, is("custom-value"));
// There are no other instructions received.
assertTrue(_handler.isEmpty()); assertTrue(_handler.isEmpty());
} }
} }

View File

@ -22,7 +22,7 @@ public class DecoderParserDebugHandler implements DecoderInstructionParser.Handl
{ {
public Queue<Integer> setCapacities = new LinkedList<>(); public Queue<Integer> setCapacities = new LinkedList<>();
public Queue<Integer> duplicates = new LinkedList<>(); public Queue<Integer> duplicates = new LinkedList<>();
public Queue<Entry> literalNameEntries = new LinkedList<>(); public Queue<LiteralEntry> literalNameEntries = new LinkedList<>();
public Queue<ReferencedEntry> referencedNameEntries = new LinkedList<>(); public Queue<ReferencedEntry> referencedNameEntries = new LinkedList<>();
private final QpackDecoder _decoder; private final QpackDecoder _decoder;
@ -37,30 +37,30 @@ public class DecoderParserDebugHandler implements DecoderInstructionParser.Handl
_decoder = decoder; _decoder = decoder;
} }
public static class Entry public static class LiteralEntry
{ {
public Entry(String name, String value) public String name;
public String value;
public LiteralEntry(String name, String value)
{ {
this.value = value; this.value = value;
this.name = name; this.name = name;
} }
String name;
String value;
} }
public static class ReferencedEntry public static class ReferencedEntry
{ {
public int index;
public boolean dynamic;
public String value;
public ReferencedEntry(int index, boolean dynamic, String value) public ReferencedEntry(int index, boolean dynamic, String value)
{ {
this.index = index; this.index = index;
this.dynamic = dynamic; this.dynamic = dynamic;
this.value = value; this.value = value;
} }
int index;
boolean dynamic;
String value;
} }
@Override @Override
@ -90,7 +90,7 @@ public class DecoderParserDebugHandler implements DecoderInstructionParser.Handl
@Override @Override
public void onInsertWithLiteralName(String name, String value) throws QpackException public void onInsertWithLiteralName(String name, String value) throws QpackException
{ {
literalNameEntries.add(new Entry(name, value)); literalNameEntries.add(new LiteralEntry(name, value));
if (_decoder != null) if (_decoder != null)
_decoder.insert(name, value); _decoder.insert(name, value);
} }

View File

@ -18,7 +18,7 @@ import java.nio.ByteBuffer;
import org.eclipse.jetty.http3.qpack.internal.parser.EncoderInstructionParser; import org.eclipse.jetty.http3.qpack.internal.parser.EncoderInstructionParser;
import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.TypeUtil;
import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
@ -27,47 +27,53 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
public class EncoderInstructionParserTest public class EncoderInstructionParserTest
{ {
private EncoderParserDebugHandler _handler;
private EncoderInstructionParser _instructionParser;
@BeforeEach
public void before()
{
_handler = new EncoderParserDebugHandler();
_instructionParser = new EncoderInstructionParser(_handler);
}
@Test @Test
public void testSectionAcknowledgement() throws Exception public void testSectionAcknowledgement() throws Exception
{ {
EncoderParserDebugHandler debugHandler = new EncoderParserDebugHandler();
EncoderInstructionParser incomingEncoderStream = new EncoderInstructionParser(debugHandler);
// Example from the spec, section acknowledgement instruction with stream id 4. // Example from the spec, section acknowledgement instruction with stream id 4.
String encoded = "84"; String encoded = "84";
ByteBuffer buffer = BufferUtil.toBuffer(TypeUtil.fromHexString(encoded)); ByteBuffer buffer = BufferUtil.toBuffer(TypeUtil.fromHexString(encoded));
incomingEncoderStream.parse(buffer); _instructionParser.parse(buffer);
assertThat(debugHandler.sectionAcknowledgements.poll(), is(4)); assertThat(_handler.sectionAcknowledgements.poll(), is(4));
assertTrue(debugHandler.isEmpty()); assertTrue(_handler.isEmpty());
// 1111 1110 == FE is largest value we can do without continuation should be stream ID 126. // 1111 1110 == FE is largest value we can do without continuation should be stream ID 126.
encoded = "FE"; encoded = "FE";
buffer = BufferUtil.toBuffer(TypeUtil.fromHexString(encoded)); buffer = BufferUtil.toBuffer(TypeUtil.fromHexString(encoded));
incomingEncoderStream.parse(buffer); _instructionParser.parse(buffer);
assertThat(debugHandler.sectionAcknowledgements.poll(), is(126)); assertThat(_handler.sectionAcknowledgements.poll(), is(126));
assertTrue(debugHandler.isEmpty()); assertTrue(_handler.isEmpty());
// 1111 1111 0000 0000 == FF00 is next value, stream id 127. // 1111 1111 0000 0000 == FF00 is next value, stream id 127.
encoded = "FF00"; encoded = "FF00";
buffer = BufferUtil.toBuffer(TypeUtil.fromHexString(encoded)); buffer = BufferUtil.toBuffer(TypeUtil.fromHexString(encoded));
incomingEncoderStream.parse(buffer); _instructionParser.parse(buffer);
assertThat(debugHandler.sectionAcknowledgements.poll(), is(127)); assertThat(_handler.sectionAcknowledgements.poll(), is(127));
assertTrue(debugHandler.isEmpty()); assertTrue(_handler.isEmpty());
// 1111 1111 0000 0001 == FF01 is next value, stream id 128. // 1111 1111 0000 0001 == FF01 is next value, stream id 128.
encoded = "FF01"; encoded = "FF01";
buffer = BufferUtil.toBuffer(TypeUtil.fromHexString(encoded)); buffer = BufferUtil.toBuffer(TypeUtil.fromHexString(encoded));
incomingEncoderStream.parse(buffer); _instructionParser.parse(buffer);
assertThat(debugHandler.sectionAcknowledgements.poll(), is(128)); assertThat(_handler.sectionAcknowledgements.poll(), is(128));
assertTrue(debugHandler.isEmpty()); assertTrue(_handler.isEmpty());
// FFBA09 contains section ack with stream ID of 1337, this contains an octet with continuation bit. // FFBA09 contains section ack with stream ID of 1337, this contains an octet with continuation bit.
encoded = "FFBA09"; encoded = "FFBA09";
buffer = BufferUtil.toBuffer(TypeUtil.fromHexString(encoded)); buffer = BufferUtil.toBuffer(TypeUtil.fromHexString(encoded));
incomingEncoderStream.parse(buffer); _instructionParser.parse(buffer);
assertThat(debugHandler.sectionAcknowledgements.poll(), is(1337)); assertThat(_handler.sectionAcknowledgements.poll(), is(1337));
assertTrue(debugHandler.isEmpty()); assertTrue(_handler.isEmpty());
// Test with continuation. // Test with continuation.
encoded = "FFBA09"; encoded = "FFBA09";
@ -76,27 +82,31 @@ public class EncoderInstructionParserTest
{ {
ByteBuffer buffer1 = BufferUtil.toBuffer(bytes, 0, 2); ByteBuffer buffer1 = BufferUtil.toBuffer(bytes, 0, 2);
ByteBuffer buffer2 = BufferUtil.toBuffer(bytes, 2, 1); ByteBuffer buffer2 = BufferUtil.toBuffer(bytes, 2, 1);
incomingEncoderStream.parse(buffer1); _instructionParser.parse(buffer1);
assertTrue(debugHandler.isEmpty()); assertTrue(_handler.isEmpty());
incomingEncoderStream.parse(buffer2); _instructionParser.parse(buffer2);
assertThat(debugHandler.sectionAcknowledgements.poll(), is(1337)); assertThat(_handler.sectionAcknowledgements.poll(), is(1337));
assertTrue(debugHandler.isEmpty()); assertTrue(_handler.isEmpty());
} }
} }
@Disabled
@Test @Test
public void testStreamCancellation() throws Exception public void testStreamCancellation() throws Exception
{ {
// TODO: Write this test. // Stream Cancellation (Stream=8).
throw new RuntimeException("TODO: testStreamCancellation"); ByteBuffer buffer = QpackTestUtil.hexToBuffer("48");
_instructionParser.parse(buffer);
assertThat(_handler.streamCancellations.poll(), is(8));
assertTrue(_handler.isEmpty());
} }
@Disabled
@Test @Test
public void testInsertCountIncrement() throws Exception public void testInsertCountIncrement() throws Exception
{ {
// TODO: Write this test. // Insert Count Increment (1).
throw new RuntimeException("TODO: testInsertCountIncrement"); ByteBuffer buffer = QpackTestUtil.hexToBuffer("01");
_instructionParser.parse(buffer);
assertThat(_handler.insertCountIncrements.poll(), is(1));
assertTrue(_handler.isEmpty());
} }
} }