parent
f8e2831185
commit
7c042b5205
|
@ -129,8 +129,19 @@ public class QpackEncoder
|
|||
_handler.onInstruction(new SetCapacityInstruction(capacity));
|
||||
}
|
||||
|
||||
public void insertCountIncrement(int increment)
|
||||
public void insertCountIncrement(int increment) throws QpackException
|
||||
{
|
||||
int insertCount = _context.getDynamicTable().getInsertCount();
|
||||
if (_knownInsertCount + increment > insertCount)
|
||||
throw new QpackException.StreamException("KnownInsertCount incremented over InsertCount");
|
||||
|
||||
// TODO: release any references to entries which used to insert new entries.
|
||||
for (Entry entry : _context.getDynamicTable())
|
||||
{
|
||||
if (entry.getIndex() > _knownInsertCount)
|
||||
break;
|
||||
}
|
||||
|
||||
_knownInsertCount += increment;
|
||||
}
|
||||
|
||||
|
@ -171,7 +182,7 @@ public class QpackEncoder
|
|||
_validateEncoding = validateEncoding;
|
||||
}
|
||||
|
||||
public boolean referenceEntry(Entry entry, StreamInfo streamInfo)
|
||||
public boolean referenceEntry(Entry entry)
|
||||
{
|
||||
if (entry == null)
|
||||
return false;
|
||||
|
@ -190,6 +201,14 @@ public class QpackEncoder
|
|||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean referenceEntry(Entry entry, StreamInfo streamInfo)
|
||||
{
|
||||
if (referenceEntry(entry))
|
||||
return true;
|
||||
|
||||
// We may need to risk blocking the stream in order to reference it.
|
||||
if (streamInfo.isBlocked())
|
||||
{
|
||||
|
@ -207,7 +226,7 @@ public class QpackEncoder
|
|||
return false;
|
||||
}
|
||||
|
||||
public static boolean shouldIndex(HttpField httpField)
|
||||
public boolean shouldIndex(HttpField httpField)
|
||||
{
|
||||
return !DO_NOT_INDEX.contains(httpField.getHeader());
|
||||
}
|
||||
|
@ -349,6 +368,38 @@ public class QpackEncoder
|
|||
}
|
||||
}
|
||||
|
||||
public boolean insert(HttpField field)
|
||||
{
|
||||
DynamicTable dynamicTable = _context.getDynamicTable();
|
||||
if (field.getValue() == null)
|
||||
field = new HttpField(field.getHeader(), field.getName(), "");
|
||||
|
||||
boolean canCreateEntry = shouldIndex(field) && (Entry.getSize(field) <= dynamicTable.getSpace());
|
||||
if (!canCreateEntry)
|
||||
return false;
|
||||
|
||||
Entry newEntry = new Entry(field);
|
||||
dynamicTable.add(newEntry);
|
||||
|
||||
Entry entry = _context.get(field);
|
||||
if (referenceEntry(entry))
|
||||
{
|
||||
_handler.onInstruction(new DuplicateInstruction(entry.getIndex()));
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean huffman = shouldHuffmanEncode(field);
|
||||
Entry nameEntry = _context.get(field.getName());
|
||||
if (referenceEntry(nameEntry))
|
||||
{
|
||||
_handler.onInstruction(new IndexedNameEntryInstruction(!nameEntry.isStatic(), nameEntry.getIndex(), huffman, field.getValue()));
|
||||
return true;
|
||||
}
|
||||
|
||||
_handler.onInstruction(new LiteralNameEntryInstruction(huffman, field.getName(), huffman, field.getValue()));
|
||||
return true;
|
||||
}
|
||||
|
||||
public static int encodeInsertCount(int reqInsertCount, int maxTableCapacity)
|
||||
{
|
||||
if (reqInsertCount == 0)
|
||||
|
|
|
@ -15,6 +15,7 @@ package org.eclipse.jetty.http3.qpack.table;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -22,7 +23,7 @@ import org.eclipse.jetty.http.HttpField;
|
|||
import org.eclipse.jetty.http3.qpack.QpackContext;
|
||||
import org.eclipse.jetty.http3.qpack.QpackException;
|
||||
|
||||
public class DynamicTable
|
||||
public class DynamicTable implements Iterable<Entry>
|
||||
{
|
||||
public static final int FIRST_INDEX = StaticTable.STATIC_SIZE + 1;
|
||||
private int _capacity;
|
||||
|
@ -189,6 +190,12 @@ public class DynamicTable
|
|||
return _capacity * 3 / 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Entry> iterator()
|
||||
{
|
||||
return _entries.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
|
|
|
@ -15,10 +15,12 @@ package org.eclipse.jetty.http3.qpack;
|
|||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
import org.eclipse.jetty.http3.qpack.generator.IndexedNameEntryInstruction;
|
||||
import org.eclipse.jetty.http3.qpack.generator.InsertCountIncrementInstruction;
|
||||
import org.eclipse.jetty.http3.qpack.generator.Instruction;
|
||||
import org.eclipse.jetty.http3.qpack.generator.LiteralNameEntryInstruction;
|
||||
import org.eclipse.jetty.http3.qpack.generator.SectionAcknowledgmentInstruction;
|
||||
import org.eclipse.jetty.http3.qpack.generator.SetCapacityInstruction;
|
||||
import org.eclipse.jetty.http3.qpack.parser.DecoderInstructionParser;
|
||||
|
@ -79,6 +81,8 @@ public class EncodeDecodeTest
|
|||
assertThat(_decoderHandler.getInstruction(), instanceOf(SectionAcknowledgmentInstruction.class));
|
||||
assertTrue(_decoderHandler.isEmpty());
|
||||
|
||||
_decoderInstructionParser.parse(toBuffer(new SectionAcknowledgmentInstruction(streamId)));
|
||||
|
||||
// B.2. Dynamic Table.
|
||||
|
||||
// Set capacity to 220.
|
||||
|
@ -86,32 +90,33 @@ public class EncodeDecodeTest
|
|||
Instruction instruction = _encoderHandler.getInstruction();
|
||||
assertThat(instruction, instanceOf(SetCapacityInstruction.class));
|
||||
assertThat(((SetCapacityInstruction)instruction).getCapacity(), is(220));
|
||||
assertThat(BufferUtil.toHexString(toBuffer(instruction)), equalsHex("3fbd01"));
|
||||
assertThat(toString(instruction), equalsHex("3fbd01"));
|
||||
|
||||
_encoderInstructionParser.parse(toHex("3fbd01"));
|
||||
assertThat(_decoder.getQpackContext().getDynamicTable().getCapacity(), is(220));
|
||||
|
||||
// Insert with named referenced to static table. Test we get two instructions generated to add to the dynamic table.
|
||||
streamId = 4;
|
||||
httpFields = HttpFields.build()
|
||||
.add(":authority", "www.example.com")
|
||||
.add(":path", "/sample/path");
|
||||
buffer = _encoder.encode(4, httpFields);
|
||||
buffer = _encoder.encode(streamId, httpFields);
|
||||
|
||||
instruction = _encoderHandler.getInstruction();
|
||||
assertThat(instruction, instanceOf(IndexedNameEntryInstruction.class));
|
||||
assertThat(((IndexedNameEntryInstruction)instruction).getIndex(), is(0));
|
||||
assertThat(((IndexedNameEntryInstruction)instruction).getValue(), is("www.example.com"));
|
||||
assertThat(BufferUtil.toHexString(toBuffer(instruction)), equalsHex("c00f 7777 772e 6578 616d 706c 652e 636f 6d"));
|
||||
assertThat(toString(instruction), equalsHex("c00f 7777 772e 6578 616d 706c 652e 636f 6d"));
|
||||
|
||||
instruction = _encoderHandler.getInstruction();
|
||||
assertThat(instruction, instanceOf(IndexedNameEntryInstruction.class));
|
||||
assertThat(((IndexedNameEntryInstruction)instruction).getIndex(), is(1));
|
||||
assertThat(((IndexedNameEntryInstruction)instruction).getValue(), is("/sample/path"));
|
||||
assertThat(BufferUtil.toHexString(toBuffer(instruction)), equalsHex("c10c 2f73 616d 706c 652f 7061 7468"));
|
||||
assertThat(toString(instruction), equalsHex("c10c 2f73 616d 706c 652f 7061 7468"));
|
||||
assertTrue(_encoderHandler.isEmpty());
|
||||
|
||||
// We cannot decode the buffer until we parse the two instructions generated above (we reach required insert count).
|
||||
_decoder.decode(4, buffer);
|
||||
_decoder.decode(streamId, buffer);
|
||||
assertNull(_decoderHandler.getHttpFields());
|
||||
|
||||
_encoderInstructionParser.parse(toHex("c00f 7777 772e 6578 616d 706c 652e 636f 6d"));
|
||||
|
@ -124,6 +129,17 @@ public class EncodeDecodeTest
|
|||
|
||||
assertThat(_decoderHandler.getInstruction(), instanceOf(SectionAcknowledgmentInstruction.class));
|
||||
assertTrue(_decoderHandler.isEmpty());
|
||||
|
||||
// Parse the decoder instructions on the encoder.
|
||||
_decoderInstructionParser.parse(toBuffer(new InsertCountIncrementInstruction(2)));
|
||||
_decoderInstructionParser.parse(toBuffer(new SectionAcknowledgmentInstruction(streamId)));
|
||||
|
||||
// B.3. Speculative Insert
|
||||
_encoder.insert(new HttpField("custom-key", "custom-value"));
|
||||
instruction = _encoderHandler.getInstruction();
|
||||
assertThat(instruction, instanceOf(LiteralNameEntryInstruction.class));
|
||||
assertThat(toString(instruction), equalsHex("4a63 7573 746f 6d2d 6b65 790c 6375 7374 6f6d 2d76 616c 7565"));
|
||||
_encoder.insertCountIncrement(1);
|
||||
}
|
||||
|
||||
public static ByteBuffer toBuffer(Instruction instruction)
|
||||
|
@ -134,6 +150,11 @@ public class EncodeDecodeTest
|
|||
return lease.getByteBuffers().get(0);
|
||||
}
|
||||
|
||||
public static String toString(Instruction instruction)
|
||||
{
|
||||
return BufferUtil.toHexString(toBuffer(instruction));
|
||||
}
|
||||
|
||||
public static ByteBuffer toHex(String hexString)
|
||||
{
|
||||
hexString = hexString.replaceAll("\\s+", "");
|
||||
|
|
Loading…
Reference in New Issue