Decouple the DynamicTable from the QpackContext.
Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
This commit is contained in:
parent
5d07842af8
commit
f39313b1d5
|
@ -17,6 +17,7 @@ import org.eclipse.jetty.http3.qpack.QpackFieldPreEncoder;
|
||||||
module org.eclipse.jetty.http3.qpack
|
module org.eclipse.jetty.http3.qpack
|
||||||
{
|
{
|
||||||
exports 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 transitive org.eclipse.jetty.http;
|
||||||
requires org.slf4j;
|
requires org.slf4j;
|
||||||
|
|
|
@ -15,6 +15,7 @@ package org.eclipse.jetty.http3.qpack;
|
||||||
|
|
||||||
import org.eclipse.jetty.http.HostPortHttpField;
|
import org.eclipse.jetty.http.HostPortHttpField;
|
||||||
import org.eclipse.jetty.http.HttpHeader;
|
import org.eclipse.jetty.http.HttpHeader;
|
||||||
|
import org.eclipse.jetty.http3.qpack.table.StaticTable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|
|
@ -13,13 +13,11 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.http3.qpack;
|
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.HttpField;
|
||||||
import org.eclipse.jetty.http.HttpHeader;
|
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.eclipse.jetty.util.StringUtil;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -36,35 +34,23 @@ public class QpackContext
|
||||||
{
|
{
|
||||||
public static final Logger LOG = LoggerFactory.getLogger(QpackContext.class);
|
public static final Logger LOG = LoggerFactory.getLogger(QpackContext.class);
|
||||||
private static final StaticTable __staticTable = new StaticTable();
|
private static final StaticTable __staticTable = new StaticTable();
|
||||||
|
|
||||||
private final DynamicTable _dynamicTable;
|
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)
|
QpackContext(int maxDynamicTableSize)
|
||||||
{
|
{
|
||||||
_maxDynamicTableSizeInBytes = maxDynamicTableSize;
|
_dynamicTable = new DynamicTable(maxDynamicTableSize);
|
||||||
int guesstimateEntries = 10 + maxDynamicTableSize / (32 + 10 + 10);
|
|
||||||
_dynamicTable = new DynamicTable(guesstimateEntries);
|
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug(String.format("HdrTbl[%x] created max=%d", hashCode(), maxDynamicTableSize));
|
LOG.debug(String.format("HdrTbl[%x] created max=%d", hashCode(), maxDynamicTableSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void resize(int newMaxDynamicTableSize)
|
public void resize(int newMaxDynamicTableSize)
|
||||||
{
|
{
|
||||||
if (LOG.isDebugEnabled())
|
_dynamicTable.setCapacity(newMaxDynamicTableSize);
|
||||||
LOG.debug(String.format("HdrTbl[%x] resized max=%d->%d", hashCode(), _maxDynamicTableSizeInBytes, newMaxDynamicTableSize));
|
|
||||||
_maxDynamicTableSizeInBytes = newMaxDynamicTableSize;
|
|
||||||
_dynamicTable.evict();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Entry get(HttpField field)
|
public Entry get(HttpField field)
|
||||||
{
|
{
|
||||||
Entry entry = _fieldMap.get(field);
|
Entry entry = _dynamicTable.get(field);
|
||||||
if (entry == null)
|
if (entry == null)
|
||||||
entry = __staticTable.get(field);
|
entry = __staticTable.get(field);
|
||||||
return entry;
|
return entry;
|
||||||
|
@ -75,7 +61,7 @@ public class QpackContext
|
||||||
Entry entry = __staticTable.get(name);
|
Entry entry = __staticTable.get(name);
|
||||||
if (entry != null)
|
if (entry != null)
|
||||||
return entry;
|
return entry;
|
||||||
return _nameMap.get(StringUtil.asciiToLowerCase(name));
|
return _dynamicTable.get(StringUtil.asciiToLowerCase(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Entry get(int index)
|
public Entry get(int index)
|
||||||
|
@ -96,32 +82,15 @@ public class QpackContext
|
||||||
|
|
||||||
public Entry add(HttpField field)
|
public Entry add(HttpField field)
|
||||||
{
|
{
|
||||||
Entry entry = new Entry(field);
|
return _dynamicTable.add(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 Current dynamic table size in entries
|
* @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()
|
public int getDynamicTableSize()
|
||||||
{
|
{
|
||||||
return _dynamicTableSizeInBytes;
|
return _dynamicTable.getSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -137,19 +106,25 @@ public class QpackContext
|
||||||
*/
|
*/
|
||||||
public int getMaxDynamicTableSize()
|
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)
|
public int index(Entry entry)
|
||||||
{
|
{
|
||||||
if (entry._slot < 0)
|
if (entry.getIndex() < 0)
|
||||||
return 0;
|
return 0;
|
||||||
if (entry.isStatic())
|
if (entry.isStatic())
|
||||||
return entry._slot;
|
return entry.getIndex();
|
||||||
|
|
||||||
return _dynamicTable.index(entry);
|
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)
|
public static int staticIndex(HttpHeader header)
|
||||||
{
|
{
|
||||||
if (header == null)
|
if (header == null)
|
||||||
|
@ -157,194 +132,6 @@ public class QpackContext
|
||||||
Entry entry = __staticTable.get(header.asString());
|
Entry entry = __staticTable.get(header.asString());
|
||||||
if (entry == null)
|
if (entry == null)
|
||||||
return 0;
|
return 0;
|
||||||
return entry._slot;
|
return entry.getIndex();
|
||||||
}
|
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ import org.eclipse.jetty.http.HttpField;
|
||||||
import org.eclipse.jetty.http.HttpHeader;
|
import org.eclipse.jetty.http.HttpHeader;
|
||||||
import org.eclipse.jetty.http.HttpTokens;
|
import org.eclipse.jetty.http.HttpTokens;
|
||||||
import org.eclipse.jetty.http.MetaData;
|
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.eclipse.jetty.util.BufferUtil;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
|
@ -29,8 +29,8 @@ import org.eclipse.jetty.http.HttpStatus;
|
||||||
import org.eclipse.jetty.http.HttpVersion;
|
import org.eclipse.jetty.http.HttpVersion;
|
||||||
import org.eclipse.jetty.http.MetaData;
|
import org.eclipse.jetty.http.MetaData;
|
||||||
import org.eclipse.jetty.http.PreEncodedHttpField;
|
import org.eclipse.jetty.http.PreEncodedHttpField;
|
||||||
import org.eclipse.jetty.http3.qpack.QpackContext.Entry;
|
import org.eclipse.jetty.http3.qpack.table.Entry;
|
||||||
import org.eclipse.jetty.http3.qpack.QpackContext.StaticEntry;
|
import org.eclipse.jetty.http3.qpack.table.StaticEntry;
|
||||||
import org.eclipse.jetty.util.BufferUtil;
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
import org.eclipse.jetty.util.StringUtil;
|
import org.eclipse.jetty.util.StringUtil;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package org.eclipse.jetty.http3.qpack;
|
package org.eclipse.jetty.http3.qpack.table;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
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.HttpHeader;
|
||||||
import org.eclipse.jetty.http.HttpMethod;
|
import org.eclipse.jetty.http.HttpMethod;
|
||||||
import org.eclipse.jetty.http.HttpScheme;
|
import org.eclipse.jetty.http.HttpScheme;
|
||||||
|
import org.eclipse.jetty.http3.qpack.StaticTableHttpField;
|
||||||
import org.eclipse.jetty.util.Index;
|
import org.eclipse.jetty.util.Index;
|
||||||
|
|
||||||
public class StaticTable
|
public class StaticTable
|
||||||
|
@ -82,18 +83,18 @@ public class StaticTable
|
||||||
|
|
||||||
public static final int STATIC_SIZE = STATIC_TABLE.length - 1;
|
public static final int STATIC_SIZE = STATIC_TABLE.length - 1;
|
||||||
|
|
||||||
private final Map<HttpField, QpackContext.Entry> __staticFieldMap = new HashMap<>();
|
private final Map<HttpField, Entry> __staticFieldMap = new HashMap<>();
|
||||||
private final Index<QpackContext.StaticEntry> __staticNameMap;
|
private final Index<StaticEntry> __staticNameMap;
|
||||||
private final QpackContext.StaticEntry[] __staticTableByHeader = new QpackContext.StaticEntry[HttpHeader.values().length];
|
private final StaticEntry[] __staticTableByHeader = new StaticEntry[HttpHeader.values().length];
|
||||||
private final QpackContext.StaticEntry[] __staticTable = new QpackContext.StaticEntry[STATIC_TABLE.length];
|
private final StaticEntry[] __staticTable = new StaticEntry[STATIC_TABLE.length];
|
||||||
|
|
||||||
public StaticTable()
|
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<>();
|
Set<String> added = new HashSet<>();
|
||||||
for (int i = 1; i < STATIC_TABLE.length; i++)
|
for (int i = 1; i < STATIC_TABLE.length; i++)
|
||||||
{
|
{
|
||||||
QpackContext.StaticEntry entry = null;
|
StaticEntry entry = null;
|
||||||
|
|
||||||
String name = STATIC_TABLE[i][0];
|
String name = STATIC_TABLE[i][0];
|
||||||
String value = STATIC_TABLE[i][1];
|
String value = STATIC_TABLE[i][1];
|
||||||
|
@ -107,7 +108,7 @@ public class StaticTable
|
||||||
|
|
||||||
HttpMethod method = HttpMethod.CACHE.get(value);
|
HttpMethod method = HttpMethod.CACHE.get(value);
|
||||||
if (method != null)
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,13 +117,13 @@ public class StaticTable
|
||||||
|
|
||||||
HttpScheme scheme = HttpScheme.CACHE.get(value);
|
HttpScheme scheme = HttpScheme.CACHE.get(value);
|
||||||
if (scheme != null)
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case C_STATUS:
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,7 +133,7 @@ public class StaticTable
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry == null)
|
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;
|
__staticTable[i] = entry;
|
||||||
|
|
||||||
|
@ -149,30 +150,30 @@ public class StaticTable
|
||||||
|
|
||||||
for (HttpHeader h : HttpHeader.values())
|
for (HttpHeader h : HttpHeader.values())
|
||||||
{
|
{
|
||||||
QpackContext.StaticEntry entry = __staticNameMap.get(h.asString());
|
StaticEntry entry = __staticNameMap.get(h.asString());
|
||||||
if (entry != null)
|
if (entry != null)
|
||||||
__staticTableByHeader[h.ordinal()] = entry;
|
__staticTableByHeader[h.ordinal()] = entry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public QpackContext.Entry get(HttpField field)
|
public Entry get(HttpField field)
|
||||||
{
|
{
|
||||||
return __staticFieldMap.get(field);
|
return __staticFieldMap.get(field);
|
||||||
}
|
}
|
||||||
|
|
||||||
public QpackContext.Entry get(String name)
|
public Entry get(String name)
|
||||||
{
|
{
|
||||||
return __staticNameMap.get(name);
|
return __staticNameMap.get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public QpackContext.Entry get(int index)
|
public Entry get(int index)
|
||||||
{
|
{
|
||||||
if (index >= __staticTable.length)
|
if (index >= __staticTable.length)
|
||||||
return null;
|
return null;
|
||||||
return __staticTable[index];
|
return __staticTable[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
public QpackContext.Entry get(HttpHeader header)
|
public Entry get(HttpHeader header)
|
||||||
{
|
{
|
||||||
int index = header.ordinal();
|
int index = header.ordinal();
|
||||||
if (index >= __staticTableByHeader.length)
|
if (index >= __staticTableByHeader.length)
|
|
@ -16,7 +16,7 @@ package org.eclipse.jetty.http3.qpack;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
import org.eclipse.jetty.http.HttpField;
|
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.hamcrest.Matchers;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
@ -197,7 +197,7 @@ public class QpackContextTest
|
||||||
Entry[] entry = new Entry[100];
|
Entry[] entry = new Entry[100];
|
||||||
|
|
||||||
// Lookup the index of a static field
|
// Lookup the index of a static field
|
||||||
assertEquals(0, ctx.size());
|
assertEquals(0, ctx.getNumEntries());
|
||||||
assertEquals(":authority", ctx.get(1).getHttpField().getName());
|
assertEquals(":authority", ctx.get(1).getHttpField().getName());
|
||||||
assertEquals(3, ctx.index(ctx.get(methodPost)));
|
assertEquals(3, ctx.index(ctx.get(methodPost)));
|
||||||
assertEquals(methodPost, ctx.get(3).getHttpField());
|
assertEquals(methodPost, ctx.get(3).getHttpField());
|
||||||
|
@ -208,7 +208,7 @@ public class QpackContextTest
|
||||||
entry[0] = ctx.add(field[0]);
|
entry[0] = ctx.add(field[0]);
|
||||||
|
|
||||||
// Check new entry is 62
|
// Check new entry is 62
|
||||||
assertEquals(1, ctx.size());
|
assertEquals(1, ctx.getNumEntries());
|
||||||
assertEquals(62, ctx.index(entry[0]));
|
assertEquals(62, ctx.index(entry[0]));
|
||||||
assertEquals(entry[0], ctx.get(62));
|
assertEquals(entry[0], ctx.get(62));
|
||||||
|
|
||||||
|
@ -217,7 +217,7 @@ public class QpackContextTest
|
||||||
assertEquals(3, ctx.index(ctx.get(methodPost)));
|
assertEquals(3, ctx.index(ctx.get(methodPost)));
|
||||||
assertEquals(methodPost, ctx.get(3).getHttpField());
|
assertEquals(methodPost, ctx.get(3).getHttpField());
|
||||||
assertEquals("www-authenticate", ctx.get(61).getHttpField().getName());
|
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
|
// Add 4 more entries
|
||||||
for (int i = 1; i <= 4; i++)
|
for (int i = 1; i <= 4; i++)
|
||||||
|
@ -226,7 +226,7 @@ public class QpackContextTest
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check newest entry is at 62 oldest at 66
|
// Check newest entry is at 62 oldest at 66
|
||||||
assertEquals(5, ctx.size());
|
assertEquals(5, ctx.getNumEntries());
|
||||||
int index = 66;
|
int index = 66;
|
||||||
for (int i = 0; i <= 4; i++)
|
for (int i = 0; i <= 4; i++)
|
||||||
{
|
{
|
||||||
|
@ -240,7 +240,7 @@ public class QpackContextTest
|
||||||
assertEquals(3, ctx.index(ctx.get(methodPost)));
|
assertEquals(3, ctx.index(ctx.get(methodPost)));
|
||||||
assertEquals(methodPost, ctx.get(3).getHttpField());
|
assertEquals(methodPost, ctx.get(3).getHttpField());
|
||||||
assertEquals("www-authenticate", ctx.get(61).getHttpField().getName());
|
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!
|
// add 1 more entry and this should cause an eviction!
|
||||||
entry[5] = ctx.add(field[5]);
|
entry[5] = ctx.add(field[5]);
|
||||||
|
@ -262,7 +262,7 @@ public class QpackContextTest
|
||||||
assertEquals(3, ctx.index(ctx.get(methodPost)));
|
assertEquals(3, ctx.index(ctx.get(methodPost)));
|
||||||
assertEquals(methodPost, ctx.get(3).getHttpField());
|
assertEquals(methodPost, ctx.get(3).getHttpField());
|
||||||
assertEquals("www-authenticate", ctx.get(61).getHttpField().getName());
|
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
|
// Add 4 more entries
|
||||||
for (int i = 6; i <= 9; i++)
|
for (int i = 6; i <= 9; i++)
|
||||||
|
@ -328,7 +328,7 @@ public class QpackContextTest
|
||||||
entry[i] = ctx.add(field[i]);
|
entry[i] = ctx.add(field[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
assertEquals(5, ctx.size());
|
assertEquals(5, ctx.getNumEntries());
|
||||||
|
|
||||||
// check indexes
|
// check indexes
|
||||||
int index = 66;
|
int index = 66;
|
||||||
|
@ -341,7 +341,7 @@ public class QpackContextTest
|
||||||
|
|
||||||
// resize so that only 2 entries may be held
|
// resize so that only 2 entries may be held
|
||||||
ctx.resize(38 * 2);
|
ctx.resize(38 * 2);
|
||||||
assertEquals(2, ctx.size());
|
assertEquals(2, ctx.getNumEntries());
|
||||||
|
|
||||||
// check indexes
|
// check indexes
|
||||||
index = 63;
|
index = 63;
|
||||||
|
@ -354,7 +354,7 @@ public class QpackContextTest
|
||||||
|
|
||||||
// resize so that 6.5 entries may be held
|
// resize so that 6.5 entries may be held
|
||||||
ctx.resize(38 * 6 + 19);
|
ctx.resize(38 * 6 + 19);
|
||||||
assertEquals(2, ctx.size());
|
assertEquals(2, ctx.getNumEntries());
|
||||||
|
|
||||||
// check indexes
|
// check indexes
|
||||||
index = 63;
|
index = 63;
|
||||||
|
@ -371,7 +371,7 @@ public class QpackContextTest
|
||||||
entry[i] = ctx.add(field[i]);
|
entry[i] = ctx.add(field[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
assertEquals(6, ctx.size());
|
assertEquals(6, ctx.getNumEntries());
|
||||||
|
|
||||||
// check indexes
|
// check indexes
|
||||||
index = 67;
|
index = 67;
|
||||||
|
@ -384,7 +384,7 @@ public class QpackContextTest
|
||||||
|
|
||||||
// resize so that only 100 entries may be held
|
// resize so that only 100 entries may be held
|
||||||
ctx.resize(38 * 100);
|
ctx.resize(38 * 100);
|
||||||
assertEquals(6, ctx.size());
|
assertEquals(6, ctx.getNumEntries());
|
||||||
// check indexes
|
// check indexes
|
||||||
index = 67;
|
index = 67;
|
||||||
for (int i = 4; i <= 9; i++)
|
for (int i = 4; i <= 9; i++)
|
||||||
|
|
|
@ -209,7 +209,7 @@ public class QpackDecoderTest
|
||||||
assertThat(metaData.getFields().get(HttpHeader.HOST), is("localhost0"));
|
assertThat(metaData.getFields().get(HttpHeader.HOST), is("localhost0"));
|
||||||
assertThat(metaData.getFields().get(HttpHeader.COOKIE), is("abcdefghij"));
|
assertThat(metaData.getFields().get(HttpHeader.COOKIE), is("abcdefghij"));
|
||||||
assertThat(decoder.getQpackContext().getMaxDynamicTableSize(), is(50));
|
assertThat(decoder.getQpackContext().getMaxDynamicTableSize(), is(50));
|
||||||
assertThat(decoder.getQpackContext().size(), is(1));
|
assertThat(decoder.getQpackContext().getNumEntries(), is(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -20,6 +20,7 @@ import org.eclipse.jetty.http.HttpFields;
|
||||||
import org.eclipse.jetty.http.HttpHeader;
|
import org.eclipse.jetty.http.HttpHeader;
|
||||||
import org.eclipse.jetty.http.HttpVersion;
|
import org.eclipse.jetty.http.HttpVersion;
|
||||||
import org.eclipse.jetty.http.MetaData;
|
import org.eclipse.jetty.http.MetaData;
|
||||||
|
import org.eclipse.jetty.http3.qpack.table.StaticTable;
|
||||||
import org.eclipse.jetty.util.BufferUtil;
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
@ -67,7 +68,7 @@ public class QpackEncoderTest
|
||||||
assertThat(buffer.remaining(), Matchers.greaterThan(0));
|
assertThat(buffer.remaining(), Matchers.greaterThan(0));
|
||||||
|
|
||||||
// All are in the dynamic table
|
// All are in the dynamic table
|
||||||
assertEquals(4, encoder.getQpackContext().size());
|
assertEquals(4, encoder.getQpackContext().getNumEntries());
|
||||||
|
|
||||||
// encode exact same fields again!
|
// encode exact same fields again!
|
||||||
BufferUtil.clearToFill(buffer);
|
BufferUtil.clearToFill(buffer);
|
||||||
|
@ -75,7 +76,7 @@ public class QpackEncoderTest
|
||||||
BufferUtil.flipToFlush(buffer, 0);
|
BufferUtil.flipToFlush(buffer, 0);
|
||||||
|
|
||||||
// All are in the dynamic table
|
// All are in the dynamic table
|
||||||
assertEquals(4, encoder.getQpackContext().size());
|
assertEquals(4, encoder.getQpackContext().getNumEntries());
|
||||||
|
|
||||||
// Add 4 more fields
|
// Add 4 more fields
|
||||||
for (int i = 4; i <= 7; i++)
|
for (int i = 4; i <= 7; i++)
|
||||||
|
@ -92,7 +93,7 @@ public class QpackEncoderTest
|
||||||
assertThat(buffer.remaining(), Matchers.greaterThan(0));
|
assertThat(buffer.remaining(), Matchers.greaterThan(0));
|
||||||
|
|
||||||
// max dynamic table size reached
|
// max dynamic table size reached
|
||||||
assertEquals(5, encoder.getQpackContext().size());
|
assertEquals(5, encoder.getQpackContext().getNumEntries());
|
||||||
|
|
||||||
// remove some fields
|
// remove some fields
|
||||||
for (int i = 0; i <= 7; i += 2)
|
for (int i = 0; i <= 7; i += 2)
|
||||||
|
@ -109,7 +110,7 @@ public class QpackEncoderTest
|
||||||
assertThat(buffer.remaining(), Matchers.greaterThan(0));
|
assertThat(buffer.remaining(), Matchers.greaterThan(0));
|
||||||
|
|
||||||
// max dynamic table size reached
|
// max dynamic table size reached
|
||||||
assertEquals(5, encoder.getQpackContext().size());
|
assertEquals(5, encoder.getQpackContext().getNumEntries());
|
||||||
|
|
||||||
// remove another fields
|
// remove another fields
|
||||||
fields.remove(field[1].getName());
|
fields.remove(field[1].getName());
|
||||||
|
@ -123,7 +124,7 @@ public class QpackEncoderTest
|
||||||
assertThat(buffer.remaining(), Matchers.greaterThan(0));
|
assertThat(buffer.remaining(), Matchers.greaterThan(0));
|
||||||
|
|
||||||
// max dynamic table size reached
|
// max dynamic table size reached
|
||||||
assertEquals(5, encoder.getQpackContext().size());
|
assertEquals(5, encoder.getQpackContext().getNumEntries());
|
||||||
|
|
||||||
// re add the field
|
// re add the field
|
||||||
|
|
||||||
|
@ -138,7 +139,7 @@ public class QpackEncoderTest
|
||||||
assertThat(buffer.remaining(), Matchers.greaterThan(0));
|
assertThat(buffer.remaining(), Matchers.greaterThan(0));
|
||||||
|
|
||||||
// max dynamic table size reached
|
// max dynamic table size reached
|
||||||
assertEquals(5, encoder.getQpackContext().size());
|
assertEquals(5, encoder.getQpackContext().getNumEntries());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -207,7 +208,7 @@ public class QpackEncoderTest
|
||||||
assertThat(buffer.remaining(), Matchers.greaterThan(0));
|
assertThat(buffer.remaining(), Matchers.greaterThan(0));
|
||||||
|
|
||||||
// empty dynamic table
|
// empty dynamic table
|
||||||
assertEquals(0, encoder.getQpackContext().size());
|
assertEquals(0, encoder.getQpackContext().getNumEntries());
|
||||||
|
|
||||||
// encode again
|
// encode again
|
||||||
BufferUtil.clearToFill(buffer);
|
BufferUtil.clearToFill(buffer);
|
||||||
|
@ -218,7 +219,7 @@ public class QpackEncoderTest
|
||||||
assertThat(buffer.remaining(), Matchers.greaterThan(0));
|
assertThat(buffer.remaining(), Matchers.greaterThan(0));
|
||||||
|
|
||||||
// empty dynamic table
|
// empty dynamic table
|
||||||
assertEquals(0, encoder.getQpackContext().size());
|
assertEquals(0, encoder.getQpackContext().getNumEntries());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -278,7 +279,7 @@ public class QpackEncoderTest
|
||||||
|
|
||||||
// Only first and third fields are put in the table
|
// Only first and third fields are put in the table
|
||||||
QpackContext context = encoder.getQpackContext();
|
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 + 1).getHttpField().getName(), equalTo("host"));
|
||||||
assertThat(context.get(StaticTable.STATIC_SIZE + 2).getHttpField().getName(), equalTo("user-agent"));
|
assertThat(context.get(StaticTable.STATIC_SIZE + 2).getHttpField().getName(), equalTo("user-agent"));
|
||||||
assertThat(context.getDynamicTableSize(), equalTo(
|
assertThat(context.getDynamicTableSize(), equalTo(
|
||||||
|
@ -304,6 +305,6 @@ public class QpackEncoderTest
|
||||||
QpackContext context = encoder.getQpackContext();
|
QpackContext context = encoder.getQpackContext();
|
||||||
|
|
||||||
assertThat(context.getMaxDynamicTableSize(), Matchers.is(50));
|
assertThat(context.getMaxDynamicTableSize(), Matchers.is(50));
|
||||||
assertThat(context.size(), Matchers.is(1));
|
assertThat(context.getNumEntries(), Matchers.is(1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import org.eclipse.jetty.http.HttpVersion;
|
||||||
import org.eclipse.jetty.http.MetaData;
|
import org.eclipse.jetty.http.MetaData;
|
||||||
import org.eclipse.jetty.http.MetaData.Response;
|
import org.eclipse.jetty.http.MetaData.Response;
|
||||||
import org.eclipse.jetty.http.PreEncodedHttpField;
|
import org.eclipse.jetty.http.PreEncodedHttpField;
|
||||||
|
import org.eclipse.jetty.http3.qpack.table.DynamicTable;
|
||||||
import org.eclipse.jetty.util.BufferUtil;
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
@ -174,10 +175,10 @@ public class QpackTest
|
||||||
BufferUtil.flipToFlush(buffer, 0);
|
BufferUtil.flipToFlush(buffer, 0);
|
||||||
MetaData decoded0 = decoder.decode(buffer);
|
MetaData decoded0 = decoder.decode(buffer);
|
||||||
|
|
||||||
assertEquals(2, encoder.getQpackContext().size());
|
assertEquals(2, encoder.getQpackContext().getNumEntries());
|
||||||
assertEquals(2, decoder.getQpackContext().size());
|
assertEquals(2, decoder.getQpackContext().getNumEntries());
|
||||||
assertEquals(longEnoughToBeEvicted, encoder.getQpackContext().get(StaticTable.STATIC_TABLE.length + 1).getHttpField().getName());
|
assertEquals(longEnoughToBeEvicted, encoder.getQpackContext().get(DynamicTable.FIRST_INDEX + 1).getHttpField().getName());
|
||||||
assertEquals("foo", encoder.getQpackContext().get(StaticTable.STATIC_TABLE.length).getHttpField().getName());
|
assertEquals("foo", encoder.getQpackContext().get(DynamicTable.FIRST_INDEX).getHttpField().getName());
|
||||||
|
|
||||||
assertMetaDataSame(original0, decoded0);
|
assertMetaDataSame(original0, decoded0);
|
||||||
|
|
||||||
|
@ -192,10 +193,10 @@ public class QpackTest
|
||||||
MetaData decoded1 = decoder.decode(buffer);
|
MetaData decoded1 = decoder.decode(buffer);
|
||||||
assertMetaDataSame(original1, decoded1);
|
assertMetaDataSame(original1, decoded1);
|
||||||
|
|
||||||
assertEquals(2, encoder.getQpackContext().size());
|
assertEquals(2, encoder.getQpackContext().getNumEntries());
|
||||||
assertEquals(2, decoder.getQpackContext().size());
|
assertEquals(2, decoder.getQpackContext().getNumEntries());
|
||||||
assertEquals("x", encoder.getQpackContext().get(StaticTable.STATIC_TABLE.length).getHttpField().getName());
|
assertEquals("x", encoder.getQpackContext().get(DynamicTable.FIRST_INDEX).getHttpField().getName());
|
||||||
assertEquals("foo", encoder.getQpackContext().get(StaticTable.STATIC_TABLE.length + 1).getHttpField().getName());
|
assertEquals("foo", encoder.getQpackContext().get(DynamicTable.FIRST_INDEX + 1).getHttpField().getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
Loading…
Reference in New Issue