* Validate dynamic table size updates.
* Set dynamic table size to 0, to reduce a per-connection ~2048 byte allocation to a ~0 byte allocation.
Resolves dotnet/corefx#39666
Commit migrated from https://github.com/dotnet/corefx/commit/
14b604c5fe52e0022881a0866bf39f344d247fd7
return bytesGenerated;
}
+ public static int EncodeDynamicTableSizeUpdate(int newMaximumSize, Span<byte> headerBlock)
+ {
+ return EncodeInteger(newMaximumSize, 0b00100000, 0b11100000, headerBlock);
+ }
+
public async Task<byte[]> ReadBodyAsync()
{
byte[] body = null;
<data name="net_http_disposed_while_in_use" xml:space="preserve">
<value>The object was disposed while operations were in progress.</value>
</data>
-</root>
+ <data name="net_http_hpack_large_table_size_update" xml:space="preserve">
+ <value>Dynamic table size update to {0} bytes exceeds limit of {1} bytes.</value>
+ </data>
+</root>
\ No newline at end of file
if (_integerDecoder.StartDecode((byte)(b & ~DynamicTableSizeUpdateMask), DynamicTableSizeUpdatePrefix))
{
- // TODO: validate that it's less than what's defined via SETTINGS
+ if (_integerDecoder.Value > _maxDynamicTableSize)
+ {
+ // Dynamic table size update is too large.
+ throw new HPackDecodingException(SR.Format(SR.net_http_hpack_large_table_size_update, _integerDecoder.Value, _maxDynamicTableSize));
+ }
+
_dynamicTable.Resize(_integerDecoder.Value);
}
else
if (_integerDecoder.Value > _maxDynamicTableSize)
{
// Dynamic table size update is too large.
- throw new HPackDecodingException();
+ throw new HPackDecodingException(SR.Format(SR.net_http_hpack_large_table_size_update, _integerDecoder.Value, _maxDynamicTableSize));
}
_dynamicTable.Resize(_integerDecoder.Value);
_outgoingBuffer = new ArrayBuffer(InitialConnectionBufferSize);
_headerBuffer = new ArrayBuffer(InitialConnectionBufferSize);
- _hpackDecoder = new HPackDecoder(maxResponseHeadersLength: pool.Settings._maxResponseHeadersLength * 1024);
+ _hpackDecoder = new HPackDecoder(maxDynamicTableSize: 0, maxResponseHeadersLength: pool.Settings._maxResponseHeadersLength * 1024);
_httpStreams = new Dictionary<int, Http2Stream>();
await con.ShutdownIgnoringErrorsAsync(streamId);
});
}
+
+ [Fact]
+ public async Task DynamicTableSizeUpdate_Exceeds_Settings_Throws()
+ {
+ await Http2LoopbackServer.CreateClientAndServerAsync(
+ async uri =>
+ {
+ using HttpClient client = CreateHttpClient();
+ Exception e = await Assert.ThrowsAsync<HttpRequestException>(() => client.GetAsync(uri));
+
+ Assert.NotNull(e.InnerException);
+ Assert.Contains("Dynamic table size update", e.InnerException.Message);
+ },
+ async server =>
+ {
+ (Http2LoopbackConnection con, SettingsFrame settings) = await server.EstablishConnectionGetSettingsAsync();
+ int streamId = await con.ReadRequestHeaderAsync();
+
+ int headerTableSize = 4096; // Default per HTTP2 spec.
+
+ foreach (SettingsEntry setting in settings.Entries)
+ {
+ if (setting.SettingId == SettingId.HeaderTableSize)
+ {
+ headerTableSize = (int)setting.Value;
+ }
+ }
+
+ byte[] headerData = new byte[16];
+ int headersLen = Http2LoopbackConnection.EncodeDynamicTableSizeUpdate(headerTableSize + 1, headerData);
+ HeadersFrame frame = new HeadersFrame(headerData.AsMemory(0, headersLen), FrameFlags.EndHeaders | FrameFlags.EndStream, 0, 0, 0, streamId);
+
+ await con.WriteFrameAsync(frame);
+ await con.ShutdownIgnoringErrorsAsync(streamId);
+ });
+ }
}
}