dynamic table resize
Signed-off-by: Greg Wilkins <gregw@webtide.com>
This commit is contained in:
parent
971ca22367
commit
0da9225056
|
@ -74,6 +74,8 @@ public class HpackDecoder
|
|||
// If the buffer is big, don't even think about decoding it
|
||||
if (buffer.remaining()>_builder.getMaxSize())
|
||||
throw new HpackException.SessionException("431 Request Header Fields too large");
|
||||
|
||||
boolean emitted = false;
|
||||
|
||||
while(buffer.hasRemaining())
|
||||
{
|
||||
|
@ -99,6 +101,7 @@ public class HpackDecoder
|
|||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("decode IdxStatic {}",entry);
|
||||
// emit field
|
||||
emitted = true;
|
||||
_builder.emit(entry.getHttpField());
|
||||
|
||||
// TODO copy and add to reference set if there is room
|
||||
|
@ -109,6 +112,7 @@ public class HpackDecoder
|
|||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("decode Idx {}",entry);
|
||||
// emit
|
||||
emitted = true;
|
||||
_builder.emit(entry.getHttpField());
|
||||
}
|
||||
}
|
||||
|
@ -133,6 +137,8 @@ public class HpackDecoder
|
|||
LOG.debug("decode resize="+size);
|
||||
if (size>_localMaxDynamicTableSize)
|
||||
throw new IllegalArgumentException();
|
||||
if (emitted)
|
||||
throw new HpackException.CompressionException("Dynamic table resize after fields");
|
||||
_context.resize(size);
|
||||
continue;
|
||||
|
||||
|
@ -240,6 +246,7 @@ public class HpackDecoder
|
|||
}
|
||||
|
||||
// emit the field
|
||||
emitted = true;
|
||||
_builder.emit(field);
|
||||
|
||||
// if indexed add to dynamic table
|
||||
|
|
|
@ -32,6 +32,7 @@ import org.eclipse.jetty.http.HttpField;
|
|||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpScheme;
|
||||
import org.eclipse.jetty.http.MetaData;
|
||||
import org.eclipse.jetty.http2.hpack.HpackException.CompressionException;
|
||||
import org.eclipse.jetty.http2.hpack.HpackException.StreamException;
|
||||
import org.eclipse.jetty.util.TypeUtil;
|
||||
import org.hamcrest.Matchers;
|
||||
|
@ -188,26 +189,55 @@ public class HpackDecoderTest
|
|||
@Test
|
||||
public void testResize() throws Exception
|
||||
{
|
||||
String encoded = "3f6166871e33A13a47497f205f8841E92b043d492d49";
|
||||
String encoded = "203f136687A0E41d139d090760881c6490B2Cd39Ba7f";
|
||||
ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
HpackDecoder decoder = new HpackDecoder(4096, 8192);
|
||||
MetaData metaData = decoder.decode(buffer);
|
||||
assertThat(metaData.getFields().get(HttpHeader.HOST),is("aHostName"));
|
||||
assertThat(metaData.getFields().get(HttpHeader.CONTENT_TYPE),is("some/content"));
|
||||
assertThat(decoder.getHpackContext().getDynamicTableSize(),is(0));
|
||||
assertThat(metaData.getFields().get(HttpHeader.HOST),is( "localhost0"));
|
||||
assertThat(metaData.getFields().get(HttpHeader.COOKIE),is("abcdefghij"));
|
||||
assertThat(decoder.getHpackContext().getMaxDynamicTableSize(),is(50));
|
||||
assertThat(decoder.getHpackContext().size(),is(1));
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadResize() throws Exception
|
||||
{
|
||||
/*
|
||||
4. Dynamic Table Management
|
||||
4.2. Maximum Table Size
|
||||
× 1: Sends a dynamic table size update at the end of header block
|
||||
-> The endpoint MUST treat this as a decoding error.
|
||||
Expected: GOAWAY Frame (Error Code: COMPRESSION_ERROR)
|
||||
Connection closed
|
||||
*/
|
||||
|
||||
String encoded = "203f136687A0E41d139d090760881c6490B2Cd39Ba7f20";
|
||||
ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
HpackDecoder decoder = new HpackDecoder(4096, 8192);
|
||||
try
|
||||
{
|
||||
decoder.decode(buffer);
|
||||
Assert.fail();
|
||||
}
|
||||
catch(CompressionException e)
|
||||
{
|
||||
Assert.assertThat(e.getMessage(),Matchers.containsString("Dynamic table resize after fields"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTooBigToIndex() throws Exception
|
||||
{
|
||||
String encoded = "44FfEc02Df3990A190A0D4Ee5b3d2940Ec98Aa4a62D127D29e273a0aA20dEcAa190a503b262d8a2671D4A2672a927aA874988a2471D05510750c951139EdA2452a3a548cAa1aA90bE4B228342864A9E0D450A5474a92992a1aA513395448E3A0Aa17B96cFe3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f14E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F353F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F54f";
|
||||
String encoded = "3f610f17FfEc02Df3990A190A0D4Ee5b3d2940Ec98Aa4a62D127D29e273a0aA20dEcAa190a503b262d8a2671D4A2672a927aA874988a2471D05510750c951139EdA2452a3a548cAa1aA90bE4B228342864A9E0D450A5474a92992a1aA513395448E3A0Aa17B96cFe3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f3f14E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F3E7Cf9f3e7cF9F353F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F7F54f";
|
||||
ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
|
||||
|
||||
HpackDecoder decoder = new HpackDecoder(128,8192);
|
||||
MetaData metaData = decoder.decode(buffer);
|
||||
|
||||
assertThat(decoder.getHpackContext().getDynamicTableSize(),is(0));
|
||||
assertThat(((MetaData.Request)metaData).getURI().toString(),Matchers.startsWith("This is a very large field"));
|
||||
assertThat(metaData.getFields().get("host"),Matchers.startsWith("This is a very large field"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -430,131 +460,27 @@ public class HpackDecoderTest
|
|||
{
|
||||
Assert.assertThat(ex.getMessage(),Matchers.containsString("Duplicate"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
8.1.2.3. Request Pseudo-Header Fields
|
||||
|
||||
8.1.2.6. Malformed Requests and Responses
|
||||
[send] SETTINGS Frame (length:6, flags:0x00, stream_id:0)
|
||||
[recv] SETTINGS Frame (length:24, flags:0x00, stream_id:0)
|
||||
[send] SETTINGS Frame (length:0, flags:0x01, stream_id:0)
|
||||
[recv] WINDOW_UPDATE Frame (length:4, flags:0x00, stream_id:0)
|
||||
[recv] SETTINGS Frame (length:0, flags:0x01, stream_id:0)
|
||||
[send] HEADERS Frame (length:18, flags:0x04, stream_id:1)
|
||||
[send] DATA Frame (length:4, flags:0x01, stream_id:1)
|
||||
[recv] HEADERS Frame (length:100, flags:0x04, stream_id:1)
|
||||
[recv] DATA Frame (length:687, flags:0x01, stream_id:1)
|
||||
[recv] Timeout
|
||||
× 1: Sends a HEADERS frame with the "content-length" header field which does not equal the DATA frame payload length
|
||||
-> The endpoint MUST treat this as a stream error of type PROTOCOL_ERROR.
|
||||
Expected: GOAWAY Frame (Error Code: PROTOCOL_ERROR)
|
||||
RST_STREAM Frame (Error Code: PROTOCOL_ERROR)
|
||||
Connection closed
|
||||
Actual: DATA Frame (length:687, flags:0x01, stream_id:1)
|
||||
[send] SETTINGS Frame (length:6, flags:0x00, stream_id:0)
|
||||
[recv] SETTINGS Frame (length:24, flags:0x00, stream_id:0)
|
||||
[send] SETTINGS Frame (length:0, flags:0x01, stream_id:0)
|
||||
[recv] WINDOW_UPDATE Frame (length:4, flags:0x00, stream_id:0)
|
||||
[recv] SETTINGS Frame (length:0, flags:0x01, stream_id:0)
|
||||
[send] HEADERS Frame (length:18, flags:0x04, stream_id:1)
|
||||
[send] DATA Frame (length:4, flags:0x00, stream_id:1)
|
||||
[send] DATA Frame (length:4, flags:0x01, stream_id:1)
|
||||
[recv] HEADERS Frame (length:100, flags:0x04, stream_id:1)
|
||||
[recv] DATA Frame (length:687, flags:0x01, stream_id:1)
|
||||
[recv] Timeout
|
||||
× 2: Sends a HEADERS frame with the "content-length" header field which does not equal the sum of the multiple DATA frames payload length
|
||||
-> The endpoint MUST treat this as a stream error of type PROTOCOL_ERROR.
|
||||
Expected: GOAWAY Frame (Error Code: PROTOCOL_ERROR)
|
||||
RST_STREAM Frame (Error Code: PROTOCOL_ERROR)
|
||||
Connection closed
|
||||
Actual: DATA Frame (length:687, flags:0x01, stream_id:1)
|
||||
|
||||
8.2. Server Push
|
||||
[send] SETTINGS Frame (length:6, flags:0x00, stream_id:0)
|
||||
[recv] SETTINGS Frame (length:24, flags:0x00, stream_id:0)
|
||||
[send] SETTINGS Frame (length:0, flags:0x01, stream_id:0)
|
||||
[recv] WINDOW_UPDATE Frame (length:4, flags:0x00, stream_id:0)
|
||||
[recv] SETTINGS Frame (length:0, flags:0x01, stream_id:0)
|
||||
[send] PUSH_PROMISE Frame (length:19, flags:0x04, stream_id:1)
|
||||
[recv] GOAWAY Frame (length:20, flags:0x00, stream_id:0)
|
||||
✔ 1: Sends a PUSH_PROMISE frame
|
||||
|
||||
HPACK: Header Compression for HTTP/2
|
||||
2. Compression Process Overview
|
||||
2.3. Indexing Tables
|
||||
2.3.3. Index Address Space
|
||||
[send] SETTINGS Frame (length:6, flags:0x00, stream_id:0)
|
||||
[recv] SETTINGS Frame (length:24, flags:0x00, stream_id:0)
|
||||
[send] SETTINGS Frame (length:0, flags:0x01, stream_id:0)
|
||||
[recv] WINDOW_UPDATE Frame (length:4, flags:0x00, stream_id:0)
|
||||
[recv] SETTINGS Frame (length:0, flags:0x01, stream_id:0)
|
||||
[send] HEADERS Frame (length:16, flags:0x05, stream_id:1)
|
||||
[recv] GOAWAY Frame (length:20, flags:0x00, stream_id:0)
|
||||
[recv] Connection closed
|
||||
✔ 1: Sends a header field representation with invalid index
|
||||
|
||||
4. Dynamic Table Management
|
||||
4.2. Maximum Table Size
|
||||
[send] SETTINGS Frame (length:6, flags:0x00, stream_id:0)
|
||||
[recv] SETTINGS Frame (length:24, flags:0x00, stream_id:0)
|
||||
[send] SETTINGS Frame (length:0, flags:0x01, stream_id:0)
|
||||
[recv] WINDOW_UPDATE Frame (length:4, flags:0x00, stream_id:0)
|
||||
[recv] SETTINGS Frame (length:0, flags:0x01, stream_id:0)
|
||||
[send] HEADERS Frame (length:16, flags:0x05, stream_id:1)
|
||||
[recv] HEADERS Frame (length:101, flags:0x04, stream_id:1)
|
||||
[recv] DATA Frame (length:687, flags:0x01, stream_id:1)
|
||||
[recv] Timeout
|
||||
× 1: Sends a dynamic table size update at the end of header block
|
||||
-> The endpoint MUST treat this as a decoding error.
|
||||
Expected: GOAWAY Frame (Error Code: COMPRESSION_ERROR)
|
||||
Connection closed
|
||||
Actual: DATA Frame (length:687, flags:0x01, stream_id:1)
|
||||
8.1.2.6. Malformed Requests and Responses
|
||||
× 1: Sends a HEADERS frame with the "content-length" header field which does not equal the DATA frame payload length
|
||||
× 2: Sends a HEADERS frame with the "content-length" header field which does not equal the sum of the multiple DATA frames payload length
|
||||
|
||||
5. Primitive Type Representations
|
||||
5.2. String Literal Representation
|
||||
[send] SETTINGS Frame (length:6, flags:0x00, stream_id:0)
|
||||
[recv] SETTINGS Frame (length:24, flags:0x00, stream_id:0)
|
||||
[send] SETTINGS Frame (length:0, flags:0x01, stream_id:0)
|
||||
[recv] WINDOW_UPDATE Frame (length:4, flags:0x00, stream_id:0)
|
||||
[recv] SETTINGS Frame (length:0, flags:0x01, stream_id:0)
|
||||
[send] HEADERS Frame (length:27, flags:0x05, stream_id:1)
|
||||
[recv] HEADERS Frame (length:101, flags:0x04, stream_id:1)
|
||||
[recv] DATA Frame (length:687, flags:0x01, stream_id:1)
|
||||
[recv] Timeout
|
||||
× 1: Sends a Huffman-encoded string literal representation with padding longer than 7 bits
|
||||
-> The endpoint MUST treat this as a decoding error.
|
||||
Expected: GOAWAY Frame (Error Code: COMPRESSION_ERROR)
|
||||
Connection closed
|
||||
Actual: DATA Frame (length:687, flags:0x01, stream_id:1)
|
||||
[send] SETTINGS Frame (length:6, flags:0x00, stream_id:0)
|
||||
[recv] SETTINGS Frame (length:24, flags:0x00, stream_id:0)
|
||||
[send] SETTINGS Frame (length:0, flags:0x01, stream_id:0)
|
||||
[recv] WINDOW_UPDATE Frame (length:4, flags:0x00, stream_id:0)
|
||||
[recv] SETTINGS Frame (length:0, flags:0x01, stream_id:0)
|
||||
[send] HEADERS Frame (length:26, flags:0x05, stream_id:1)
|
||||
[recv] HEADERS Frame (length:101, flags:0x04, stream_id:1)
|
||||
[recv] DATA Frame (length:687, flags:0x01, stream_id:1)
|
||||
[recv] Timeout
|
||||
× 2: Sends a Huffman-encoded string literal representation padded by zero
|
||||
-> The endpoint MUST treat this as a decoding error.
|
||||
Expected: GOAWAY Frame (Error Code: COMPRESSION_ERROR)
|
||||
Connection closed
|
||||
Actual: DATA Frame (length:687, flags:0x01, stream_id:1)
|
||||
[send] SETTINGS Frame (length:6, flags:0x00, stream_id:0)
|
||||
[recv] SETTINGS Frame (length:24, flags:0x00, stream_id:0)
|
||||
[send] SETTINGS Frame (length:0, flags:0x01, stream_id:0)
|
||||
[recv] WINDOW_UPDATE Frame (length:4, flags:0x00, stream_id:0)
|
||||
[recv] SETTINGS Frame (length:0, flags:0x01, stream_id:0)
|
||||
[send] HEADERS Frame (length:28, flags:0x05, stream_id:1)
|
||||
[recv] GOAWAY Frame (length:20, flags:0x00, stream_id:0)
|
||||
[recv] Connection closed
|
||||
✔ 3: Sends a Huffman-encoded string literal representation containing the EOS symbol
|
||||
|
||||
*/
|
||||
|
||||
}
|
||||
|
|
|
@ -249,4 +249,28 @@ public class HpackEncoderTest
|
|||
context.get(HpackContext.STATIC_SIZE+1).getSize()+context.get(HpackContext.STATIC_SIZE+2).getSize()));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResize()
|
||||
{
|
||||
HttpFields fields = new HttpFields();
|
||||
fields.add("host", "localhost0");
|
||||
fields.add("cookie","abcdefghij");
|
||||
|
||||
HpackEncoder encoder = new HpackEncoder(4096);
|
||||
|
||||
ByteBuffer buffer = BufferUtil.allocate(4096);
|
||||
int pos = BufferUtil.flipToFill(buffer);
|
||||
encoder.encodeMaxDynamicTableSize(buffer,0);
|
||||
encoder.setRemoteMaxDynamicTableSize(50);
|
||||
encoder.encode(buffer,new MetaData(HttpVersion.HTTP_2,fields));
|
||||
BufferUtil.flipToFlush(buffer,pos);
|
||||
|
||||
HpackContext context = encoder.getHpackContext();
|
||||
|
||||
Assert.assertThat(context.getMaxDynamicTableSize(),Matchers.is(50));
|
||||
Assert.assertThat(context.size(),Matchers.is(1));
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
|
||||
org.eclipse.jetty.http2.LEVEL=INFO
|
||||
org.eclipse.jetty.http2.hpack.LEVEL=DEBUG
|
||||
org.eclipse.jetty.http2.hpack.LEVEL=INFO
|
||||
|
|
Loading…
Reference in New Issue