diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackContext.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackContext.java index 4f8b2b06201..180fcd9ce3e 100644 --- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackContext.java +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackContext.java @@ -116,7 +116,7 @@ public class HpackContext private static final Trie __staticNameMap = new ArrayTernaryTrie<>(true,512); private static final StaticEntry[] __staticTableByHeader = new StaticEntry[HttpHeader.UNKNOWN.ordinal()]; private static final StaticEntry[] __staticTable=new StaticEntry[STATIC_TABLE.length]; - private static final int STATIC_SIZE = STATIC_TABLE.length-1; + public static final int STATIC_SIZE = STATIC_TABLE.length-1; static { Set added = new HashSet<>(); diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java index b56fdb16b77..77cee8b4d92 100644 --- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java @@ -88,7 +88,7 @@ public class HpackEncoder private int _remoteMaxDynamicTableSize; private int _localMaxDynamicTableSize; private int _maxHeaderListSize; - private int _size; + private int _headerListSize; public HpackEncoder() { @@ -144,7 +144,7 @@ public class HpackEncoder if (LOG.isDebugEnabled()) LOG.debug(String.format("CtxTbl[%x] encoding",_context.hashCode())); - _size=0; + _headerListSize=0; int pos = buffer.position(); // Check the dynamic table sizes! @@ -179,9 +179,9 @@ public class HpackEncoder encode(buffer,field); // Check size - if (_maxHeaderListSize>0 && _size>_maxHeaderListSize) + if (_maxHeaderListSize>0 && _headerListSize>_maxHeaderListSize) { - LOG.warn("Header list size too large {} > {} for {}",_size,_maxHeaderListSize); + LOG.warn("Header list size too large {} > {} for {}",_headerListSize,_maxHeaderListSize); if (LOG.isDebugEnabled()) LOG.debug("metadata={}",metadata); } @@ -205,7 +205,7 @@ public class HpackEncoder field = new HttpField(field.getHeader(),field.getName(),""); int field_size = field.getName().length() + field.getValue().length(); - _size+=field_size+32; + _headerListSize+=field_size+32; final int p=_debug?buffer.position():-1; @@ -307,9 +307,9 @@ public class HpackEncoder (huffman?"HuffV":"LitV")+ (indexed?"Idx":(never_index?"!!Idx":"!Idx")); } - else if (header==HttpHeader.CONTENT_LENGTH && field.getValue().length()>1) + else if (field_size>=_context.getMaxDynamicTableSize() || header==HttpHeader.CONTENT_LENGTH && field.getValue().length()>2) { - // Non indexed content length for 2 digits or more + // Non indexed if field too large or a content length for 3 digits or more indexed=false; encodeName(buffer,(byte)0x00,4,header.asString(),name); encodeValue(buffer,true,field.getValue()); @@ -332,7 +332,8 @@ public class HpackEncoder // If we want the field referenced, then we add it to our // table and reference set. if (indexed) - _context.add(field); + if (_context.add(field)==null) + throw new IllegalStateException(); } if (_debug) diff --git a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackEncoderTest.java b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackEncoderTest.java index bfb8b92fb5a..006bd45679d 100644 --- a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackEncoderTest.java +++ b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackEncoderTest.java @@ -20,6 +20,7 @@ package org.eclipse.jetty.http2.hpack; +import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertThat; import java.nio.ByteBuffer; @@ -29,6 +30,7 @@ import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.http.MetaData; import org.eclipse.jetty.util.BufferUtil; +import org.eclipse.jetty.util.TypeUtil; import org.hamcrest.Matchers; import org.junit.Assert; import org.junit.Test; @@ -185,6 +187,69 @@ public class HpackEncoderTest } + @Test + public void testFieldLargerThanTable() + { + HttpFields fields = new HttpFields(); + + HpackEncoder encoder = new HpackEncoder(128); + ByteBuffer buffer0 = BufferUtil.allocate(4096); + int pos = BufferUtil.flipToFill(buffer0); + encoder.encode(buffer0,new MetaData(HttpVersion.HTTP_2,fields)); + BufferUtil.flipToFlush(buffer0,pos); + + encoder = new HpackEncoder(128); + fields.add(new HttpField("user-agent","jetty/test")); + ByteBuffer buffer1 = BufferUtil.allocate(4096); + pos = BufferUtil.flipToFill(buffer1); + encoder.encode(buffer1,new MetaData(HttpVersion.HTTP_2,fields)); + BufferUtil.flipToFlush(buffer1,pos); + + encoder = new HpackEncoder(128); + fields.add(new HttpField(":path", + "This is a very large field, whose size is larger than the dynamic table so it should not be indexed as it will not fit in the table ever!"+ + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX "+ + "YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY "+ + "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ ")); + ByteBuffer buffer2 = BufferUtil.allocate(4096); + pos = BufferUtil.flipToFill(buffer2); + encoder.encode(buffer2,new MetaData(HttpVersion.HTTP_2,fields)); + BufferUtil.flipToFlush(buffer2,pos); + + encoder = new HpackEncoder(128); + fields.add(new HttpField("host","somehost")); + ByteBuffer buffer = BufferUtil.allocate(4096); + pos = BufferUtil.flipToFill(buffer); + encoder.encode(buffer,new MetaData(HttpVersion.HTTP_2,fields)); + BufferUtil.flipToFlush(buffer,pos); + + //System.err.println(BufferUtil.toHexString(buffer0)); + //System.err.println(BufferUtil.toHexString(buffer1)); + //System.err.println(BufferUtil.toHexString(buffer2)); + //System.err.println(BufferUtil.toHexString(buffer)); + + // something was encoded! + assertThat(buffer.remaining(),Matchers.greaterThan(0)); + + // check first field is static index name and dynamic index body + assertThat((buffer.get(buffer0.remaining())&0xFF)>>6,equalTo(1)); + + // check first field is static index name and literal body + assertThat((buffer.get(buffer1.remaining())&0xFF)>>4,equalTo(0)); + + // check first field is static index name and dynamic index body + assertThat((buffer.get(buffer2.remaining())&0xFF)>>6,equalTo(1)); + + // Only first and third fields are put in the table + HpackContext context = encoder.getHpackContext(); + assertThat(context.size(),equalTo(2)); + assertThat(context.get(HpackContext.STATIC_SIZE+1).getHttpField().getName(),equalTo("host")); + assertThat(context.get(HpackContext.STATIC_SIZE+2).getHttpField().getName(),equalTo("user-agent")); + assertThat(context.getDynamicTableSize(),equalTo( + context.get(HpackContext.STATIC_SIZE+1).getSize()+context.get(HpackContext.STATIC_SIZE+2).getSize())); + + } + } diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/BufferUtil.java b/jetty-util/src/main/java/org/eclipse/jetty/util/BufferUtil.java index 5add8c718f3..a53480f5a46 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/BufferUtil.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/BufferUtil.java @@ -1126,12 +1126,10 @@ public class BufferUtil buf.append("\\x").append(TypeUtil.toHexString(b)); } - /* ------------------------------------------------------------ */ /** Convert buffer to a Hex Summary String. * @param buffer the buffer to generate a hex byte summary from - * @return A string showing the escaped content of the buffer around the - * position and limit (marked with <<< and >>>) + * @return A string showing a summary of the content in hex */ public static String toHexSummary(ByteBuffer buffer) { @@ -1152,6 +1150,18 @@ public class BufferUtil return buf.toString(); } + /* ------------------------------------------------------------ */ + /** Convert buffer to a Hex String. + * @param buffer the buffer to generate a hex byte summary from + * @return A hex string + */ + public static String toHexString(ByteBuffer buffer) + { + if (buffer == null) + return "null"; + return TypeUtil.toHexString(toArray(buffer)); + } + private final static int[] decDivisors = {1000000000, 100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10, 1};