Merge branch 'release/2.2'
This commit is contained in:
commit
73e56084fd
|
|
@ -1,17 +1,17 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<root>
|
<root>
|
||||||
<!--
|
<!--
|
||||||
Microsoft ResX Schema
|
Microsoft ResX Schema
|
||||||
|
|
||||||
Version 2.0
|
Version 2.0
|
||||||
|
|
||||||
The primary goals of this format is to allow a simple XML format
|
The primary goals of this format is to allow a simple XML format
|
||||||
that is mostly human readable. The generation and parsing of the
|
that is mostly human readable. The generation and parsing of the
|
||||||
various data types are done through the TypeConverter classes
|
various data types are done through the TypeConverter classes
|
||||||
associated with the data types.
|
associated with the data types.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
... ado.net/XML headers & schema ...
|
... ado.net/XML headers & schema ...
|
||||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
<resheader name="version">2.0</resheader>
|
<resheader name="version">2.0</resheader>
|
||||||
|
|
@ -26,36 +26,36 @@
|
||||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
<comment>This is a comment</comment>
|
<comment>This is a comment</comment>
|
||||||
</data>
|
</data>
|
||||||
|
|
||||||
There are any number of "resheader" rows that contain simple
|
There are any number of "resheader" rows that contain simple
|
||||||
name/value pairs.
|
name/value pairs.
|
||||||
|
|
||||||
Each data row contains a name, and value. The row also contains a
|
Each data row contains a name, and value. The row also contains a
|
||||||
type or mimetype. Type corresponds to a .NET class that support
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
text/value conversion through the TypeConverter architecture.
|
text/value conversion through the TypeConverter architecture.
|
||||||
Classes that don't support this are serialized and stored with the
|
Classes that don't support this are serialized and stored with the
|
||||||
mimetype set.
|
mimetype set.
|
||||||
|
|
||||||
The mimetype is used for serialized objects, and tells the
|
The mimetype is used for serialized objects, and tells the
|
||||||
ResXResourceReader how to depersist the object. This is currently not
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
extensible. For a given mimetype the value must be set accordingly:
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
that the ResXResourceWriter will generate, however the reader can
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
read any of the formats listed below.
|
read any of the formats listed below.
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.binary.base64
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
value : The object must be serialized with
|
value : The object must be serialized with
|
||||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.soap.base64
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
value : The object must be serialized with
|
value : The object must be serialized with
|
||||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
value : The object must be serialized into a byte array
|
value : The object must be serialized into a byte array
|
||||||
: using a System.ComponentModel.TypeConverter
|
: using a System.ComponentModel.TypeConverter
|
||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
-->
|
-->
|
||||||
|
|
@ -578,4 +578,7 @@ For more information on configuring HTTPS see https://go.microsoft.com/fwlink/?l
|
||||||
<data name="ArgumentOutOfRange" xml:space="preserve">
|
<data name="ArgumentOutOfRange" xml:space="preserve">
|
||||||
<value>A value between {min} and {max} is required.</value>
|
<value>A value between {min} and {max} is required.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="HPackErrorDynamicTableSizeUpdateNotAtBeginningOfHeaderBlock" xml:space="preserve">
|
||||||
|
<value>Dynamic tables size update did not occur at the beginning of the first header block.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -94,6 +94,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.HPack
|
||||||
private int _headerValueLength;
|
private int _headerValueLength;
|
||||||
private bool _index;
|
private bool _index;
|
||||||
private bool _huffman;
|
private bool _huffman;
|
||||||
|
private bool _headersObserved;
|
||||||
|
|
||||||
public HPackDecoder(int maxDynamicTableSize)
|
public HPackDecoder(int maxDynamicTableSize)
|
||||||
: this(maxDynamicTableSize, new DynamicTable(maxDynamicTableSize))
|
: this(maxDynamicTableSize, new DynamicTable(maxDynamicTableSize))
|
||||||
|
|
@ -115,19 +116,25 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.HPack
|
||||||
OnByte(data[i], handler);
|
OnByte(data[i], handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (endHeaders && _state != State.Ready)
|
if (endHeaders)
|
||||||
{
|
{
|
||||||
throw new HPackDecodingException(CoreStrings.HPackErrorIncompleteHeaderBlock);
|
if (_state != State.Ready)
|
||||||
|
{
|
||||||
|
throw new HPackDecodingException(CoreStrings.HPackErrorIncompleteHeaderBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
_headersObserved = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnByte(byte b, IHttpHeadersHandler handler)
|
private void OnByte(byte b, IHttpHeadersHandler handler)
|
||||||
{
|
{
|
||||||
switch (_state)
|
switch (_state)
|
||||||
{
|
{
|
||||||
case State.Ready:
|
case State.Ready:
|
||||||
if ((b & IndexedHeaderFieldMask) == IndexedHeaderFieldRepresentation)
|
if ((b & IndexedHeaderFieldMask) == IndexedHeaderFieldRepresentation)
|
||||||
{
|
{
|
||||||
|
_headersObserved = true;
|
||||||
var val = b & ~IndexedHeaderFieldMask;
|
var val = b & ~IndexedHeaderFieldMask;
|
||||||
|
|
||||||
if (_integerDecoder.BeginDecode((byte)val, IndexedHeaderFieldPrefix))
|
if (_integerDecoder.BeginDecode((byte)val, IndexedHeaderFieldPrefix))
|
||||||
|
|
@ -141,6 +148,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.HPack
|
||||||
}
|
}
|
||||||
else if ((b & LiteralHeaderFieldWithIncrementalIndexingMask) == LiteralHeaderFieldWithIncrementalIndexingRepresentation)
|
else if ((b & LiteralHeaderFieldWithIncrementalIndexingMask) == LiteralHeaderFieldWithIncrementalIndexingRepresentation)
|
||||||
{
|
{
|
||||||
|
_headersObserved = true;
|
||||||
_index = true;
|
_index = true;
|
||||||
var val = b & ~LiteralHeaderFieldWithIncrementalIndexingMask;
|
var val = b & ~LiteralHeaderFieldWithIncrementalIndexingMask;
|
||||||
|
|
||||||
|
|
@ -159,6 +167,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.HPack
|
||||||
}
|
}
|
||||||
else if ((b & LiteralHeaderFieldWithoutIndexingMask) == LiteralHeaderFieldWithoutIndexingRepresentation)
|
else if ((b & LiteralHeaderFieldWithoutIndexingMask) == LiteralHeaderFieldWithoutIndexingRepresentation)
|
||||||
{
|
{
|
||||||
|
_headersObserved = true;
|
||||||
_index = false;
|
_index = false;
|
||||||
var val = b & ~LiteralHeaderFieldWithoutIndexingMask;
|
var val = b & ~LiteralHeaderFieldWithoutIndexingMask;
|
||||||
|
|
||||||
|
|
@ -177,6 +186,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.HPack
|
||||||
}
|
}
|
||||||
else if ((b & LiteralHeaderFieldNeverIndexedMask) == LiteralHeaderFieldNeverIndexedRepresentation)
|
else if ((b & LiteralHeaderFieldNeverIndexedMask) == LiteralHeaderFieldNeverIndexedRepresentation)
|
||||||
{
|
{
|
||||||
|
_headersObserved = true;
|
||||||
_index = false;
|
_index = false;
|
||||||
var val = b & ~LiteralHeaderFieldNeverIndexedMask;
|
var val = b & ~LiteralHeaderFieldNeverIndexedMask;
|
||||||
|
|
||||||
|
|
@ -195,10 +205,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.HPack
|
||||||
}
|
}
|
||||||
else if ((b & DynamicTableSizeUpdateMask) == DynamicTableSizeUpdateRepresentation)
|
else if ((b & DynamicTableSizeUpdateMask) == DynamicTableSizeUpdateRepresentation)
|
||||||
{
|
{
|
||||||
|
// https://tools.ietf.org/html/rfc7541#section-4.2
|
||||||
|
// This dynamic table size
|
||||||
|
// update MUST occur at the beginning of the first header block
|
||||||
|
// following the change to the dynamic table size.
|
||||||
|
if (_headersObserved)
|
||||||
|
{
|
||||||
|
throw new HPackDecodingException(CoreStrings.HPackErrorDynamicTableSizeUpdateNotAtBeginningOfHeaderBlock);
|
||||||
|
}
|
||||||
|
|
||||||
if (_integerDecoder.BeginDecode((byte)(b & ~DynamicTableSizeUpdateMask), DynamicTableSizeUpdatePrefix))
|
if (_integerDecoder.BeginDecode((byte)(b & ~DynamicTableSizeUpdateMask), DynamicTableSizeUpdatePrefix))
|
||||||
{
|
{
|
||||||
// TODO: validate that it's less than what's defined via SETTINGS
|
SetDynamicHeaderTableSize(_integerDecoder.Value);
|
||||||
_dynamicTable.Resize(_integerDecoder.Value);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -295,13 +313,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.HPack
|
||||||
case State.DynamicTableSizeUpdate:
|
case State.DynamicTableSizeUpdate:
|
||||||
if (_integerDecoder.Decode(b))
|
if (_integerDecoder.Decode(b))
|
||||||
{
|
{
|
||||||
if (_integerDecoder.Value > _maxDynamicTableSize)
|
SetDynamicHeaderTableSize(_integerDecoder.Value);
|
||||||
{
|
|
||||||
throw new HPackDecodingException(
|
|
||||||
CoreStrings.FormatHPackErrorDynamicTableSizeUpdateTooLarge(_integerDecoder.Value, _maxDynamicTableSize));
|
|
||||||
}
|
|
||||||
|
|
||||||
_dynamicTable.Resize(_integerDecoder.Value);
|
|
||||||
_state = State.Ready;
|
_state = State.Ready;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -360,7 +372,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.HPack
|
||||||
{
|
{
|
||||||
if (_huffman)
|
if (_huffman)
|
||||||
{
|
{
|
||||||
return Huffman.Decode(_stringOctets, 0, _stringLength, dst);
|
return Huffman.Decode(new ReadOnlySpan<byte>(_stringOctets, 0, _stringLength), dst);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -402,5 +414,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.HPack
|
||||||
throw new HPackDecodingException(CoreStrings.FormatHPackErrorIndexOutOfRange(index), ex);
|
throw new HPackDecodingException(CoreStrings.FormatHPackErrorIndexOutOfRange(index), ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SetDynamicHeaderTableSize(int size)
|
||||||
|
{
|
||||||
|
if (size > _maxDynamicTableSize)
|
||||||
|
{
|
||||||
|
throw new HPackDecodingException(
|
||||||
|
CoreStrings.FormatHPackErrorDynamicTableSizeUpdateTooLarge(size, _maxDynamicTableSize));
|
||||||
|
}
|
||||||
|
|
||||||
|
_dynamicTable.Resize(size);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -303,24 +303,26 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.HPack
|
||||||
/// Decodes a Huffman encoded string from a byte array.
|
/// Decodes a Huffman encoded string from a byte array.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="src">The source byte array containing the encoded data.</param>
|
/// <param name="src">The source byte array containing the encoded data.</param>
|
||||||
/// <param name="offset">The offset in the byte array where the coded data starts.</param>
|
|
||||||
/// <param name="count">The number of bytes to decode.</param>
|
|
||||||
/// <param name="dst">The destination byte array to store the decoded data.</param>
|
/// <param name="dst">The destination byte array to store the decoded data.</param>
|
||||||
/// <returns>The number of decoded symbols.</returns>
|
/// <returns>The number of decoded symbols.</returns>
|
||||||
public static int Decode(byte[] src, int offset, int count, byte[] dst)
|
public static int Decode(ReadOnlySpan<byte> src, Span<byte> dst)
|
||||||
{
|
{
|
||||||
var i = offset;
|
var i = 0;
|
||||||
var j = 0;
|
var j = 0;
|
||||||
var lastDecodedBits = 0;
|
var lastDecodedBits = 0;
|
||||||
while (i < count)
|
while (i < src.Length)
|
||||||
{
|
{
|
||||||
|
// Note that if lastDecodeBits is 3 or more, then we will only get 5 bits (or less)
|
||||||
|
// from src[i]. Thus we need to read 5 bytes here to ensure that we always have
|
||||||
|
// at least 30 bits available for decoding.
|
||||||
var next = (uint)(src[i] << 24 + lastDecodedBits);
|
var next = (uint)(src[i] << 24 + lastDecodedBits);
|
||||||
next |= (i + 1 < src.Length ? (uint)(src[i + 1] << 16 + lastDecodedBits) : 0);
|
next |= (i + 1 < src.Length ? (uint)(src[i + 1] << 16 + lastDecodedBits) : 0);
|
||||||
next |= (i + 2 < src.Length ? (uint)(src[i + 2] << 8 + lastDecodedBits) : 0);
|
next |= (i + 2 < src.Length ? (uint)(src[i + 2] << 8 + lastDecodedBits) : 0);
|
||||||
next |= (i + 3 < src.Length ? (uint)(src[i + 3] << lastDecodedBits) : 0);
|
next |= (i + 3 < src.Length ? (uint)(src[i + 3] << lastDecodedBits) : 0);
|
||||||
|
next |= (i + 4 < src.Length ? (uint)(src[i + 4] >> (8 - lastDecodedBits)) : 0);
|
||||||
|
|
||||||
var ones = (uint)(int.MinValue >> (8 - lastDecodedBits - 1));
|
var ones = (uint)(int.MinValue >> (8 - lastDecodedBits - 1));
|
||||||
if (i == count - 1 && lastDecodedBits > 0 && (next & ones) == ones)
|
if (i == src.Length - 1 && lastDecodedBits > 0 && (next & ones) == ones)
|
||||||
{
|
{
|
||||||
// The remaining 7 or less bits are all 1, which is padding.
|
// The remaining 7 or less bits are all 1, which is padding.
|
||||||
// We specifically check that lastDecodedBits > 0 because padding
|
// We specifically check that lastDecodedBits > 0 because padding
|
||||||
|
|
@ -332,8 +334,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.HPack
|
||||||
// The longest possible symbol size is 30 bits. If we're at the last 4 bytes
|
// The longest possible symbol size is 30 bits. If we're at the last 4 bytes
|
||||||
// of the input, we need to make sure we pass the correct number of valid bits
|
// of the input, we need to make sure we pass the correct number of valid bits
|
||||||
// left, otherwise the trailing 0s in next may form a valid symbol.
|
// left, otherwise the trailing 0s in next may form a valid symbol.
|
||||||
var validBits = Math.Min(30, (8 - lastDecodedBits) + (count - i - 1) * 8);
|
var validBits = Math.Min(30, (8 - lastDecodedBits) + (src.Length - i - 1) * 8);
|
||||||
var ch = Decode(next, validBits, out var decodedBits);
|
var ch = DecodeValue(next, validBits, out var decodedBits);
|
||||||
|
|
||||||
if (ch == -1)
|
if (ch == -1)
|
||||||
{
|
{
|
||||||
|
|
@ -377,7 +379,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.HPack
|
||||||
/// </param>
|
/// </param>
|
||||||
/// <param name="decodedBits">The number of bits decoded from <paramref name="data"/>.</param>
|
/// <param name="decodedBits">The number of bits decoded from <paramref name="data"/>.</param>
|
||||||
/// <returns>The decoded symbol.</returns>
|
/// <returns>The decoded symbol.</returns>
|
||||||
public static int Decode(uint data, int validBits, out int decodedBits)
|
internal static int DecodeValue(uint data, int validBits, out int decodedBits)
|
||||||
{
|
{
|
||||||
// The code below implements the decoding logic for a canonical Huffman code.
|
// The code below implements the decoding logic for a canonical Huffman code.
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -2156,6 +2156,20 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
||||||
internal static string FormatArgumentOutOfRange(object min, object max)
|
internal static string FormatArgumentOutOfRange(object min, object max)
|
||||||
=> string.Format(CultureInfo.CurrentCulture, GetString("ArgumentOutOfRange", "min", "max"), min, max);
|
=> string.Format(CultureInfo.CurrentCulture, GetString("ArgumentOutOfRange", "min", "max"), min, max);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dynamic tables size update did not occur at the beginning of the first header block.
|
||||||
|
/// </summary>
|
||||||
|
internal static string HPackErrorDynamicTableSizeUpdateNotAtBeginningOfHeaderBlock
|
||||||
|
{
|
||||||
|
get => GetString("HPackErrorDynamicTableSizeUpdateNotAtBeginningOfHeaderBlock");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dynamic tables size update did not occur at the beginning of the first header block.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatHPackErrorDynamicTableSizeUpdateNotAtBeginningOfHeaderBlock()
|
||||||
|
=> GetString("HPackErrorDynamicTableSizeUpdateNotAtBeginningOfHeaderBlock");
|
||||||
|
|
||||||
private static string GetString(string name, params string[] formatterNames)
|
private static string GetString(string name, params string[] formatterNames)
|
||||||
{
|
{
|
||||||
var value = _resourceManager.GetString(name);
|
var value = _resourceManager.GetString(name);
|
||||||
|
|
|
||||||
|
|
@ -375,6 +375,47 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
||||||
Assert.Empty(_decodedHeaders);
|
Assert.Empty(_decodedHeaders);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DecodesDynamicTableSizeUpdate_AfterIndexedHeaderStatic_Error()
|
||||||
|
{
|
||||||
|
// 001 (Dynamic Table Size Update)
|
||||||
|
// 11110 (30 encoded with 5-bit prefix - see http://httpwg.org/specs/rfc7541.html#integer.representation)
|
||||||
|
|
||||||
|
Assert.Equal(DynamicTableInitialMaxSize, _dynamicTable.MaxSize);
|
||||||
|
|
||||||
|
var exception = Assert.Throws<HPackDecodingException>(() => _decoder.Decode(_indexedHeaderStatic.Concat(new byte[] { 0x3e }).ToArray(), endHeaders: true, handler: this));
|
||||||
|
Assert.Equal(CoreStrings.HPackErrorDynamicTableSizeUpdateNotAtBeginningOfHeaderBlock, exception.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DecodesDynamicTableSizeUpdate_AfterIndexedHeaderStatic_SubsequentDecodeCall_Error()
|
||||||
|
{
|
||||||
|
Assert.Equal(DynamicTableInitialMaxSize, _dynamicTable.MaxSize);
|
||||||
|
|
||||||
|
_decoder.Decode(_indexedHeaderStatic, endHeaders: false, handler: this);
|
||||||
|
Assert.Equal("GET", _decodedHeaders[HeaderNames.Method]);
|
||||||
|
|
||||||
|
// 001 (Dynamic Table Size Update)
|
||||||
|
// 11110 (30 encoded with 5-bit prefix - see http://httpwg.org/specs/rfc7541.html#integer.representation)
|
||||||
|
var exception = Assert.Throws<HPackDecodingException>(() => _decoder.Decode(new byte[] { 0x3e }, endHeaders: true, handler: this));
|
||||||
|
Assert.Equal(CoreStrings.HPackErrorDynamicTableSizeUpdateNotAtBeginningOfHeaderBlock, exception.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DecodesDynamicTableSizeUpdate_AfterIndexedHeaderStatic_ResetAfterEndHeaders_Succeeds()
|
||||||
|
{
|
||||||
|
Assert.Equal(DynamicTableInitialMaxSize, _dynamicTable.MaxSize);
|
||||||
|
|
||||||
|
_decoder.Decode(_indexedHeaderStatic, endHeaders: true, handler: this);
|
||||||
|
Assert.Equal("GET", _decodedHeaders[HeaderNames.Method]);
|
||||||
|
|
||||||
|
// 001 (Dynamic Table Size Update)
|
||||||
|
// 11110 (30 encoded with 5-bit prefix - see http://httpwg.org/specs/rfc7541.html#integer.representation)
|
||||||
|
_decoder.Decode(new byte[] { 0x3e }, endHeaders: true, handler: this);
|
||||||
|
|
||||||
|
Assert.Equal(30, _dynamicTable.MaxSize);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void DecodesDynamicTableSizeUpdate_GreaterThanLimit_Error()
|
public void DecodesDynamicTableSizeUpdate_GreaterThanLimit_Error()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.HPack;
|
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.HPack;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
@ -67,7 +68,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
||||||
public void HuffmanDecodeArray(byte[] encoded, byte[] expected)
|
public void HuffmanDecodeArray(byte[] encoded, byte[] expected)
|
||||||
{
|
{
|
||||||
var dst = new byte[expected.Length];
|
var dst = new byte[expected.Length];
|
||||||
Assert.Equal(expected.Length, Huffman.Decode(encoded, 0, encoded.Length, dst));
|
Assert.Equal(expected.Length, Huffman.Decode(new ReadOnlySpan<byte>(encoded), dst));
|
||||||
Assert.Equal(expected, dst);
|
Assert.Equal(expected, dst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -87,7 +88,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
||||||
[MemberData(nameof(_longPaddingData))]
|
[MemberData(nameof(_longPaddingData))]
|
||||||
public void ThrowsOnPaddingLongerThanSevenBits(byte[] encoded)
|
public void ThrowsOnPaddingLongerThanSevenBits(byte[] encoded)
|
||||||
{
|
{
|
||||||
var exception = Assert.Throws<HuffmanDecodingException>(() => Huffman.Decode(encoded, 0, encoded.Length, new byte[encoded.Length * 2]));
|
var exception = Assert.Throws<HuffmanDecodingException>(() => Huffman.Decode(new ReadOnlySpan<byte>(encoded), new byte[encoded.Length * 2]));
|
||||||
Assert.Equal(CoreStrings.HPackHuffmanErrorIncomplete, exception.Message);
|
Assert.Equal(CoreStrings.HPackHuffmanErrorIncomplete, exception.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -103,7 +104,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
||||||
[MemberData(nameof(_eosData))]
|
[MemberData(nameof(_eosData))]
|
||||||
public void ThrowsOnEOS(byte[] encoded)
|
public void ThrowsOnEOS(byte[] encoded)
|
||||||
{
|
{
|
||||||
var exception = Assert.Throws<HuffmanDecodingException>(() => Huffman.Decode(encoded, 0, encoded.Length, new byte[encoded.Length * 2]));
|
var exception = Assert.Throws<HuffmanDecodingException>(() => Huffman.Decode(new ReadOnlySpan<byte>(encoded), new byte[encoded.Length * 2]));
|
||||||
Assert.Equal(CoreStrings.HPackHuffmanErrorEOS, exception.Message);
|
Assert.Equal(CoreStrings.HPackHuffmanErrorEOS, exception.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -112,7 +113,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
||||||
{
|
{
|
||||||
// h e l l o *
|
// h e l l o *
|
||||||
var encoded = new byte[] { 0b100111_00, 0b101_10100, 0b0_101000_0, 0b0111_1111 };
|
var encoded = new byte[] { 0b100111_00, 0b101_10100, 0b0_101000_0, 0b0111_1111 };
|
||||||
var exception = Assert.Throws<HuffmanDecodingException>(() => Huffman.Decode(encoded, 0, encoded.Length, new byte[encoded.Length]));
|
var exception = Assert.Throws<HuffmanDecodingException>(() => Huffman.Decode(new ReadOnlySpan<byte>(encoded), new byte[encoded.Length]));
|
||||||
Assert.Equal(CoreStrings.HPackHuffmanErrorDestinationTooSmall, exception.Message);
|
Assert.Equal(CoreStrings.HPackHuffmanErrorDestinationTooSmall, exception.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -144,10 +145,23 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
||||||
[MemberData(nameof(_incompleteSymbolData))]
|
[MemberData(nameof(_incompleteSymbolData))]
|
||||||
public void ThrowsOnIncompleteSymbol(byte[] encoded)
|
public void ThrowsOnIncompleteSymbol(byte[] encoded)
|
||||||
{
|
{
|
||||||
var exception = Assert.Throws<HuffmanDecodingException>(() => Huffman.Decode(encoded, 0, encoded.Length, new byte[encoded.Length * 2]));
|
var exception = Assert.Throws<HuffmanDecodingException>(() => Huffman.Decode(new ReadOnlySpan<byte>(encoded), new byte[encoded.Length * 2]));
|
||||||
Assert.Equal(CoreStrings.HPackHuffmanErrorIncomplete, exception.Message);
|
Assert.Equal(CoreStrings.HPackHuffmanErrorIncomplete, exception.Message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DecodeCharactersThatSpans5Octets()
|
||||||
|
{
|
||||||
|
var expectedLength = 2;
|
||||||
|
var decodedBytes = new byte[expectedLength];
|
||||||
|
// B LF EOS
|
||||||
|
var encoded = new byte[] { 0b1011101_1, 0b11111111, 0b11111111, 0b11111111, 0b11100_111 };
|
||||||
|
var decodedLength = Huffman.Decode(new ReadOnlySpan<byte>(encoded, 0, encoded.Length), decodedBytes);
|
||||||
|
|
||||||
|
Assert.Equal(expectedLength, decodedLength);
|
||||||
|
Assert.Equal(new byte [] { (byte)'B', (byte)'\n' }, decodedBytes);
|
||||||
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[MemberData(nameof(HuffmanData))]
|
[MemberData(nameof(HuffmanData))]
|
||||||
public void HuffmanEncode(int code, uint expectedEncoded, int expectedBitLength)
|
public void HuffmanEncode(int code, uint expectedEncoded, int expectedBitLength)
|
||||||
|
|
@ -161,7 +175,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
||||||
[MemberData(nameof(HuffmanData))]
|
[MemberData(nameof(HuffmanData))]
|
||||||
public void HuffmanDecode(int code, uint encoded, int bitLength)
|
public void HuffmanDecode(int code, uint encoded, int bitLength)
|
||||||
{
|
{
|
||||||
Assert.Equal(code, Huffman.Decode(encoded, bitLength, out var decodedBits));
|
Assert.Equal(code, Huffman.DecodeValue(encoded, bitLength, out var decodedBits));
|
||||||
Assert.Equal(bitLength, decodedBits);
|
Assert.Equal(bitLength, decodedBits);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -176,7 +190,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
||||||
#pragma warning restore xUnit1026
|
#pragma warning restore xUnit1026
|
||||||
int bitLength)
|
int bitLength)
|
||||||
{
|
{
|
||||||
Assert.Equal(code, Huffman.Decode(Huffman.Encode(code).encoded, bitLength, out var decodedBits));
|
Assert.Equal(code, Huffman.DecodeValue(Huffman.Encode(code).encoded, bitLength, out var decodedBits));
|
||||||
Assert.Equal(bitLength, decodedBits);
|
Assert.Equal(bitLength, decodedBits);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests.Http2
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var dataset = new TheoryData<H2SpecTestCase>();
|
var dataset = new TheoryData<H2SpecTestCase>();
|
||||||
var toSkip = new[] { "hpack/4.2/1", "http2/5.1/8" };
|
var toSkip = new[] { "http2/5.1/8" };
|
||||||
|
|
||||||
foreach (var testcase in H2SpecCommands.EnumerateTestCases())
|
foreach (var testcase in H2SpecCommands.EnumerateTestCases())
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue