misc optimisation of http2 field creation

This commit is contained in:
Greg Wilkins 2014-08-05 12:55:12 +10:00
parent 636c7eaeae
commit d7f2c42e2d
6 changed files with 107 additions and 61 deletions

View File

@ -69,9 +69,14 @@ public class HttpField
return _value; return _value;
} }
public int getIntValue()
{
return Integer.valueOf(_value);
}
public long getLongValue() public long getLongValue()
{ {
return StringUtil.toLong(_value); return Long.valueOf(_value);
} }
public String[] getValues() public String[] getValues()
@ -416,21 +421,65 @@ public class HttpField
return true; return true;
} }
public static class IntValueHttpField extends HttpField
{
final int _int;
public IntValueHttpField(HttpHeader header, String value, int intValue)
{
super(header,value);
_int=intValue;
}
public IntValueHttpField(HttpHeader header, String value)
{
this(header,value,Integer.valueOf(value));
}
public IntValueHttpField(HttpHeader header, int value)
{
this(header,Integer.toString(value),value);
}
@Override
public int getIntValue()
{
return _int;
}
@Override
public long getLongValue()
{
return _int;
}
}
public static class LongValueHttpField extends HttpField public static class LongValueHttpField extends HttpField
{ {
final long _long; final long _long;
public LongValueHttpField(HttpHeader header, long value) public LongValueHttpField(HttpHeader header, String value, long longValue)
{ {
super(header,Long.toString(value)); super(header,value);
_long=value; _long=longValue;
} }
public LongValueHttpField(HttpHeader header, String value) public LongValueHttpField(HttpHeader header, String value)
{ {
super(header,value); this(header,value,StringUtil.toLong(value));
_long=StringUtil.toLong(value); }
public LongValueHttpField(HttpHeader header,long value)
{
this(header,Long.toString(value),value);
}
@Override
public int getIntValue()
{
return (int)_long;
} }
@Override @Override

View File

@ -119,20 +119,20 @@ public class HpackContext
switch(i) switch(i)
{ {
case 2: case 2:
entry=new StaticEntry(i,new StaticValueHttpField(STATIC_TABLE[i][0],STATIC_TABLE[i][1],HttpMethod.GET)); entry=new StaticEntry(i,new StaticTableHttpField(STATIC_TABLE[i][0],STATIC_TABLE[i][1],HttpMethod.GET));
break; break;
case 3: case 3:
entry=new StaticEntry(i,new StaticValueHttpField(STATIC_TABLE[i][0],STATIC_TABLE[i][1],HttpMethod.POST)); entry=new StaticEntry(i,new StaticTableHttpField(STATIC_TABLE[i][0],STATIC_TABLE[i][1],HttpMethod.POST));
break; break;
case 6: case 6:
entry=new StaticEntry(i,new StaticValueHttpField(STATIC_TABLE[i][0],STATIC_TABLE[i][1],HttpScheme.HTTP)); entry=new StaticEntry(i,new StaticTableHttpField(STATIC_TABLE[i][0],STATIC_TABLE[i][1],HttpScheme.HTTP));
break; break;
case 7: case 7:
entry=new StaticEntry(i,new StaticValueHttpField(STATIC_TABLE[i][0],STATIC_TABLE[i][1],HttpScheme.HTTPS)); entry=new StaticEntry(i,new StaticTableHttpField(STATIC_TABLE[i][0],STATIC_TABLE[i][1],HttpScheme.HTTPS));
break; break;
case 8: case 8:
case 11: case 11:
entry=new StaticEntry(i,new StaticValueHttpField(STATIC_TABLE[i][0],STATIC_TABLE[i][1],Integer.valueOf(STATIC_TABLE[i][1]))); entry=new StaticEntry(i,new StaticTableHttpField(STATIC_TABLE[i][0],STATIC_TABLE[i][1],Integer.valueOf(STATIC_TABLE[i][1])));
break; break;
case 9: case 9:
@ -140,7 +140,7 @@ public class HpackContext
case 12: case 12:
case 13: case 13:
case 14: case 14:
entry=new StaticEntry(i,new StaticValueHttpField(STATIC_TABLE[i][0],STATIC_TABLE[i][1],Integer.valueOf(STATIC_TABLE[i][1]))); entry=new StaticEntry(i,new StaticTableHttpField(STATIC_TABLE[i][0],STATIC_TABLE[i][1],Integer.valueOf(STATIC_TABLE[i][1])));
break; break;
default: default:
@ -429,6 +429,7 @@ public class HpackContext
public static class StaticEntry extends Entry public static class StaticEntry extends Entry
{ {
private final byte[] _huffmanValue; private final byte[] _huffmanValue;
private final byte _encodedField;
StaticEntry(int index,HttpField field) StaticEntry(int index,HttpField field)
{ {
@ -450,6 +451,8 @@ public class HpackContext
} }
else else
_huffmanValue=null; _huffmanValue=null;
_encodedField=(byte)(0x80|index);
} }
@Override @Override
@ -463,6 +466,11 @@ public class HpackContext
{ {
return _huffmanValue; return _huffmanValue;
} }
public byte getEncodedField()
{
return _encodedField;
}
} }

View File

@ -41,7 +41,8 @@ import org.eclipse.jetty.util.log.Logger;
public class HpackDecoder public class HpackDecoder
{ {
public static final Logger LOG = Log.getLogger(HpackDecoder.class); public static final Logger LOG = Log.getLogger(HpackDecoder.class);
public final static HttpField.LongValueHttpField CONTENT_LENGTH_0 = new HttpField.LongValueHttpField(HttpHeader.CONTENT_LENGTH,0L); public final static HttpField.LongValueHttpField CONTENT_LENGTH_0 =
new HttpField.LongValueHttpField(HttpHeader.CONTENT_LENGTH,0L);
private final HpackContext _context; private final HpackContext _context;
private final MetaDataBuilder _builder; private final MetaDataBuilder _builder;
@ -191,41 +192,26 @@ public class HpackDecoder
HttpField field; HttpField field;
if (header==null) if (header==null)
{ {
// just make a normal field and bypass header name lookup
field = new HttpField(null,name,value); field = new HttpField(null,name,value);
} }
else else
{ {
// might be worthwhile to create a value HttpField if it is indexed
// and/or of a type that may be looked up multiple times.
switch(header) switch(header)
{ {
case C_METHOD:
HttpMethod method=HttpMethod.CACHE.get(value);
if (method!=null)
field = new StaticValueHttpField(HttpHeader.C_METHOD,method.asString(),method);
else
field = new AuthorityHttpField(value);
break;
case C_STATUS: case C_STATUS:
Integer code = Integer.valueOf(value); if (indexed)
field = new StaticValueHttpField(HttpHeader.C_STATUS,value,code); field = new HttpField.IntValueHttpField(header,value);
break;
case C_SCHEME:
HttpScheme scheme=HttpScheme.CACHE.get(value);
if (scheme!=null)
field = new StaticValueHttpField(HttpHeader.C_SCHEME,scheme.asString(),scheme);
else else
field = new AuthorityHttpField(value); field = new HttpField(header,name,value);
break; break;
case C_AUTHORITY: case C_AUTHORITY:
field = new AuthorityHttpField(value); field = new AuthorityHttpField(value);
break; break;
case C_PATH:
field = new HttpField(HttpHeader.C_PATH,value);
break;
case CONTENT_LENGTH: case CONTENT_LENGTH:
if ("0".equals(value)) if ("0".equals(value))
field = CONTENT_LENGTH_0; field = CONTENT_LENGTH_0;

View File

@ -28,6 +28,7 @@ import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.MetaData; import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.http2.hpack.HpackContext.Entry; import org.eclipse.jetty.http2.hpack.HpackContext.Entry;
import org.eclipse.jetty.http2.hpack.HpackContext.StaticEntry;
import org.eclipse.jetty.io.ByteBufferPool.Lease; import org.eclipse.jetty.io.ByteBufferPool.Lease;
import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.TypeUtil;
@ -78,6 +79,7 @@ public class HpackEncoder
} }
private final HpackContext _context; private final HpackContext _context;
private final boolean _debug;
private int _remoteMaxHeaderTableSize; private int _remoteMaxHeaderTableSize;
private int _localMaxHeaderTableSize; private int _localMaxHeaderTableSize;
@ -96,6 +98,7 @@ public class HpackEncoder
_context=new HpackContext(remoteMaxHeaderTableSize); _context=new HpackContext(remoteMaxHeaderTableSize);
_remoteMaxHeaderTableSize=remoteMaxHeaderTableSize; _remoteMaxHeaderTableSize=remoteMaxHeaderTableSize;
_localMaxHeaderTableSize=localMaxHeaderTableSize; _localMaxHeaderTableSize=localMaxHeaderTableSize;
_debug=LOG.isDebugEnabled();
} }
public HpackContext getContext() public HpackContext getContext()
@ -154,15 +157,13 @@ public class HpackEncoder
int code=response.getStatus(); int code=response.getStatus();
HttpField status = code<__status.length?__status[code]:null; HttpField status = code<__status.length?__status[code]:null;
if (status==null) if (status==null)
status=new HttpField(HttpHeader.C_STATUS,Integer.toString(code)); status=new HttpField.IntValueHttpField(HttpHeader.C_STATUS,code);
encode(buffer,status); encode(buffer,status);
} }
// Add all the other fields // Add all the other fields
for (HttpField field : metadata) for (HttpField field : metadata)
{
encode(buffer,field); encode(buffer,field);
}
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
LOG.debug(String.format("CtxTbl[%x] encoded %d octets",_context.hashCode(), buffer.position() - pos)); LOG.debug(String.format("CtxTbl[%x] encoded %d octets",_context.hashCode(), buffer.position() - pos));
@ -179,7 +180,7 @@ public class HpackEncoder
private void encode(ByteBuffer buffer, HttpField field) private void encode(ByteBuffer buffer, HttpField field)
{ {
final int p=LOG.isDebugEnabled()?buffer.position():-1; final int p=_debug?buffer.position():-1;
String encoding=null; String encoding=null;
@ -190,16 +191,24 @@ public class HpackEncoder
if (entry!=null) if (entry!=null)
{ {
// Known field entry, so encode it as indexed // Known field entry, so encode it as indexed
int index=_context.index(entry); if (entry.isStatic())
if (p>=0) {
encoding="IdxField"+(entry.isStatic()?"S":"")+(1+NBitInteger.octectsNeeded(7,index)); buffer.put(((StaticEntry)entry).getEncodedField());
buffer.put((byte)0x80); if (_debug)
NBitInteger.encode(buffer,7,index); encoding="IdxFieldS1";
}
else
{
int index=_context.index(entry);
buffer.put((byte)0x80);
NBitInteger.encode(buffer,7,index);
if (_debug)
encoding="IdxField"+(entry.isStatic()?"S":"")+(1+NBitInteger.octectsNeeded(7,index));
}
} }
else else
{ {
// Unknown field entry, so we will have to send literally. // Unknown field entry, so we will have to send literally.
final Entry name; final Entry name;
final boolean indexed; final boolean indexed;
final boolean never_index; final boolean never_index;
@ -211,6 +220,7 @@ public class HpackEncoder
if (header==null) if (header==null)
{ {
name = _context.get(field.getName()); name = _context.get(field.getName());
// has the custom header name been seen before? // has the custom header name been seen before?
if (name==null) if (name==null)
{ {
@ -226,7 +236,7 @@ public class HpackEncoder
else else
{ {
// known custom name, but unknown value. // known custom name, but unknown value.
// This is probably a custom field with changing value, so don't index now. // This is probably a custom field with changing value, so don't index.
indexed=false; indexed=false;
never_index=false; never_index=false;
huffman=true; huffman=true;
@ -267,7 +277,7 @@ public class HpackEncoder
} }
} }
if (p>=0) if (_debug)
{ {
encoding="Lit"+ encoding="Lit"+
((name==null)?"HuffN":("IdxN"+(name.isStatic()?"S":"")+(1+NBitInteger.octectsNeeded(bits,_context.index(name)))))+ ((name==null)?"HuffN":("IdxN"+(name.isStatic()?"S":"")+(1+NBitInteger.octectsNeeded(bits,_context.index(name)))))+
@ -316,7 +326,7 @@ public class HpackEncoder
_context.add(field); _context.add(field);
} }
if (p>=0) if (_debug)
{ {
int e=buffer.position(); int e=buffer.position();
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())

View File

@ -72,9 +72,9 @@ public class MetaDataBuilder
if (_size>_maxSize) if (_size>_maxSize)
throw new BadMessageException(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413,"Header size "+_size+">"+_maxSize); throw new BadMessageException(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413,"Header size "+_size+">"+_maxSize);
if (field instanceof StaticValueHttpField) if (field instanceof StaticTableHttpField)
{ {
StaticValueHttpField value = (StaticValueHttpField)field; StaticTableHttpField value = (StaticTableHttpField)field;
switch(field.getHeader()) switch(field.getHeader())
{ {
case C_STATUS: case C_STATUS:
@ -98,7 +98,7 @@ public class MetaDataBuilder
switch(field.getHeader()) switch(field.getHeader())
{ {
case C_STATUS: case C_STATUS:
_status=Integer.parseInt(field.getValue()); _status=field.getIntValue();
break; break;
case C_METHOD: case C_METHOD:
@ -114,6 +114,7 @@ public class MetaDataBuilder
break; break;
case HOST: case HOST:
// :authority fields must come first. If we have one, ignore the host header as far as authority goes.
if (_authority==null) if (_authority==null)
_authority=(field instanceof HostPortHttpField)?((HostPortHttpField)field):new AuthorityHttpField(field.getValue()); _authority=(field instanceof HostPortHttpField)?((HostPortHttpField)field):new AuthorityHttpField(field.getValue());
_fields.add(field); _fields.add(field);

View File

@ -23,19 +23,11 @@ import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeader;
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
public class StaticValueHttpField extends HttpField public class StaticTableHttpField extends HttpField
{ {
private final Object _value; private final Object _value;
public StaticValueHttpField(HttpHeader header,String name, String valueString, Object value) public StaticTableHttpField(HttpHeader header,String valueString, Object value)
{
super(header,name,valueString);
if (value==null)
throw new IllegalArgumentException();
_value=value;
}
public StaticValueHttpField(HttpHeader header,String valueString, Object value)
{ {
super(header,header.asString(),valueString); super(header,header.asString(),valueString);
if (value==null) if (value==null)
@ -43,7 +35,7 @@ public class StaticValueHttpField extends HttpField
_value=value; _value=value;
} }
public StaticValueHttpField(String name, String valueString, Object value) public StaticTableHttpField(String name, String valueString, Object value)
{ {
super(name,valueString); super(name,valueString);
if (value==null) if (value==null)