improve known header handling in hpack encoding
This commit is contained in:
parent
aaa2e5c6c1
commit
cd59d0085e
|
@ -111,16 +111,26 @@ public enum HttpHeader
|
|||
|
||||
X_POWERED_BY("X-Powered-By"),
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** HTTP2 Fields.
|
||||
*/
|
||||
C_METHOD(":method"),
|
||||
C_SCHEME(":scheme"),
|
||||
C_AUTHORITY(":authority"),
|
||||
C_PATH(":path"),
|
||||
C_STATUS(":status"),
|
||||
|
||||
UNKNOWN("::UNKNOWN::");
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public final static Trie<HttpHeader> CACHE= new ArrayTrie<>(512);
|
||||
public final static Trie<HttpHeader> CACHE= new ArrayTrie<>(520);
|
||||
static
|
||||
{
|
||||
for (HttpHeader header : HttpHeader.values())
|
||||
if (header!=UNKNOWN)
|
||||
CACHE.put(header.toString(),header);
|
||||
if (!CACHE.put(header.toString(),header))
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
private final String _string;
|
||||
|
|
|
@ -25,8 +25,10 @@ import java.util.Map;
|
|||
import java.util.Set;
|
||||
|
||||
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.http2.hpack.HpackContext.Entry;
|
||||
import org.eclipse.jetty.util.ArrayQueue;
|
||||
import org.eclipse.jetty.util.ArrayTernaryTrie;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
|
@ -106,7 +108,7 @@ public class HpackContext
|
|||
|
||||
private static final Map<HttpField,Entry> __staticFieldMap = new HashMap<>();
|
||||
private static final Trie<Entry> __staticNameMap = new ArrayTernaryTrie<>(true,512);
|
||||
|
||||
private static final Entry[] __headerEntryTable = new Entry[HttpHeader.UNKNOWN.ordinal()];
|
||||
private static final StaticEntry[] __staticTable=new StaticEntry[STATIC_TABLE.length];
|
||||
static
|
||||
{
|
||||
|
@ -158,6 +160,13 @@ public class HpackContext
|
|||
throw new IllegalStateException("name trie too small");
|
||||
}
|
||||
}
|
||||
|
||||
for (HttpHeader h : HttpHeader.values())
|
||||
{
|
||||
Entry entry = __staticNameMap.get(h.asString());
|
||||
if (entry!=null)
|
||||
__headerEntryTable[h.ordinal()]=entry;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -215,6 +224,14 @@ public class HpackContext
|
|||
return null;
|
||||
}
|
||||
|
||||
public Entry get(HttpHeader header)
|
||||
{
|
||||
Entry e = __headerEntryTable[header.ordinal()];
|
||||
if (e==null)
|
||||
return get(header.asString());
|
||||
return e;
|
||||
}
|
||||
|
||||
public Entry add(HttpField field)
|
||||
{
|
||||
int slot=_headerTable.getNextSlotUnsafe();
|
||||
|
|
|
@ -50,6 +50,7 @@ public class HpackEncoder
|
|||
|
||||
private final static EnumSet<HttpHeader> __DO_NOT_INDEX =
|
||||
EnumSet.of(
|
||||
// TODO ??? HttpHeader.C_PATH,
|
||||
HttpHeader.CONTENT_MD5,
|
||||
HttpHeader.CONTENT_RANGE,
|
||||
HttpHeader.ETAG,
|
||||
|
@ -139,10 +140,10 @@ public class HpackEncoder
|
|||
|
||||
// TODO optimise these to avoid HttpField creation
|
||||
String scheme=request.getURI().getScheme();
|
||||
encode(buffer,new HttpField(":scheme",scheme==null?HttpScheme.HTTP.asString():scheme));
|
||||
encode(buffer,new HttpField(":method",request.getMethod()));
|
||||
encode(buffer,new HttpField(":authority",request.getURI().getAuthority()));
|
||||
encode(buffer,new HttpField(":path",request.getURI().getPathQuery()));
|
||||
encode(buffer,new HttpField(HttpHeader.C_SCHEME,scheme==null?HttpScheme.HTTP.asString():scheme));
|
||||
encode(buffer,new HttpField(HttpHeader.C_METHOD,request.getMethod()));
|
||||
encode(buffer,new HttpField(HttpHeader.C_AUTHORITY,request.getURI().getAuthority()));
|
||||
encode(buffer,new HttpField(HttpHeader.C_PATH,request.getURI().getPathQuery()));
|
||||
|
||||
}
|
||||
else if (metadata.isResponse())
|
||||
|
@ -151,7 +152,7 @@ public class HpackEncoder
|
|||
int code=response.getStatus();
|
||||
HttpField status = code<__status.length?__status[code]:null;
|
||||
if (status==null)
|
||||
status=new HttpField(":status",Integer.toString(code));
|
||||
status=new HttpField(HttpHeader.C_STATUS,Integer.toString(code));
|
||||
encode(buffer,status);
|
||||
}
|
||||
|
||||
|
@ -188,7 +189,7 @@ public class HpackEncoder
|
|||
{
|
||||
int index=_context.index(entry);
|
||||
if (p>=0)
|
||||
encoding="Indexed"+index;
|
||||
encoding="IdxField"+(entry.isStatic()?"S":"")+(1+NBitInteger.octectsNeeded(7,index));
|
||||
buffer.put((byte)0x80);
|
||||
NBitInteger.encode(buffer,7,index);
|
||||
}
|
||||
|
@ -196,18 +197,18 @@ public class HpackEncoder
|
|||
{
|
||||
// Must be a new entry, so we will have to send literally.
|
||||
|
||||
// Look for a name Index
|
||||
Entry name_entry = _context.get(field.getName());
|
||||
|
||||
HttpHeader header = field.getHeader();
|
||||
final Entry name;
|
||||
final boolean indexed;
|
||||
final boolean never_index;
|
||||
final boolean huffman;
|
||||
final boolean indexed;
|
||||
final int name_bits;
|
||||
final int bits;
|
||||
|
||||
HttpHeader header = field.getHeader();
|
||||
if (header==null)
|
||||
{
|
||||
name = _context.get(field.getName());
|
||||
// has the custom header name been seen before?
|
||||
if (name_entry==null)
|
||||
if (name==null)
|
||||
{
|
||||
// unknown name and value, so let's index this just in case it is
|
||||
// the first time we have seen a custom name or a custom field.
|
||||
|
@ -215,7 +216,7 @@ public class HpackEncoder
|
|||
indexed=true;
|
||||
never_index=false;
|
||||
huffman=true;
|
||||
name_bits = 6;
|
||||
bits = 6;
|
||||
buffer.put((byte)0x40);
|
||||
}
|
||||
else
|
||||
|
@ -225,49 +226,53 @@ public class HpackEncoder
|
|||
indexed=false;
|
||||
never_index=false;
|
||||
huffman=true;
|
||||
name_bits = 4;
|
||||
bits = 4;
|
||||
buffer.put((byte)0x00);
|
||||
}
|
||||
}
|
||||
else if (__DO_NOT_INDEX.contains(header))
|
||||
else
|
||||
{
|
||||
// Non indexed field
|
||||
indexed=false;
|
||||
never_index=__NEVER_INDEX.contains(header);
|
||||
huffman=!__DO_NOT_HUFFMAN.contains(header);
|
||||
name_bits = 4;
|
||||
buffer.put(never_index?(byte)0x10:(byte)0x00);
|
||||
}
|
||||
else if (header==HttpHeader.CONTENT_LENGTH && field.getValue().length()>1)
|
||||
{
|
||||
// Non indexed content length for non zero value
|
||||
indexed=false;
|
||||
never_index=false;
|
||||
huffman=true;
|
||||
name_bits = 4;
|
||||
buffer.put((byte)0x00);
|
||||
}
|
||||
else
|
||||
{
|
||||
// indexed
|
||||
indexed=true;
|
||||
never_index=false;
|
||||
huffman=!__DO_NOT_HUFFMAN.contains(header);
|
||||
name_bits = 6;
|
||||
buffer.put((byte)0x40);
|
||||
}
|
||||
name = _context.get(header);
|
||||
|
||||
if (__DO_NOT_INDEX.contains(header))
|
||||
{
|
||||
// Non indexed field
|
||||
indexed=false;
|
||||
never_index=__NEVER_INDEX.contains(header);
|
||||
huffman=!__DO_NOT_HUFFMAN.contains(header);
|
||||
bits = 4;
|
||||
buffer.put(never_index?(byte)0x10:(byte)0x00);
|
||||
}
|
||||
else if (header==HttpHeader.CONTENT_LENGTH && field.getValue().length()>1)
|
||||
{
|
||||
// Non indexed content length for non zero value
|
||||
indexed=false;
|
||||
never_index=false;
|
||||
huffman=true;
|
||||
bits = 4;
|
||||
buffer.put((byte)0x00);
|
||||
}
|
||||
else
|
||||
{
|
||||
// indexed
|
||||
indexed=true;
|
||||
never_index=false;
|
||||
huffman=!__DO_NOT_HUFFMAN.contains(header);
|
||||
bits = 6;
|
||||
buffer.put((byte)0x40);
|
||||
}
|
||||
}
|
||||
|
||||
if (p>=0)
|
||||
{
|
||||
encoding="Lit"+
|
||||
((name_entry==null)?"HuffN":"IdxN")+
|
||||
((name==null)?"HuffN":("IdxN"+(name.isStatic()?"S":"")+(1+NBitInteger.octectsNeeded(bits,_context.index(name)))))+
|
||||
(huffman?"HuffV":"LitV")+
|
||||
(indexed?"Idx":(never_index?"!!Idx":"!Idx"));
|
||||
}
|
||||
|
||||
if (name_entry!=null)
|
||||
NBitInteger.encode(buffer,name_bits,_context.index(name_entry));
|
||||
if (name!=null)
|
||||
NBitInteger.encode(buffer,bits,_context.index(name));
|
||||
else
|
||||
{
|
||||
// leave name index bits as 0
|
||||
|
|
|
@ -52,17 +52,17 @@ public class HpackPerfTest
|
|||
@After
|
||||
public void after()
|
||||
{
|
||||
System.err.printf("headertable=%d unencoded=%d encoded=%d p=%d%%%n",_maxHeaderTableSize,_unencodedSize,_encodedSize,(100*_encodedSize+49)/_unencodedSize);
|
||||
System.err.printf("headertable=%d unencoded=%d encoded=%d p=%3.1f%%%n",_maxHeaderTableSize,_unencodedSize,_encodedSize,100.0*_encodedSize/_unencodedSize);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simpleTest() throws Exception
|
||||
{
|
||||
runStories(_maxHeaderTableSize,new HpackEncoder(_maxHeaderTableSize,_maxHeaderTableSize));
|
||||
runStories(_maxHeaderTableSize);
|
||||
}
|
||||
|
||||
private void runStories(int maxHeaderTableSize, HpackEncoder encoder) throws Exception
|
||||
private void runStories(int maxHeaderTableSize) throws Exception
|
||||
{
|
||||
// Find files
|
||||
File data = MavenTestingUtils.getTestResourceDir("data");
|
||||
|
@ -84,25 +84,25 @@ public class HpackPerfTest
|
|||
ByteBuffer buffer = BufferUtil.allocate(256*1024);
|
||||
|
||||
// Encode all the requests
|
||||
encodeStories(buffer,stories,"request",encoder);
|
||||
encodeStories(buffer,stories,"request");
|
||||
|
||||
// clear table
|
||||
BufferUtil.clearToFill(buffer);
|
||||
encoder.encodeMaxHeaderTableSize(buffer,0);
|
||||
encoder.encodeMaxHeaderTableSize(buffer,maxHeaderTableSize);
|
||||
BufferUtil.flipToFlush(buffer,0);
|
||||
|
||||
// Encode all the responses
|
||||
encodeStories(buffer,stories,"response",encoder);
|
||||
encodeStories(buffer,stories,"response");
|
||||
|
||||
}
|
||||
|
||||
private void encodeStories(ByteBuffer buffer,Map<String,Object>[] stories, String type, HpackEncoder encoder) throws Exception
|
||||
private void encodeStories(ByteBuffer buffer,Map<String,Object>[] stories, String type) throws Exception
|
||||
{
|
||||
for (Map<String,Object> story : stories)
|
||||
{
|
||||
if (type.equals(story.get("context")))
|
||||
{
|
||||
HpackEncoder encoder = new HpackEncoder(_maxHeaderTableSize,_maxHeaderTableSize);
|
||||
|
||||
// System.err.println(story);
|
||||
Object[] cases = (Object[])story.get("cases");
|
||||
for (Object c : cases)
|
||||
|
|
Loading…
Reference in New Issue