encoded static field
This commit is contained in:
parent
ae4dea3e1e
commit
9e639d9236
|
@ -18,6 +18,7 @@
|
|||
|
||||
package org.eclipse.jetty.hpack;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
|
@ -109,7 +110,7 @@ public class HpackContext
|
|||
Set<String> added = new HashSet<>();
|
||||
for (int i=1;i<STATIC_TABLE.length;i++)
|
||||
{
|
||||
Entry entry=__staticTable[i]=new Entry(i,STATIC_TABLE[i][0],STATIC_TABLE[i][1],true);
|
||||
Entry entry=__staticTable[i]=new StaticEntry(i,STATIC_TABLE[i][0],STATIC_TABLE[i][1]);
|
||||
if (entry._field.getValue()!=null)
|
||||
__staticFieldMap.put(entry._field,entry);
|
||||
if (!added.contains(entry._field.getName()))
|
||||
|
@ -122,7 +123,7 @@ public class HpackContext
|
|||
|
||||
private int _maxHeaderTableSizeInBytes;
|
||||
private int _headerTableSizeInBytes;
|
||||
private final Entry _refSet=new Entry(true);
|
||||
private final Entry _refSet=new Entry();
|
||||
private final HeaderTable _headerTable;
|
||||
private final Map<HttpField,Entry> _fieldMap = new HashMap<>();
|
||||
private final Map<String,Entry> _nameMap = new HashMap<>();
|
||||
|
@ -183,7 +184,7 @@ public class HpackContext
|
|||
public Entry add(HttpField field)
|
||||
{
|
||||
int i=_headerTable.getNextIndexUnsafe();
|
||||
Entry entry=new Entry(i,field,false);
|
||||
Entry entry=new Entry(i,field);
|
||||
int size = entry.getSize();
|
||||
if (size>_maxHeaderTableSizeInBytes)
|
||||
return null;
|
||||
|
@ -220,6 +221,17 @@ public class HpackContext
|
|||
return referenceSet;
|
||||
}
|
||||
|
||||
public void clearReferenceSet()
|
||||
{
|
||||
Entry entry = _refSet._refSetNext;
|
||||
while(entry!=_refSet)
|
||||
{
|
||||
Entry next = entry._refSetNext;
|
||||
entry.removeFromRefSet();
|
||||
entry=next;
|
||||
}
|
||||
}
|
||||
|
||||
public Iterator<Entry> iterateReferenceSet()
|
||||
{
|
||||
return new Iterator<Entry>()
|
||||
|
@ -339,36 +351,32 @@ public class HpackContext
|
|||
public static class Entry
|
||||
{
|
||||
int _index;
|
||||
final boolean _static;
|
||||
final HttpField _field;
|
||||
Entry _refSetNext=this;
|
||||
Entry _refSetPrev=this;
|
||||
boolean _refSetUsed;
|
||||
|
||||
Entry(boolean isStatic)
|
||||
Entry()
|
||||
{
|
||||
_static=isStatic;
|
||||
_index=0;
|
||||
_field=null;
|
||||
}
|
||||
|
||||
Entry(int index,String name, String value, boolean isStatic)
|
||||
Entry(int index,String name, String value)
|
||||
{
|
||||
_static=isStatic;
|
||||
_index=index;
|
||||
_field=new HttpField(name,value);
|
||||
}
|
||||
|
||||
Entry(int index, HttpField field, boolean isStatic)
|
||||
Entry(int index, HttpField field)
|
||||
{
|
||||
_static=isStatic;
|
||||
_index=index;
|
||||
_field=field;
|
||||
}
|
||||
|
||||
private void addToRefSet(HpackContext ctx)
|
||||
{
|
||||
if (_static)
|
||||
if (isStatic())
|
||||
throw new IllegalStateException("static");
|
||||
if (_index<0)
|
||||
throw new IllegalStateException("evicted");
|
||||
|
@ -380,6 +388,11 @@ public class HpackContext
|
|||
ctx._refSet._refSetPrev._refSetNext=this;
|
||||
ctx._refSet._refSetPrev=this;
|
||||
}
|
||||
|
||||
public boolean isInReferenceSet()
|
||||
{
|
||||
return _refSetNext!=this;
|
||||
}
|
||||
|
||||
public void removeFromRefSet()
|
||||
{
|
||||
|
@ -397,25 +410,63 @@ public class HpackContext
|
|||
return 32+_field.getName().length()+_field.getValue().length();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public HttpField getHttpField()
|
||||
{
|
||||
return _field;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
*/
|
||||
public boolean isStatic()
|
||||
{
|
||||
return _static;
|
||||
return false;
|
||||
}
|
||||
|
||||
public byte[] getStaticHuffmanValue()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return String.format("{%s,%d,%s,%x}",_static?"S":"D",_index,_field,hashCode());
|
||||
return String.format("{%s,%d,%s,%x}",isStatic()?"S":"D",_index,_field,hashCode());
|
||||
}
|
||||
}
|
||||
|
||||
public static class StaticEntry extends Entry
|
||||
{
|
||||
final byte[] _huffmanValue;
|
||||
|
||||
StaticEntry(int index,String name, String value)
|
||||
{
|
||||
super(index,name,value);
|
||||
if (value!=null && value.length()>0)
|
||||
{
|
||||
int huffmanLen = Huffman.octetsNeeded(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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStatic()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getStaticHuffmanValue()
|
||||
{
|
||||
return _huffmanValue;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,20 +19,68 @@
|
|||
|
||||
package org.eclipse.jetty.hpack;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.eclipse.jetty.hpack.HpackContext.Entry;
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
|
||||
public class HpackEncoder
|
||||
{
|
||||
private final ByteBufferPool byteBufferPool;
|
||||
{
|
||||
private final ByteBufferPool _byteBufferPool;
|
||||
private final HpackContext _context;
|
||||
|
||||
public HpackEncoder(ByteBufferPool byteBufferPool)
|
||||
{
|
||||
this.byteBufferPool = byteBufferPool;
|
||||
this(byteBufferPool,4096);
|
||||
}
|
||||
|
||||
public HpackEncoder(ByteBufferPool byteBufferPool,int maxHeaderTableSize)
|
||||
{
|
||||
this._byteBufferPool = byteBufferPool;
|
||||
_context=new HpackContext(maxHeaderTableSize);
|
||||
}
|
||||
|
||||
public ByteBufferPool.Lease encode(HttpFields fields)
|
||||
{
|
||||
return new ByteBufferPool.Lease(byteBufferPool);
|
||||
return new ByteBufferPool.Lease(_byteBufferPool);
|
||||
}
|
||||
|
||||
|
||||
private boolean encode(ByteBuffer buffer, HttpField field)
|
||||
{
|
||||
// Is there an entry for the field?
|
||||
Entry entry = _context.get(field);
|
||||
|
||||
if (entry!=null)
|
||||
{
|
||||
// if entry is already in the reference set, then nothing more to do.
|
||||
if (entry.isInReferenceSet())
|
||||
return true;
|
||||
|
||||
// Is this as static field
|
||||
if (entry.isStatic())
|
||||
{
|
||||
// TODO Policy decision to make!
|
||||
// Should we add to reference set or just always send as indexed?
|
||||
// Let's always send as indexed to reduce churn in header table!
|
||||
// BUGGER! Can't do that. Oh well all the static fields have small values, so
|
||||
// lets send as literal header, indexed name.
|
||||
// We don't need never indexed because the cookie fields are name only and we can
|
||||
// huffman encode the value for the same reason.
|
||||
|
||||
// Add the token
|
||||
buffer.put((byte)0x00);
|
||||
// Add the name index
|
||||
NBitInteger.encode(buffer,4,_context.index(entry));
|
||||
// Add the value
|
||||
buffer.put(entry.getStaticHuffmanValue());
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -341,7 +341,7 @@ public class Huffman
|
|||
}
|
||||
|
||||
|
||||
static public String decode(ByteBuffer buffer) throws IOException
|
||||
static public String decode(ByteBuffer buffer)
|
||||
{
|
||||
StringBuilder out = new StringBuilder(buffer.remaining()*2);
|
||||
int node = 0;
|
||||
|
|
|
@ -26,6 +26,7 @@ import static org.junit.Assert.assertNull;
|
|||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
@ -397,6 +398,13 @@ public class HpackContextTest
|
|||
ctx.addToRefSet(ctx.get(3));
|
||||
ctx.addToRefSet(ctx.get(1));
|
||||
ctx.addToRefSet(ctx.get(5));
|
||||
|
||||
// check isInReferenceSet
|
||||
assertTrue(ctx.get(1).isInReferenceSet());
|
||||
assertFalse(ctx.get(2).isInReferenceSet());
|
||||
assertTrue(ctx.get(3).isInReferenceSet());
|
||||
assertFalse(ctx.get(4).isInReferenceSet());
|
||||
assertTrue(ctx.get(5).isInReferenceSet());
|
||||
|
||||
// iterate ref set
|
||||
HashSet<HttpField> fields = new HashSet<>();
|
||||
|
@ -426,6 +434,13 @@ public class HpackContextTest
|
|||
assertTrue(fields.contains(field[0]));
|
||||
assertTrue(fields.contains(field[4]));
|
||||
|
||||
// check isInReferenceSet
|
||||
assertTrue(ctx.get(1).isInReferenceSet());
|
||||
assertFalse(ctx.get(2).isInReferenceSet());
|
||||
assertFalse(ctx.get(3).isInReferenceSet());
|
||||
assertFalse(ctx.get(4).isInReferenceSet());
|
||||
assertTrue(ctx.get(5).isInReferenceSet());
|
||||
|
||||
// iterator remove
|
||||
Iterator<Entry> iter=ctx.iterateReferenceSet();
|
||||
iter.next();
|
||||
|
@ -445,6 +460,55 @@ public class HpackContextTest
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefSetClear()
|
||||
{
|
||||
// Only enough space for 5 entries
|
||||
HpackContext ctx = new HpackContext(38*5);
|
||||
|
||||
HttpField[] field =
|
||||
{
|
||||
new HttpField("fo0","b0r"),
|
||||
new HttpField("fo1","b1r"),
|
||||
new HttpField("fo2","b2r"),
|
||||
new HttpField("fo3","b3r"),
|
||||
new HttpField("fo4","b4r"),
|
||||
new HttpField("fo5","b5r"),
|
||||
new HttpField("fo6","b6r"),
|
||||
new HttpField("fo7","b7r"),
|
||||
new HttpField("fo8","b8r"),
|
||||
new HttpField("fo9","b9r"),
|
||||
new HttpField("foA","bAr"),
|
||||
};
|
||||
Entry[] entry = new Entry[field.length];
|
||||
|
||||
// Add 5 entries
|
||||
for (int i=0;i<=4;i++)
|
||||
entry[i]=ctx.add(field[i]);
|
||||
|
||||
// Add 3 entries to reference set
|
||||
ctx.clearReferenceSet();
|
||||
ctx.addToRefSet(ctx.get(3));
|
||||
ctx.addToRefSet(ctx.get(1));
|
||||
ctx.addToRefSet(ctx.get(5));
|
||||
|
||||
// iterate ref set
|
||||
HashSet<HttpField> fields = new HashSet<>();
|
||||
for (Entry e: ctx.getReferenceSet() )
|
||||
fields.add(e.getHttpField());
|
||||
assertEquals(3,fields.size());
|
||||
assertTrue(fields.contains(field[0]));
|
||||
assertTrue(fields.contains(field[2]));
|
||||
assertTrue(fields.contains(field[4]));
|
||||
|
||||
// Clear set
|
||||
ctx.clearReferenceSet();
|
||||
fields.clear();
|
||||
for (Entry e: ctx.getReferenceSet() )
|
||||
fields.add(e.getHttpField());
|
||||
assertEquals(0,fields.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResize()
|
||||
{
|
||||
|
@ -598,8 +662,28 @@ public class HpackContextTest
|
|||
assertFalse(fields.contains(field[0]));
|
||||
assertFalse(fields.contains(field[2]));
|
||||
assertTrue(fields.contains(field[4]));
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStaticHuffmanValues()
|
||||
{
|
||||
HpackContext ctx = new HpackContext(4096);
|
||||
for (int i=2;i<=14;i++)
|
||||
{
|
||||
Entry entry=ctx.get(i);
|
||||
assertTrue(entry.isStatic());
|
||||
|
||||
ByteBuffer buffer = ByteBuffer.wrap(entry.getStaticHuffmanValue());
|
||||
int huff = 0xff&buffer.get();
|
||||
assertTrue((0x80&huff)==0x80);
|
||||
|
||||
int len = NBitInteger.decode(buffer,7);
|
||||
|
||||
assertEquals(len,buffer.remaining());
|
||||
String value = Huffman.decode(buffer);
|
||||
|
||||
assertEquals(entry.getHttpField().getValue(),value);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue