HpackDecoder implements 413 limit

This commit is contained in:
Greg Wilkins 2014-06-18 11:11:23 +02:00
parent 140e7ed0c5
commit 30affa57c7
4 changed files with 114 additions and 10 deletions

View File

@ -27,6 +27,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.http.HttpStatus;
import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.http2.hpack.HpackContext.Entry;
import org.eclipse.jetty.util.TypeUtil;
@ -43,18 +44,21 @@ public class HpackDecoder
public static final Logger LOG = Log.getLogger(HpackDecoder.class);
private final HpackContext _context;
private final MetaDataBuilder _builder = new MetaDataBuilder();
private final MetaDataBuilder _builder;
private int _localMaxHeaderTableSize;
@Deprecated
public HpackDecoder()
{
this(4096);
this(4*1024,8*1024);
LOG.warn("USE HpackDecoder constructor with maxHeaderSize!!!");
}
public HpackDecoder(int localMaxHeaderTableSize)
public HpackDecoder(int localMaxHeaderTableSize, int maxHeaderSize)
{
_context=new HpackContext(localMaxHeaderTableSize);
_localMaxHeaderTableSize=localMaxHeaderTableSize;
_builder = new MetaDataBuilder(maxHeaderSize);
}
public void setLocalMaxHeaderTableSize(int localMaxHeaderTableSize)
@ -63,9 +67,15 @@ public class HpackDecoder
}
public MetaData decode(ByteBuffer buffer)
{
{
if (LOG.isDebugEnabled())
LOG.debug(String.format("CtxTbl[%x] decoding",_context.hashCode()));
LOG.debug(String.format("CtxTbl[%x] decoding %d octets",_context.hashCode(),buffer.remaining()));
// If the buffer is big, don't even think about decoding it
if (buffer.remaining()>_builder.getMaxSize())
throw new BadMessageException(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413,"Header frame size "+buffer.remaining()+">"+_builder.getMaxSize());
while(buffer.hasRemaining())
{
if (LOG.isDebugEnabled())
@ -129,6 +139,7 @@ public class HpackDecoder
{
huffmanName = (buffer.get()&0x80)==0x80;
int length = NBitInteger.decode(buffer,7);
_builder.checkSize(length,huffmanName);
if (huffmanName)
name=Huffman.decode(buffer,length);
else
@ -147,6 +158,7 @@ public class HpackDecoder
// decode the value
boolean huffmanValue = (buffer.get()&0x80)==0x80;
int length = NBitInteger.decode(buffer,7);
_builder.checkSize(length,huffmanValue);
if (huffmanValue)
value=Huffman.decode(buffer,length);
else

View File

@ -20,10 +20,12 @@
package org.eclipse.jetty.http2.hpack;
import org.eclipse.jetty.http.BadMessageException;
import org.eclipse.jetty.http.HostPortHttpField;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MetaData;
@ -32,16 +34,44 @@ import org.eclipse.jetty.http.MetaData;
/* -------------------------------------------------------- */
public class MetaDataBuilder
{
private final int _maxSize;
private int _size;
private int _status;
private String _method;
private HttpScheme _scheme;
private HostPortHttpField _authority;
private String _path;
HttpFields _fields = new HttpFields(10);
private HttpFields _fields = new HttpFields(10);
MetaDataBuilder(int maxSize)
{
_maxSize=maxSize;
}
/** Get the maxSize.
* @return the maxSize
*/
public int getMaxSize()
{
return _maxSize;
}
/** Get the size.
* @return the current size in bytes
*/
public int getSize()
{
return _size;
}
public void emit(HttpField field)
{
int field_size = field.getName().length()+field.getValue().length();
_size+=field_size;
if (_size>_maxSize)
throw new BadMessageException(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413,"Header size "+_size+">"+_maxSize);
if (field instanceof StaticValueHttpField)
{
StaticValueHttpField value = (StaticValueHttpField)field;
@ -65,7 +95,6 @@ public class MetaDataBuilder
}
else
{
switch(field.getName())
{
case ":status":
@ -114,6 +143,21 @@ public class MetaDataBuilder
_scheme=null;
_authority=null;
_path=null;
_size=0;
}
}
/* ------------------------------------------------------------ */
/** Check that the max size will not be exceeded.
* @param length
* @param huffmanName
*/
public void checkSize(int length, boolean huffman)
{
// Apply a huffman fudge factor
if (huffman)
length=(length*4)/3;
if ((_size+length)>_maxSize)
throw new BadMessageException(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413,"Header size "+(_size+length)+">"+_maxSize);
}
}

View File

@ -43,7 +43,7 @@ public class HpackDecoderTest
@Test
public void testDecodeD_3()
{
HpackDecoder decoder = new HpackDecoder();
HpackDecoder decoder = new HpackDecoder(4096,8192);
// First request
String encoded="828786440f7777772e6578616d706c652e636f6d";
@ -93,7 +93,7 @@ public class HpackDecoderTest
@Test
public void testDecodeD_4()
{
HpackDecoder decoder = new HpackDecoder();
HpackDecoder decoder = new HpackDecoder(4096,8192);
// First request
String encoded="828786448ce7cf9bebe89b6fb16fa9b6ff";

View File

@ -19,11 +19,16 @@
package org.eclipse.jetty.http2.hpack;
import static org.junit.Assert.assertEquals;
import java.nio.ByteBuffer;
import org.eclipse.jetty.http.BadMessageException;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MetaData;
import org.junit.Assert;
import org.junit.Test;
import org.eclipse.jetty.http.MetaData.Request;
@ -37,7 +42,7 @@ public class HpackTest
public void encodeDecodeResponseTest()
{
HpackEncoder encoder = new HpackEncoder();
HpackDecoder decoder = new HpackDecoder();
HpackDecoder decoder = new HpackDecoder(4096,8192);
ByteBuffer buffer = BufferUtil.allocate(16*1024);
HttpFields fields0 = new HttpFields();
@ -80,4 +85,47 @@ public class HpackTest
Assert.assertEquals("custom-key",decoded1.getFields().getField("Custom-Key").getName());
}
@Test
public void encodeDecodeTooLargeTest()
{
HpackEncoder encoder = new HpackEncoder();
HpackDecoder decoder = new HpackDecoder(4096,101);
ByteBuffer buffer = BufferUtil.allocate(16*1024);
HttpFields fields0 = new HttpFields();
fields0.add("1234567890","1234567890123456789012345678901234567890");
fields0.add("Cookie","abcdeffhijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQR");
MetaData original0= new MetaData(HttpVersion.HTTP_2,fields0);
BufferUtil.clearToFill(buffer);
encoder.encode(buffer,original0);
BufferUtil.flipToFlush(buffer,0);
MetaData decoded0 = (MetaData)decoder.decode(buffer);
Assert.assertEquals(original0,decoded0);
HttpFields fields1 = new HttpFields();
fields1.add("1234567890","1234567890123456789012345678901234567890");
fields1.add("Cookie","abcdeffhijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQR");
fields1.add("x","y");
MetaData original1 = new MetaData(HttpVersion.HTTP_2,fields1);
BufferUtil.clearToFill(buffer);
encoder.encode(buffer,original1);
BufferUtil.flipToFlush(buffer,0);
try
{
decoder.decode(buffer);
Assert.fail();
}
catch(BadMessageException e)
{
assertEquals(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413,e.getCode());
}
}
}