Decouple the DynamicTable from the QpackContext.

Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
This commit is contained in:
Lachlan Roberts 2021-02-05 18:48:54 +11:00 committed by Simone Bordet
parent 5d07842af8
commit f39313b1d5
13 changed files with 351 additions and 284 deletions

View File

@ -17,6 +17,7 @@ import org.eclipse.jetty.http3.qpack.QpackFieldPreEncoder;
module org.eclipse.jetty.http3.qpack
{
exports org.eclipse.jetty.http3.qpack;
exports org.eclipse.jetty.http3.qpack.table;
requires transitive org.eclipse.jetty.http;
requires org.slf4j;

View File

@ -15,6 +15,7 @@ package org.eclipse.jetty.http3.qpack;
import org.eclipse.jetty.http.HostPortHttpField;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http3.qpack.table.StaticTable;
/**
*

View File

@ -13,13 +13,11 @@
package org.eclipse.jetty.http3.qpack;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http3.qpack.table.DynamicTable;
import org.eclipse.jetty.http3.qpack.table.Entry;
import org.eclipse.jetty.http3.qpack.table.StaticTable;
import org.eclipse.jetty.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -36,35 +34,23 @@ public class QpackContext
{
public static final Logger LOG = LoggerFactory.getLogger(QpackContext.class);
private static final StaticTable __staticTable = new StaticTable();
private final DynamicTable _dynamicTable;
private int _maxDynamicTableSizeInBytes;
private int _dynamicTableSizeInBytes;
private final Map<HttpField, Entry> _fieldMap = new HashMap<>();
private final Map<String, Entry> _nameMap = new HashMap<>();
QpackContext(int maxDynamicTableSize)
{
_maxDynamicTableSizeInBytes = maxDynamicTableSize;
int guesstimateEntries = 10 + maxDynamicTableSize / (32 + 10 + 10);
_dynamicTable = new DynamicTable(guesstimateEntries);
_dynamicTable = new DynamicTable(maxDynamicTableSize);
if (LOG.isDebugEnabled())
LOG.debug(String.format("HdrTbl[%x] created max=%d", hashCode(), maxDynamicTableSize));
}
public void resize(int newMaxDynamicTableSize)
{
if (LOG.isDebugEnabled())
LOG.debug(String.format("HdrTbl[%x] resized max=%d->%d", hashCode(), _maxDynamicTableSizeInBytes, newMaxDynamicTableSize));
_maxDynamicTableSizeInBytes = newMaxDynamicTableSize;
_dynamicTable.evict();
_dynamicTable.setCapacity(newMaxDynamicTableSize);
}
public Entry get(HttpField field)
{
Entry entry = _fieldMap.get(field);
Entry entry = _dynamicTable.get(field);
if (entry == null)
entry = __staticTable.get(field);
return entry;
@ -75,7 +61,7 @@ public class QpackContext
Entry entry = __staticTable.get(name);
if (entry != null)
return entry;
return _nameMap.get(StringUtil.asciiToLowerCase(name));
return _dynamicTable.get(StringUtil.asciiToLowerCase(name));
}
public Entry get(int index)
@ -96,32 +82,15 @@ public class QpackContext
public Entry add(HttpField field)
{
Entry entry = new Entry(field);
int size = entry.getSize();
if (size > _maxDynamicTableSizeInBytes)
{
if (LOG.isDebugEnabled())
LOG.debug(String.format("HdrTbl[%x] !added size %d>%d", hashCode(), size, _maxDynamicTableSizeInBytes));
_dynamicTable.evictAll();
return null;
}
_dynamicTableSizeInBytes += size;
_dynamicTable.add(entry);
_fieldMap.put(field, entry);
_nameMap.put(field.getLowerCaseName(), entry);
if (LOG.isDebugEnabled())
LOG.debug(String.format("HdrTbl[%x] added %s", hashCode(), entry));
_dynamicTable.evict();
return entry;
return _dynamicTable.add(new Entry(field));
}
/**
* @return Current dynamic table size in entries
*/
public int size()
public int getNumEntries()
{
return _dynamicTable.size();
return _dynamicTable.getNumEntries();
}
/**
@ -129,7 +98,7 @@ public class QpackContext
*/
public int getDynamicTableSize()
{
return _dynamicTableSizeInBytes;
return _dynamicTable.getSize();
}
/**
@ -137,19 +106,25 @@ public class QpackContext
*/
public int getMaxDynamicTableSize()
{
return _maxDynamicTableSizeInBytes;
return _dynamicTable.getMaxSize();
}
/**
* @return index of entry in COMBINED address space (QPACK has separate address spaces for dynamic and static tables).
*/
public int index(Entry entry)
{
if (entry._slot < 0)
if (entry.getIndex() < 0)
return 0;
if (entry.isStatic())
return entry._slot;
return entry.getIndex();
return _dynamicTable.index(entry);
}
/**
* @return index of entry in the static table or 0 if not in the table (I guess the entries start from 1 not 0 unlike QPACK).
*/
public static int staticIndex(HttpHeader header)
{
if (header == null)
@ -157,194 +132,6 @@ public class QpackContext
Entry entry = __staticTable.get(header.asString());
if (entry == null)
return 0;
return entry._slot;
}
@Override
public String toString()
{
return String.format("QpackContext@%x{entries=%d,size=%d,max=%d}", hashCode(), _dynamicTable.size(), _dynamicTableSizeInBytes, _maxDynamicTableSizeInBytes);
}
private class DynamicTable
{
Entry[] _entries;
int _size;
int _offset;
int _growby;
private DynamicTable(int initCapacity)
{
_entries = new Entry[initCapacity];
_growby = initCapacity;
}
public void add(Entry entry)
{
if (_size == _entries.length)
{
Entry[] entries = new Entry[_entries.length + _growby];
for (int i = 0; i < _size; i++)
{
int slot = (_offset + i) % _entries.length;
entries[i] = _entries[slot];
entries[i]._slot = i;
}
_entries = entries;
_offset = 0;
}
int slot = (_size++ + _offset) % _entries.length;
_entries[slot] = entry;
entry._slot = slot;
}
public int index(Entry entry)
{
return StaticTable.STATIC_SIZE + _size - (entry._slot - _offset + _entries.length) % _entries.length;
}
public Entry get(int index)
{
int d = index - StaticTable.STATIC_SIZE - 1;
if (d < 0 || d >= _size)
return null;
int slot = (_offset + _size - d - 1) % _entries.length;
return _entries[slot];
}
public int size()
{
return _size;
}
private void evict()
{
while (_dynamicTableSizeInBytes > _maxDynamicTableSizeInBytes)
{
Entry entry = _entries[_offset];
_entries[_offset] = null;
_offset = (_offset + 1) % _entries.length;
_size--;
if (LOG.isDebugEnabled())
LOG.debug(String.format("HdrTbl[%x] evict %s", QpackContext.this.hashCode(), entry));
_dynamicTableSizeInBytes -= entry.getSize();
entry._slot = -1;
_fieldMap.remove(entry.getHttpField());
String lc = entry.getHttpField().getLowerCaseName();
if (entry == _nameMap.get(lc))
_nameMap.remove(lc);
}
if (LOG.isDebugEnabled())
LOG.debug(String.format("HdrTbl[%x] entries=%d, size=%d, max=%d", QpackContext.this.hashCode(), _dynamicTable.size(), _dynamicTableSizeInBytes, _maxDynamicTableSizeInBytes));
}
private void evictAll()
{
if (LOG.isDebugEnabled())
LOG.debug(String.format("HdrTbl[%x] evictAll", QpackContext.this.hashCode()));
if (size() > 0)
{
_fieldMap.clear();
_nameMap.clear();
_offset = 0;
_size = 0;
_dynamicTableSizeInBytes = 0;
Arrays.fill(_entries, null);
}
}
}
public static class Entry
{
final HttpField _field;
int _slot; // The index within it's array
Entry()
{
_slot = -1;
_field = null;
}
Entry(HttpField field)
{
_field = field;
}
public int getSize()
{
String value = _field.getValue();
return 32 + _field.getName().length() + (value == null ? 0 : value.length());
}
public HttpField getHttpField()
{
return _field;
}
public boolean isStatic()
{
return false;
}
public byte[] getStaticHuffmanValue()
{
return null;
}
@Override
public String toString()
{
return String.format("{%s,%d,%s,%x}", isStatic() ? "S" : "D", _slot, _field, hashCode());
}
}
public static class StaticEntry extends Entry
{
private final byte[] _huffmanValue;
private final byte _encodedField;
StaticEntry(int index, HttpField field)
{
super(field);
_slot = index;
String value = field.getValue();
if (value != null && value.length() > 0)
{
int huffmanLen = Huffman.octetsNeeded(value);
if (huffmanLen < 0)
throw new IllegalStateException("bad value");
int lenLen = NBitInteger.octectsNeeded(7, huffmanLen);
_huffmanValue = new byte[1 + lenLen + huffmanLen];
ByteBuffer buffer = ByteBuffer.wrap(_huffmanValue);
// Indicate Huffman
buffer.put((byte)0x80);
// Add huffman length
NBitInteger.encode(buffer, 7, huffmanLen);
// Encode value
Huffman.encode(buffer, value);
}
else
_huffmanValue = null;
_encodedField = (byte)(0x80 | index);
}
@Override
public boolean isStatic()
{
return true;
}
@Override
public byte[] getStaticHuffmanValue()
{
return _huffmanValue;
}
public byte getEncodedField()
{
return _encodedField;
}
return entry.getIndex();
}
}

View File

@ -19,7 +19,7 @@ import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpTokens;
import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.http3.qpack.QpackContext.Entry;
import org.eclipse.jetty.http3.qpack.table.Entry;
import org.eclipse.jetty.util.BufferUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@ -29,8 +29,8 @@ import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.http.PreEncodedHttpField;
import org.eclipse.jetty.http3.qpack.QpackContext.Entry;
import org.eclipse.jetty.http3.qpack.QpackContext.StaticEntry;
import org.eclipse.jetty.http3.qpack.table.Entry;
import org.eclipse.jetty.http3.qpack.table.StaticEntry;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.StringUtil;
import org.slf4j.Logger;

View File

@ -0,0 +1,157 @@
package org.eclipse.jetty.http3.qpack.table;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http3.qpack.QpackContext;
public class DynamicTable
{
public static final int FIRST_INDEX = StaticTable.STATIC_SIZE + 1;
private int _maxDynamicTableSizeInBytes;
private int _dynamicTableSizeInBytes;
private final Map<HttpField, Entry> _fieldMap = new HashMap<>();
private final Map<String, Entry> _nameMap = new HashMap<>();
private final int _growby;
private Entry[] _entries;
private int _numEntries;
private int _offset;
public DynamicTable(int maxSize)
{
_maxDynamicTableSizeInBytes = maxSize;
int initCapacity = 10 + maxSize / (32 + 10 + 10);
_entries = new Entry[initCapacity];
_growby = initCapacity;
}
public Entry add(Entry entry)
{
int size = entry.getSize();
if (size > _maxDynamicTableSizeInBytes)
{
if (QpackContext.LOG.isDebugEnabled())
QpackContext.LOG.debug(String.format("HdrTbl[%x] !added size %d>%d", hashCode(), size, _maxDynamicTableSizeInBytes));
evictAll();
return null;
}
_dynamicTableSizeInBytes += size;
if (_numEntries == _entries.length)
{
Entry[] entries = new Entry[_entries.length + _growby];
for (int i = 0; i < _numEntries; i++)
{
int slot = (_offset + i) % _entries.length;
entries[i] = _entries[slot];
entries[i].setIndex(i);
}
_entries = entries;
_offset = 0;
}
int slot = (_numEntries++ + _offset) % _entries.length;
_entries[slot] = entry;
entry.setIndex(slot);
_fieldMap.put(entry.getHttpField(), entry);
_nameMap.put(entry.getHttpField().getLowerCaseName(), entry);
if (QpackContext.LOG.isDebugEnabled())
QpackContext.LOG.debug(String.format("HdrTbl[%x] added %s", hashCode(), entry));
evict();
return entry;
}
public int index(Entry entry)
{
return StaticTable.STATIC_SIZE + _numEntries - (entry.getIndex() - _offset + _entries.length) % _entries.length;
}
public Entry get(int index)
{
int d = index - StaticTable.STATIC_SIZE - 1;
if (d < 0 || d >= _numEntries)
return null;
int slot = (_offset + _numEntries - d - 1) % _entries.length;
return _entries[slot];
}
public Entry get(String name)
{
return _nameMap.get(name);
}
public Entry get(HttpField field)
{
return _fieldMap.get(field);
}
public int getSize()
{
return _dynamicTableSizeInBytes;
}
public int getMaxSize()
{
return _maxDynamicTableSizeInBytes;
}
public int getNumEntries()
{
return _numEntries;
}
public void setCapacity(int capacity)
{
if (QpackContext.LOG.isDebugEnabled())
QpackContext.LOG.debug(String.format("HdrTbl[%x] resized max=%d->%d", hashCode(), _maxDynamicTableSizeInBytes, capacity));
_maxDynamicTableSizeInBytes = capacity;
evict();
}
private void evict()
{
while (_dynamicTableSizeInBytes > _maxDynamicTableSizeInBytes)
{
Entry entry = _entries[_offset];
_entries[_offset] = null;
_offset = (_offset + 1) % _entries.length;
_numEntries--;
if (QpackContext.LOG.isDebugEnabled())
QpackContext.LOG.debug(String.format("HdrTbl[%x] evict %s", hashCode(), entry));
_dynamicTableSizeInBytes -= entry.getSize();
entry.setIndex(-1);
_fieldMap.remove(entry.getHttpField());
String lc = entry.getHttpField().getLowerCaseName();
if (entry == _nameMap.get(lc))
_nameMap.remove(lc);
}
if (QpackContext.LOG.isDebugEnabled())
QpackContext.LOG.debug(String.format("HdrTbl[%x] entries=%d, size=%d, max=%d", hashCode(), getNumEntries(), _dynamicTableSizeInBytes, _maxDynamicTableSizeInBytes));
}
private void evictAll()
{
if (QpackContext.LOG.isDebugEnabled())
QpackContext.LOG.debug(String.format("HdrTbl[%x] evictAll", hashCode()));
if (getNumEntries() > 0)
{
_fieldMap.clear();
_nameMap.clear();
_offset = 0;
_numEntries = 0;
_dynamicTableSizeInBytes = 0;
Arrays.fill(_entries, null);
}
}
@Override
public String toString()
{
return String.format("%s@%x{entries=%d,size=%d,max=%d}", getClass().getSimpleName(), hashCode(), getNumEntries(), _dynamicTableSizeInBytes, _maxDynamicTableSizeInBytes);
}
}

View File

@ -0,0 +1,62 @@
package org.eclipse.jetty.http3.qpack.table;
import org.eclipse.jetty.http.HttpField;
public class Entry
{
final HttpField _field;
private int _slot; // The index within it's array
public Entry()
{
this(-1, null);
}
public Entry(HttpField field)
{
this(-1, field);
}
public Entry(int index, HttpField field)
{
_field = field;
_slot = index;
}
public int getSize()
{
String value = _field.getValue();
return 32 + _field.getName().length() + (value == null ? 0 : value.length());
}
public void setIndex(int index)
{
_slot = index;
}
public int getIndex()
{
return _slot;
}
public HttpField getHttpField()
{
return _field;
}
public boolean isStatic()
{
return false;
}
public byte[] getStaticHuffmanValue()
{
return null;
}
@Override
public String toString()
{
return String.format("{%s,%d,%s,%x}", isStatic() ? "S" : "D", _slot, _field, hashCode());
}
}

View File

@ -0,0 +1,56 @@
package org.eclipse.jetty.http3.qpack.table;
import java.nio.ByteBuffer;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http3.qpack.Huffman;
import org.eclipse.jetty.http3.qpack.NBitInteger;
public class StaticEntry extends Entry
{
private final byte[] _huffmanValue;
private final byte _encodedField;
StaticEntry(int index, HttpField field)
{
super(index, field);
String value = field.getValue();
if (value != null && value.length() > 0)
{
int huffmanLen = Huffman.octetsNeeded(value);
if (huffmanLen < 0)
throw new IllegalStateException("bad value");
int lenLen = NBitInteger.octectsNeeded(7, huffmanLen);
_huffmanValue = new byte[1 + lenLen + huffmanLen];
ByteBuffer buffer = ByteBuffer.wrap(_huffmanValue);
// Indicate Huffman
buffer.put((byte)0x80);
// Add huffman length
NBitInteger.encode(buffer, 7, huffmanLen);
// Encode value
Huffman.encode(buffer, value);
}
else
_huffmanValue = null;
_encodedField = (byte)(0x80 | index);
}
@Override
public boolean isStatic()
{
return true;
}
@Override
public byte[] getStaticHuffmanValue()
{
return _huffmanValue;
}
public byte getEncodedField()
{
return _encodedField;
}
}

View File

@ -1,4 +1,4 @@
package org.eclipse.jetty.http3.qpack;
package org.eclipse.jetty.http3.qpack.table;
import java.util.HashMap;
import java.util.HashSet;
@ -9,6 +9,7 @@ import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.http3.qpack.StaticTableHttpField;
import org.eclipse.jetty.util.Index;
public class StaticTable
@ -82,18 +83,18 @@ public class StaticTable
public static final int STATIC_SIZE = STATIC_TABLE.length - 1;
private final Map<HttpField, QpackContext.Entry> __staticFieldMap = new HashMap<>();
private final Index<QpackContext.StaticEntry> __staticNameMap;
private final QpackContext.StaticEntry[] __staticTableByHeader = new QpackContext.StaticEntry[HttpHeader.values().length];
private final QpackContext.StaticEntry[] __staticTable = new QpackContext.StaticEntry[STATIC_TABLE.length];
private final Map<HttpField, Entry> __staticFieldMap = new HashMap<>();
private final Index<StaticEntry> __staticNameMap;
private final StaticEntry[] __staticTableByHeader = new StaticEntry[HttpHeader.values().length];
private final StaticEntry[] __staticTable = new StaticEntry[STATIC_TABLE.length];
public StaticTable()
{
Index.Builder<QpackContext.StaticEntry> staticNameMapBuilder = new Index.Builder<QpackContext.StaticEntry>().caseSensitive(false);
Index.Builder<StaticEntry> staticNameMapBuilder = new Index.Builder<StaticEntry>().caseSensitive(false);
Set<String> added = new HashSet<>();
for (int i = 1; i < STATIC_TABLE.length; i++)
{
QpackContext.StaticEntry entry = null;
StaticEntry entry = null;
String name = STATIC_TABLE[i][0];
String value = STATIC_TABLE[i][1];
@ -107,7 +108,7 @@ public class StaticTable
HttpMethod method = HttpMethod.CACHE.get(value);
if (method != null)
entry = new QpackContext.StaticEntry(i, new StaticTableHttpField(header, name, value, method));
entry = new StaticEntry(i, new StaticTableHttpField(header, name, value, method));
break;
}
@ -116,13 +117,13 @@ public class StaticTable
HttpScheme scheme = HttpScheme.CACHE.get(value);
if (scheme != null)
entry = new QpackContext.StaticEntry(i, new StaticTableHttpField(header, name, value, scheme));
entry = new StaticEntry(i, new StaticTableHttpField(header, name, value, scheme));
break;
}
case C_STATUS:
{
entry = new QpackContext.StaticEntry(i, new StaticTableHttpField(header, name, value, value));
entry = new StaticEntry(i, new StaticTableHttpField(header, name, value, value));
break;
}
@ -132,7 +133,7 @@ public class StaticTable
}
if (entry == null)
entry = new QpackContext.StaticEntry(i, header == null ? new HttpField(STATIC_TABLE[i][0], value) : new HttpField(header, name, value));
entry = new StaticEntry(i, header == null ? new HttpField(STATIC_TABLE[i][0], value) : new HttpField(header, name, value));
__staticTable[i] = entry;
@ -149,30 +150,30 @@ public class StaticTable
for (HttpHeader h : HttpHeader.values())
{
QpackContext.StaticEntry entry = __staticNameMap.get(h.asString());
StaticEntry entry = __staticNameMap.get(h.asString());
if (entry != null)
__staticTableByHeader[h.ordinal()] = entry;
}
}
public QpackContext.Entry get(HttpField field)
public Entry get(HttpField field)
{
return __staticFieldMap.get(field);
}
public QpackContext.Entry get(String name)
public Entry get(String name)
{
return __staticNameMap.get(name);
}
public QpackContext.Entry get(int index)
public Entry get(int index)
{
if (index >= __staticTable.length)
return null;
return __staticTable[index];
}
public QpackContext.Entry get(HttpHeader header)
public Entry get(HttpHeader header)
{
int index = header.ordinal();
if (index >= __staticTableByHeader.length)

View File

@ -16,7 +16,7 @@ package org.eclipse.jetty.http3.qpack;
import java.nio.ByteBuffer;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http3.qpack.QpackContext.Entry;
import org.eclipse.jetty.http3.qpack.table.Entry;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;
@ -197,7 +197,7 @@ public class QpackContextTest
Entry[] entry = new Entry[100];
// Lookup the index of a static field
assertEquals(0, ctx.size());
assertEquals(0, ctx.getNumEntries());
assertEquals(":authority", ctx.get(1).getHttpField().getName());
assertEquals(3, ctx.index(ctx.get(methodPost)));
assertEquals(methodPost, ctx.get(3).getHttpField());
@ -208,7 +208,7 @@ public class QpackContextTest
entry[0] = ctx.add(field[0]);
// Check new entry is 62
assertEquals(1, ctx.size());
assertEquals(1, ctx.getNumEntries());
assertEquals(62, ctx.index(entry[0]));
assertEquals(entry[0], ctx.get(62));
@ -217,7 +217,7 @@ public class QpackContextTest
assertEquals(3, ctx.index(ctx.get(methodPost)));
assertEquals(methodPost, ctx.get(3).getHttpField());
assertEquals("www-authenticate", ctx.get(61).getHttpField().getName());
assertEquals(null, ctx.get(62 + ctx.size()));
assertEquals(null, ctx.get(62 + ctx.getNumEntries()));
// Add 4 more entries
for (int i = 1; i <= 4; i++)
@ -226,7 +226,7 @@ public class QpackContextTest
}
// Check newest entry is at 62 oldest at 66
assertEquals(5, ctx.size());
assertEquals(5, ctx.getNumEntries());
int index = 66;
for (int i = 0; i <= 4; i++)
{
@ -240,7 +240,7 @@ public class QpackContextTest
assertEquals(3, ctx.index(ctx.get(methodPost)));
assertEquals(methodPost, ctx.get(3).getHttpField());
assertEquals("www-authenticate", ctx.get(61).getHttpField().getName());
assertEquals(null, ctx.get(62 + ctx.size()));
assertEquals(null, ctx.get(62 + ctx.getNumEntries()));
// add 1 more entry and this should cause an eviction!
entry[5] = ctx.add(field[5]);
@ -262,7 +262,7 @@ public class QpackContextTest
assertEquals(3, ctx.index(ctx.get(methodPost)));
assertEquals(methodPost, ctx.get(3).getHttpField());
assertEquals("www-authenticate", ctx.get(61).getHttpField().getName());
assertEquals(null, ctx.get(62 + ctx.size()));
assertEquals(null, ctx.get(62 + ctx.getNumEntries()));
// Add 4 more entries
for (int i = 6; i <= 9; i++)
@ -328,7 +328,7 @@ public class QpackContextTest
entry[i] = ctx.add(field[i]);
}
assertEquals(5, ctx.size());
assertEquals(5, ctx.getNumEntries());
// check indexes
int index = 66;
@ -341,7 +341,7 @@ public class QpackContextTest
// resize so that only 2 entries may be held
ctx.resize(38 * 2);
assertEquals(2, ctx.size());
assertEquals(2, ctx.getNumEntries());
// check indexes
index = 63;
@ -354,7 +354,7 @@ public class QpackContextTest
// resize so that 6.5 entries may be held
ctx.resize(38 * 6 + 19);
assertEquals(2, ctx.size());
assertEquals(2, ctx.getNumEntries());
// check indexes
index = 63;
@ -371,7 +371,7 @@ public class QpackContextTest
entry[i] = ctx.add(field[i]);
}
assertEquals(6, ctx.size());
assertEquals(6, ctx.getNumEntries());
// check indexes
index = 67;
@ -384,7 +384,7 @@ public class QpackContextTest
// resize so that only 100 entries may be held
ctx.resize(38 * 100);
assertEquals(6, ctx.size());
assertEquals(6, ctx.getNumEntries());
// check indexes
index = 67;
for (int i = 4; i <= 9; i++)

View File

@ -209,7 +209,7 @@ public class QpackDecoderTest
assertThat(metaData.getFields().get(HttpHeader.HOST), is("localhost0"));
assertThat(metaData.getFields().get(HttpHeader.COOKIE), is("abcdefghij"));
assertThat(decoder.getQpackContext().getMaxDynamicTableSize(), is(50));
assertThat(decoder.getQpackContext().size(), is(1));
assertThat(decoder.getQpackContext().getNumEntries(), is(1));
}
@Test

View File

@ -20,6 +20,7 @@ import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.http3.qpack.table.StaticTable;
import org.eclipse.jetty.util.BufferUtil;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;
@ -67,7 +68,7 @@ public class QpackEncoderTest
assertThat(buffer.remaining(), Matchers.greaterThan(0));
// All are in the dynamic table
assertEquals(4, encoder.getQpackContext().size());
assertEquals(4, encoder.getQpackContext().getNumEntries());
// encode exact same fields again!
BufferUtil.clearToFill(buffer);
@ -75,7 +76,7 @@ public class QpackEncoderTest
BufferUtil.flipToFlush(buffer, 0);
// All are in the dynamic table
assertEquals(4, encoder.getQpackContext().size());
assertEquals(4, encoder.getQpackContext().getNumEntries());
// Add 4 more fields
for (int i = 4; i <= 7; i++)
@ -92,7 +93,7 @@ public class QpackEncoderTest
assertThat(buffer.remaining(), Matchers.greaterThan(0));
// max dynamic table size reached
assertEquals(5, encoder.getQpackContext().size());
assertEquals(5, encoder.getQpackContext().getNumEntries());
// remove some fields
for (int i = 0; i <= 7; i += 2)
@ -109,7 +110,7 @@ public class QpackEncoderTest
assertThat(buffer.remaining(), Matchers.greaterThan(0));
// max dynamic table size reached
assertEquals(5, encoder.getQpackContext().size());
assertEquals(5, encoder.getQpackContext().getNumEntries());
// remove another fields
fields.remove(field[1].getName());
@ -123,7 +124,7 @@ public class QpackEncoderTest
assertThat(buffer.remaining(), Matchers.greaterThan(0));
// max dynamic table size reached
assertEquals(5, encoder.getQpackContext().size());
assertEquals(5, encoder.getQpackContext().getNumEntries());
// re add the field
@ -138,7 +139,7 @@ public class QpackEncoderTest
assertThat(buffer.remaining(), Matchers.greaterThan(0));
// max dynamic table size reached
assertEquals(5, encoder.getQpackContext().size());
assertEquals(5, encoder.getQpackContext().getNumEntries());
}
@Test
@ -207,7 +208,7 @@ public class QpackEncoderTest
assertThat(buffer.remaining(), Matchers.greaterThan(0));
// empty dynamic table
assertEquals(0, encoder.getQpackContext().size());
assertEquals(0, encoder.getQpackContext().getNumEntries());
// encode again
BufferUtil.clearToFill(buffer);
@ -218,7 +219,7 @@ public class QpackEncoderTest
assertThat(buffer.remaining(), Matchers.greaterThan(0));
// empty dynamic table
assertEquals(0, encoder.getQpackContext().size());
assertEquals(0, encoder.getQpackContext().getNumEntries());
}
@Test
@ -278,7 +279,7 @@ public class QpackEncoderTest
// Only first and third fields are put in the table
QpackContext context = encoder.getQpackContext();
assertThat(context.size(), equalTo(2));
assertThat(context.getNumEntries(), equalTo(2));
assertThat(context.get(StaticTable.STATIC_SIZE + 1).getHttpField().getName(), equalTo("host"));
assertThat(context.get(StaticTable.STATIC_SIZE + 2).getHttpField().getName(), equalTo("user-agent"));
assertThat(context.getDynamicTableSize(), equalTo(
@ -304,6 +305,6 @@ public class QpackEncoderTest
QpackContext context = encoder.getQpackContext();
assertThat(context.getMaxDynamicTableSize(), Matchers.is(50));
assertThat(context.size(), Matchers.is(1));
assertThat(context.getNumEntries(), Matchers.is(1));
}
}

View File

@ -24,6 +24,7 @@ import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.http.MetaData.Response;
import org.eclipse.jetty.http.PreEncodedHttpField;
import org.eclipse.jetty.http3.qpack.table.DynamicTable;
import org.eclipse.jetty.util.BufferUtil;
import org.junit.jupiter.api.Test;
@ -174,10 +175,10 @@ public class QpackTest
BufferUtil.flipToFlush(buffer, 0);
MetaData decoded0 = decoder.decode(buffer);
assertEquals(2, encoder.getQpackContext().size());
assertEquals(2, decoder.getQpackContext().size());
assertEquals(longEnoughToBeEvicted, encoder.getQpackContext().get(StaticTable.STATIC_TABLE.length + 1).getHttpField().getName());
assertEquals("foo", encoder.getQpackContext().get(StaticTable.STATIC_TABLE.length).getHttpField().getName());
assertEquals(2, encoder.getQpackContext().getNumEntries());
assertEquals(2, decoder.getQpackContext().getNumEntries());
assertEquals(longEnoughToBeEvicted, encoder.getQpackContext().get(DynamicTable.FIRST_INDEX + 1).getHttpField().getName());
assertEquals("foo", encoder.getQpackContext().get(DynamicTable.FIRST_INDEX).getHttpField().getName());
assertMetaDataSame(original0, decoded0);
@ -192,10 +193,10 @@ public class QpackTest
MetaData decoded1 = decoder.decode(buffer);
assertMetaDataSame(original1, decoded1);
assertEquals(2, encoder.getQpackContext().size());
assertEquals(2, decoder.getQpackContext().size());
assertEquals("x", encoder.getQpackContext().get(StaticTable.STATIC_TABLE.length).getHttpField().getName());
assertEquals("foo", encoder.getQpackContext().get(StaticTable.STATIC_TABLE.length + 1).getHttpField().getName());
assertEquals(2, encoder.getQpackContext().getNumEntries());
assertEquals(2, decoder.getQpackContext().getNumEntries());
assertEquals("x", encoder.getQpackContext().get(DynamicTable.FIRST_INDEX).getHttpField().getName());
assertEquals("foo", encoder.getQpackContext().get(DynamicTable.FIRST_INDEX + 1).getHttpField().getName());
}
@Test