From 2c48269661dc5343ab0b9482df97cfaf5de76ba8 Mon Sep 17 00:00:00 2001 From: Tratcher Date: Wed, 19 Apr 2023 20:34:57 +0000 Subject: [PATCH 1/3] Sync shared code from runtime --- .../runtime/Http2/Hpack/HPackDecoder.cs | 11 +- .../runtime/Http3/QPack/QPackDecoder.cs | 4 +- .../runtime/Http2/HPackDecoderTest.cs | 106 ++++++++++++++++++ .../runtime/Http3/QPackDecoderTest.cs | 105 ++++++++++++++++- 4 files changed, 214 insertions(+), 12 deletions(-) diff --git a/src/Shared/runtime/Http2/Hpack/HPackDecoder.cs b/src/Shared/runtime/Http2/Hpack/HPackDecoder.cs index fb8739999a57..1f9083e29645 100644 --- a/src/Shared/runtime/Http2/Hpack/HPackDecoder.cs +++ b/src/Shared/runtime/Http2/Hpack/HPackDecoder.cs @@ -187,12 +187,11 @@ private void DecodeInternal(ReadOnlySpan data, IHttpStreamHeadersHandler h // will no longer be valid. if (_headerNameRange != null) { - EnsureStringCapacity(ref _headerNameOctets); + EnsureStringCapacity(ref _headerNameOctets, _headerNameLength); _headerName = _headerNameOctets; ReadOnlySpan headerBytes = data.Slice(_headerNameRange.GetValueOrDefault().start, _headerNameRange.GetValueOrDefault().length); headerBytes.CopyTo(_headerName); - _headerNameLength = headerBytes.Length; _headerNameRange = null; } } @@ -427,6 +426,7 @@ private void ParseHeaderName(ReadOnlySpan data, ref int currentIndex, IHtt { // Fast path. Store the range rather than copying. _headerNameRange = (start: currentIndex, count); + _headerNameLength = _stringLength; currentIndex += count; _state = State.HeaderValueLength; @@ -621,11 +621,12 @@ int Decode(ref byte[] dst) _state = nextState; } - private void EnsureStringCapacity(ref byte[] dst) + private void EnsureStringCapacity(ref byte[] dst, int stringLength = -1) { - if (dst.Length < _stringLength) + stringLength = stringLength >= 0 ? stringLength : _stringLength; + if (dst.Length < stringLength) { - dst = new byte[Math.Max(_stringLength, Math.Min(dst.Length * 2, _maxHeadersLength))]; + dst = new byte[Math.Max(stringLength, Math.Min(dst.Length * 2, _maxHeadersLength))]; } } diff --git a/src/Shared/runtime/Http3/QPack/QPackDecoder.cs b/src/Shared/runtime/Http3/QPack/QPackDecoder.cs index 47fc89aaa5d9..ea575da49391 100644 --- a/src/Shared/runtime/Http3/QPack/QPackDecoder.cs +++ b/src/Shared/runtime/Http3/QPack/QPackDecoder.cs @@ -243,12 +243,11 @@ private void DecodeInternal(ReadOnlySpan data, IHttpStreamHeadersHandler h // will no longer be valid. if (_headerNameRange != null) { - EnsureStringCapacity(ref _headerNameOctets, _stringLength, existingLength: 0); + EnsureStringCapacity(ref _headerNameOctets, _headerNameLength, existingLength: 0); _headerName = _headerNameOctets; ReadOnlySpan headerBytes = data.Slice(_headerNameRange.GetValueOrDefault().start, _headerNameRange.GetValueOrDefault().length); headerBytes.CopyTo(_headerName); - _headerNameLength = headerBytes.Length; _headerNameRange = null; } } @@ -294,6 +293,7 @@ private void ParseHeaderName(ReadOnlySpan data, ref int currentIndex, IHtt { // Fast path. Store the range rather than copying. _headerNameRange = (start: currentIndex, count); + _headerNameLength = _stringLength; currentIndex += count; _state = State.HeaderValueLength; diff --git a/src/Shared/test/Shared.Tests/runtime/Http2/HPackDecoderTest.cs b/src/Shared/test/Shared.Tests/runtime/Http2/HPackDecoderTest.cs index b7f4c19072a6..0e94e8c7c6d6 100644 --- a/src/Shared/test/Shared.Tests/runtime/Http2/HPackDecoderTest.cs +++ b/src/Shared/test/Shared.Tests/runtime/Http2/HPackDecoderTest.cs @@ -46,8 +46,13 @@ public class HPackDecoderTests private const string _headerNameString = "new-header"; + // On purpose longer than 4096 (DefaultStringOctetsSize from HPackDecoder) to trigger https://github.com/dotnet/runtime/issues/78516 + private static readonly string _literalHeaderNameString = string.Concat(Enumerable.Range(0, 4100).Select(c => (char)('a' + (c % 26)))); + private static readonly byte[] _headerNameBytes = Encoding.ASCII.GetBytes(_headerNameString); + private static readonly byte[] _literalHeaderNameBytes = Encoding.ASCII.GetBytes(_literalHeaderNameString); + // n e w - h e a d e r * // 10101000 10111110 00010110 10011100 10100011 10010000 10110110 01111111 private static readonly byte[] _headerNameHuffmanBytes = new byte[] { 0xa8, 0xbe, 0x16, 0x9c, 0xa3, 0x90, 0xb6, 0x7f }; @@ -64,6 +69,12 @@ public class HPackDecoderTests .Concat(_headerNameBytes) .ToArray(); + // size = 4096 ==> 0x7f, 0x81, 0x1f (7+) prefixed integer + // size = 4100 ==> 0x7f, 0x85, 0x1f (7+) prefixed integer + private static readonly byte[] _literalHeaderName = new byte[] { 0x7f, 0x85, 0x1f } // 4100 + .Concat(_literalHeaderNameBytes) + .ToArray(); + private static readonly byte[] _headerNameHuffman = new byte[] { (byte)(0x80 | _headerNameHuffmanBytes.Length) } .Concat(_headerNameHuffmanBytes) .ToArray(); @@ -392,6 +403,101 @@ public void DecodesLiteralHeaderFieldNeverIndexed_IndexedName_OutOfRange_Error() Assert.Empty(_handler.DecodedHeaders); } + [Fact] + public void DecodesLiteralHeaderFieldNeverIndexed_NewName_SingleBuffer() + { + byte[] encoded = _literalHeaderFieldWithoutIndexingNewName + .Concat(_literalHeaderName) + .Concat(_headerValue) + .ToArray(); + + _decoder.Decode(encoded, endHeaders: true, handler: _handler); + + Assert.Equal(1, _handler.DecodedHeaders.Count); + Assert.True(_handler.DecodedHeaders.ContainsKey(_literalHeaderNameString)); + Assert.Equal(_headerValueString, _handler.DecodedHeaders[_literalHeaderNameString]); + } + + [Fact] + public void DecodesLiteralHeaderFieldNeverIndexed_NewName_NameLengthBrokenIntoSeparateBuffers() + { + byte[] encoded = _literalHeaderFieldWithoutIndexingNewName + .Concat(_literalHeaderName) + .Concat(_headerValue) + .ToArray(); + + _decoder.Decode(encoded[..1], endHeaders: false, handler: _handler); + _decoder.Decode(encoded[1..], endHeaders: true, handler: _handler); + + Assert.Equal(1, _handler.DecodedHeaders.Count); + Assert.True(_handler.DecodedHeaders.ContainsKey(_literalHeaderNameString)); + Assert.Equal(_headerValueString, _handler.DecodedHeaders[_literalHeaderNameString]); + } + + [Fact] + public void DecodesLiteralHeaderFieldNeverIndexed_NewName_NameBrokenIntoSeparateBuffers() + { + byte[] encoded = _literalHeaderFieldWithoutIndexingNewName + .Concat(_literalHeaderName) + .Concat(_headerValue) + .ToArray(); + + _decoder.Decode(encoded[..(_literalHeaderNameString.Length / 2)], endHeaders: false, handler: _handler); + _decoder.Decode(encoded[(_literalHeaderNameString.Length / 2)..], endHeaders: true, handler: _handler); + + Assert.Equal(1, _handler.DecodedHeaders.Count); + Assert.True(_handler.DecodedHeaders.ContainsKey(_literalHeaderNameString)); + Assert.Equal(_headerValueString, _handler.DecodedHeaders[_literalHeaderNameString]); + } + + [Fact] + public void DecodesLiteralHeaderFieldNeverIndexed_NewName_NameAndValueBrokenIntoSeparateBuffers() + { + byte[] encoded = _literalHeaderFieldWithoutIndexingNewName + .Concat(_literalHeaderName) + .Concat(_headerValue) + .ToArray(); + + _decoder.Decode(encoded[..^_headerValue.Length], endHeaders: false, handler: _handler); + _decoder.Decode(encoded[^_headerValue.Length..], endHeaders: true, handler: _handler); + + Assert.Equal(1, _handler.DecodedHeaders.Count); + Assert.True(_handler.DecodedHeaders.ContainsKey(_literalHeaderNameString)); + Assert.Equal(_headerValueString, _handler.DecodedHeaders[_literalHeaderNameString]); + } + + [Fact] + public void DecodesLiteralHeaderFieldNeverIndexed_NewName_ValueLengthBrokenIntoSeparateBuffers() + { + byte[] encoded = _literalHeaderFieldWithoutIndexingNewName + .Concat(_literalHeaderName) + .Concat(_headerValue) + .ToArray(); + + _decoder.Decode(encoded[..^(_headerValue.Length - 1)], endHeaders: false, handler: _handler); + _decoder.Decode(encoded[^(_headerValue.Length - 1)..], endHeaders: true, handler: _handler); + + Assert.Equal(1, _handler.DecodedHeaders.Count); + Assert.True(_handler.DecodedHeaders.ContainsKey(_literalHeaderNameString)); + Assert.Equal(_headerValueString, _handler.DecodedHeaders[_literalHeaderNameString]); + } + + [Fact] + public void DecodesLiteralHeaderFieldNeverIndexed_NewName_ValueBrokenIntoSeparateBuffers() + { + byte[] encoded = _literalHeaderFieldWithoutIndexingNewName + .Concat(_literalHeaderName) + .Concat(_headerValue) + .ToArray(); + + _decoder.Decode(encoded[..^(_headerValueString.Length / 2)], endHeaders: false, handler: _handler); + _decoder.Decode(encoded[^(_headerValueString.Length / 2)..], endHeaders: true, handler: _handler); + + Assert.Equal(1, _handler.DecodedHeaders.Count); + Assert.True(_handler.DecodedHeaders.ContainsKey(_literalHeaderNameString)); + Assert.Equal(_headerValueString, _handler.DecodedHeaders[_literalHeaderNameString]); + } + [Fact] public void DecodesDynamicTableSizeUpdate() { diff --git a/src/Shared/test/Shared.Tests/runtime/Http3/QPackDecoderTest.cs b/src/Shared/test/Shared.Tests/runtime/Http3/QPackDecoderTest.cs index 475fddeab09b..8db70d84ee05 100644 --- a/src/Shared/test/Shared.Tests/runtime/Http3/QPackDecoderTest.cs +++ b/src/Shared/test/Shared.Tests/runtime/Http3/QPackDecoderTest.cs @@ -25,11 +25,11 @@ public class QPackDecoderTests // 4.5.4 - Literal Header Field With Name Reference - Static Table - Index 44 (content-type) private static readonly byte[] _literalHeaderFieldWithNameReferenceStatic = new byte[] { 0x5f, 0x1d }; - // 4.5.6 - Literal Field Line With Literal Name - (translate) - private static readonly byte[] _literalFieldLineWithLiteralName = new byte[] { 0x37, 0x02, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x6c, 0x61, 0x74, 0x65 }; + // 4.5.6 - Literal Field Line With Literal Name - (literal-header-field) + private static readonly byte[] _literalFieldLineWithLiteralName = new byte[] { 0x37, 0x0d, 0x6c, 0x69, 0x74, 0x65, 0x72, 0x61, 0x6c, 0x2d, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x2d, 0x66, 0x69, 0x65, 0x6c, 0x64 }; private const string _contentTypeString = "content-type"; - private const string _translateString = "translate"; + private const string _literalHeaderFieldString = "literal-header-field"; // n e w - h e a d e r * // 10101000 10111110 00010110 10011100 10100011 10010000 10110110 01111111 @@ -97,7 +97,7 @@ public void DecodesLiteralFieldLineWithLiteralName_Value() .Concat(_headerValue) .ToArray(); - TestDecodeWithoutIndexing(encoded, _translateString, _headerValueString); + TestDecodeWithoutIndexing(encoded, _literalHeaderFieldString, _headerValueString); } [Fact] @@ -140,7 +140,7 @@ public void DecodesLiteralFieldLineWithLiteralName_HuffmanEncodedValue() .Concat(_headerValueHuffman) .ToArray(); - TestDecodeWithoutIndexing(encoded, _translateString, _headerValueString); + TestDecodeWithoutIndexing(encoded, _literalHeaderFieldString, _headerValueString); } [Fact] @@ -173,6 +173,101 @@ public void DecodesLiteralFieldLineWithLiteralName_LargeValues() }); } + [Fact] + public void LiteralFieldWithoutNameReference_SingleBuffer() + { + byte[] encoded = _literalFieldLineWithLiteralName + .Concat(_headerValue) + .ToArray(); + + _decoder.Decode(new byte[] { 0x00, 0x00 }, endHeaders: false, handler: _handler); + _decoder.Decode(encoded, endHeaders: true, handler: _handler); + + Assert.Equal(1, _handler.DecodedHeaders.Count); + Assert.True(_handler.DecodedHeaders.ContainsKey(_literalHeaderFieldString)); + Assert.Equal(_headerValueString, _handler.DecodedHeaders[_literalHeaderFieldString]); + } + + [Fact] + public void LiteralFieldWithoutNameReference_NameLengthBrokenIntoSeparateBuffers() + { + byte[] encoded = _literalFieldLineWithLiteralName + .Concat(_headerValue) + .ToArray(); + + _decoder.Decode(new byte[] { 0x00, 0x00 }, endHeaders: false, handler: _handler); + _decoder.Decode(encoded[..1], endHeaders: false, handler: _handler); + _decoder.Decode(encoded[1..], endHeaders: true, handler: _handler); + + Assert.Equal(1, _handler.DecodedHeaders.Count); + Assert.True(_handler.DecodedHeaders.ContainsKey(_literalHeaderFieldString)); + Assert.Equal(_headerValueString, _handler.DecodedHeaders[_literalHeaderFieldString]); + } + + [Fact] + public void LiteralFieldWithoutNameReference_NameBrokenIntoSeparateBuffers() + { + byte[] encoded = _literalFieldLineWithLiteralName + .Concat(_headerValue) + .ToArray(); + + _decoder.Decode(new byte[] { 0x00, 0x00 }, endHeaders: false, handler: _handler); + _decoder.Decode(encoded[..(_literalHeaderFieldString.Length / 2)], endHeaders: false, handler: _handler); + _decoder.Decode(encoded[(_literalHeaderFieldString.Length / 2)..], endHeaders: true, handler: _handler); + + Assert.Equal(1, _handler.DecodedHeaders.Count); + Assert.True(_handler.DecodedHeaders.ContainsKey(_literalHeaderFieldString)); + Assert.Equal(_headerValueString, _handler.DecodedHeaders[_literalHeaderFieldString]); + } + + [Fact] + public void LiteralFieldWithoutNameReference_NameAndValueBrokenIntoSeparateBuffers() + { + byte[] encoded = _literalFieldLineWithLiteralName + .Concat(_headerValue) + .ToArray(); + + _decoder.Decode(new byte[] { 0x00, 0x00 }, endHeaders: false, handler: _handler); + _decoder.Decode(encoded[..^_headerValue.Length], endHeaders: false, handler: _handler); + _decoder.Decode(encoded[^_headerValue.Length..], endHeaders: true, handler: _handler); + + Assert.Equal(1, _handler.DecodedHeaders.Count); + Assert.True(_handler.DecodedHeaders.ContainsKey(_literalHeaderFieldString)); + Assert.Equal(_headerValueString, _handler.DecodedHeaders[_literalHeaderFieldString]); + } + + [Fact] + public void LiteralFieldWithoutNameReference_ValueLengthBrokenIntoSeparateBuffers() + { + byte[] encoded = _literalFieldLineWithLiteralName + .Concat(_headerValue) + .ToArray(); + + _decoder.Decode(new byte[] { 0x00, 0x00 }, endHeaders: false, handler: _handler); + _decoder.Decode(encoded[..^(_headerValue.Length - 1)], endHeaders: false, handler: _handler); + _decoder.Decode(encoded[^(_headerValue.Length - 1)..], endHeaders: true, handler: _handler); + + Assert.Equal(1, _handler.DecodedHeaders.Count); + Assert.True(_handler.DecodedHeaders.ContainsKey(_literalHeaderFieldString)); + Assert.Equal(_headerValueString, _handler.DecodedHeaders[_literalHeaderFieldString]); + } + + [Fact] + public void LiteralFieldWithoutNameReference_ValueBrokenIntoSeparateBuffers() + { + byte[] encoded = _literalFieldLineWithLiteralName + .Concat(_headerValue) + .ToArray(); + + _decoder.Decode(new byte[] { 0x00, 0x00 }, endHeaders: false, handler: _handler); + _decoder.Decode(encoded[..^(_headerValueString.Length / 2)], endHeaders: false, handler: _handler); + _decoder.Decode(encoded[^(_headerValueString.Length / 2)..], endHeaders: true, handler: _handler); + + Assert.Equal(1, _handler.DecodedHeaders.Count); + Assert.True(_handler.DecodedHeaders.ContainsKey(_literalHeaderFieldString)); + Assert.Equal(_headerValueString, _handler.DecodedHeaders[_literalHeaderFieldString]); + } + public static readonly TheoryData _incompleteHeaderBlockData = new TheoryData { // Incomplete header From 37d0e85b1655d7ec4a0dd116f1f03d8097025586 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Thu, 20 Apr 2023 10:51:41 +0800 Subject: [PATCH 2/3] Disable indexer analyzers in unit tests --- .editorconfig | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/.editorconfig b/.editorconfig index 5af2f4fa01e5..b9e03bb637f3 100644 --- a/.editorconfig +++ b/.editorconfig @@ -145,10 +145,12 @@ dotnet_diagnostic.CA1829.severity = warning dotnet_diagnostic.CA1830.severity = warning # CA1831: Use AsSpan or AsMemory instead of Range-based indexers when appropriate -# CA1832: Use AsSpan or AsMemory instead of Range-based indexers when appropriate -# CA1833: Use AsSpan or AsMemory instead of Range-based indexers when appropriate dotnet_diagnostic.CA1831.severity = warning + +# CA1832: Use AsSpan or AsMemory instead of Range-based indexers when appropriate dotnet_diagnostic.CA1832.severity = warning + +# CA1833: Use AsSpan or AsMemory instead of Range-based indexers when appropriate dotnet_diagnostic.CA1833.severity = warning # CA1834: Consider using 'StringBuilder.Append(char)' when applicable @@ -343,6 +345,12 @@ dotnet_diagnostic.CA1826.severity = suggestion dotnet_diagnostic.CA1827.severity = suggestion # CA1829: Use Length/Count property instead of Count() when available dotnet_diagnostic.CA1829.severity = suggestion +# CA1831: Use AsSpan or AsMemory instead of Range-based indexers when appropriate +dotnet_diagnostic.CA1831.severity = suggestion +# CA1832: Use AsSpan or AsMemory instead of Range-based indexers when appropriate +dotnet_diagnostic.CA1832.severity = suggestion +# CA1833: Use AsSpan or AsMemory instead of Range-based indexers when appropriate +dotnet_diagnostic.CA1833.severity = suggestion # CA1834: Consider using 'StringBuilder.Append(char)' when applicable dotnet_diagnostic.CA1834.severity = suggestion # CA1835: Prefer the 'Memory'-based overloads for 'ReadAsync' and 'WriteAsync' From 31b95bb758720aa7c78bcf3f040096f2ee83add1 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Thu, 20 Apr 2023 10:59:01 +0800 Subject: [PATCH 3/3] Fix unit test warnings --- .../Shared.Tests/runtime/Http2/HPackDecoderTest.cs | 12 ++++++------ .../Shared.Tests/runtime/Http3/QPackDecoderTest.cs | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Shared/test/Shared.Tests/runtime/Http2/HPackDecoderTest.cs b/src/Shared/test/Shared.Tests/runtime/Http2/HPackDecoderTest.cs index 0e94e8c7c6d6..28e7a9710736 100644 --- a/src/Shared/test/Shared.Tests/runtime/Http2/HPackDecoderTest.cs +++ b/src/Shared/test/Shared.Tests/runtime/Http2/HPackDecoderTest.cs @@ -413,7 +413,7 @@ public void DecodesLiteralHeaderFieldNeverIndexed_NewName_SingleBuffer() _decoder.Decode(encoded, endHeaders: true, handler: _handler); - Assert.Equal(1, _handler.DecodedHeaders.Count); + Assert.Single(_handler.DecodedHeaders); Assert.True(_handler.DecodedHeaders.ContainsKey(_literalHeaderNameString)); Assert.Equal(_headerValueString, _handler.DecodedHeaders[_literalHeaderNameString]); } @@ -429,7 +429,7 @@ public void DecodesLiteralHeaderFieldNeverIndexed_NewName_NameLengthBrokenIntoSe _decoder.Decode(encoded[..1], endHeaders: false, handler: _handler); _decoder.Decode(encoded[1..], endHeaders: true, handler: _handler); - Assert.Equal(1, _handler.DecodedHeaders.Count); + Assert.Single(_handler.DecodedHeaders); Assert.True(_handler.DecodedHeaders.ContainsKey(_literalHeaderNameString)); Assert.Equal(_headerValueString, _handler.DecodedHeaders[_literalHeaderNameString]); } @@ -445,7 +445,7 @@ public void DecodesLiteralHeaderFieldNeverIndexed_NewName_NameBrokenIntoSeparate _decoder.Decode(encoded[..(_literalHeaderNameString.Length / 2)], endHeaders: false, handler: _handler); _decoder.Decode(encoded[(_literalHeaderNameString.Length / 2)..], endHeaders: true, handler: _handler); - Assert.Equal(1, _handler.DecodedHeaders.Count); + Assert.Single(_handler.DecodedHeaders); Assert.True(_handler.DecodedHeaders.ContainsKey(_literalHeaderNameString)); Assert.Equal(_headerValueString, _handler.DecodedHeaders[_literalHeaderNameString]); } @@ -461,7 +461,7 @@ public void DecodesLiteralHeaderFieldNeverIndexed_NewName_NameAndValueBrokenInto _decoder.Decode(encoded[..^_headerValue.Length], endHeaders: false, handler: _handler); _decoder.Decode(encoded[^_headerValue.Length..], endHeaders: true, handler: _handler); - Assert.Equal(1, _handler.DecodedHeaders.Count); + Assert.Single(_handler.DecodedHeaders); Assert.True(_handler.DecodedHeaders.ContainsKey(_literalHeaderNameString)); Assert.Equal(_headerValueString, _handler.DecodedHeaders[_literalHeaderNameString]); } @@ -477,7 +477,7 @@ public void DecodesLiteralHeaderFieldNeverIndexed_NewName_ValueLengthBrokenIntoS _decoder.Decode(encoded[..^(_headerValue.Length - 1)], endHeaders: false, handler: _handler); _decoder.Decode(encoded[^(_headerValue.Length - 1)..], endHeaders: true, handler: _handler); - Assert.Equal(1, _handler.DecodedHeaders.Count); + Assert.Single(_handler.DecodedHeaders); Assert.True(_handler.DecodedHeaders.ContainsKey(_literalHeaderNameString)); Assert.Equal(_headerValueString, _handler.DecodedHeaders[_literalHeaderNameString]); } @@ -493,7 +493,7 @@ public void DecodesLiteralHeaderFieldNeverIndexed_NewName_ValueBrokenIntoSeparat _decoder.Decode(encoded[..^(_headerValueString.Length / 2)], endHeaders: false, handler: _handler); _decoder.Decode(encoded[^(_headerValueString.Length / 2)..], endHeaders: true, handler: _handler); - Assert.Equal(1, _handler.DecodedHeaders.Count); + Assert.Single(_handler.DecodedHeaders); Assert.True(_handler.DecodedHeaders.ContainsKey(_literalHeaderNameString)); Assert.Equal(_headerValueString, _handler.DecodedHeaders[_literalHeaderNameString]); } diff --git a/src/Shared/test/Shared.Tests/runtime/Http3/QPackDecoderTest.cs b/src/Shared/test/Shared.Tests/runtime/Http3/QPackDecoderTest.cs index 8db70d84ee05..931ecbcd9bd2 100644 --- a/src/Shared/test/Shared.Tests/runtime/Http3/QPackDecoderTest.cs +++ b/src/Shared/test/Shared.Tests/runtime/Http3/QPackDecoderTest.cs @@ -183,7 +183,7 @@ public void LiteralFieldWithoutNameReference_SingleBuffer() _decoder.Decode(new byte[] { 0x00, 0x00 }, endHeaders: false, handler: _handler); _decoder.Decode(encoded, endHeaders: true, handler: _handler); - Assert.Equal(1, _handler.DecodedHeaders.Count); + Assert.Single(_handler.DecodedHeaders); Assert.True(_handler.DecodedHeaders.ContainsKey(_literalHeaderFieldString)); Assert.Equal(_headerValueString, _handler.DecodedHeaders[_literalHeaderFieldString]); } @@ -199,7 +199,7 @@ public void LiteralFieldWithoutNameReference_NameLengthBrokenIntoSeparateBuffers _decoder.Decode(encoded[..1], endHeaders: false, handler: _handler); _decoder.Decode(encoded[1..], endHeaders: true, handler: _handler); - Assert.Equal(1, _handler.DecodedHeaders.Count); + Assert.Single(_handler.DecodedHeaders); Assert.True(_handler.DecodedHeaders.ContainsKey(_literalHeaderFieldString)); Assert.Equal(_headerValueString, _handler.DecodedHeaders[_literalHeaderFieldString]); } @@ -215,7 +215,7 @@ public void LiteralFieldWithoutNameReference_NameBrokenIntoSeparateBuffers() _decoder.Decode(encoded[..(_literalHeaderFieldString.Length / 2)], endHeaders: false, handler: _handler); _decoder.Decode(encoded[(_literalHeaderFieldString.Length / 2)..], endHeaders: true, handler: _handler); - Assert.Equal(1, _handler.DecodedHeaders.Count); + Assert.Single(_handler.DecodedHeaders); Assert.True(_handler.DecodedHeaders.ContainsKey(_literalHeaderFieldString)); Assert.Equal(_headerValueString, _handler.DecodedHeaders[_literalHeaderFieldString]); } @@ -231,7 +231,7 @@ public void LiteralFieldWithoutNameReference_NameAndValueBrokenIntoSeparateBuffe _decoder.Decode(encoded[..^_headerValue.Length], endHeaders: false, handler: _handler); _decoder.Decode(encoded[^_headerValue.Length..], endHeaders: true, handler: _handler); - Assert.Equal(1, _handler.DecodedHeaders.Count); + Assert.Single(_handler.DecodedHeaders); Assert.True(_handler.DecodedHeaders.ContainsKey(_literalHeaderFieldString)); Assert.Equal(_headerValueString, _handler.DecodedHeaders[_literalHeaderFieldString]); } @@ -247,7 +247,7 @@ public void LiteralFieldWithoutNameReference_ValueLengthBrokenIntoSeparateBuffer _decoder.Decode(encoded[..^(_headerValue.Length - 1)], endHeaders: false, handler: _handler); _decoder.Decode(encoded[^(_headerValue.Length - 1)..], endHeaders: true, handler: _handler); - Assert.Equal(1, _handler.DecodedHeaders.Count); + Assert.Single(_handler.DecodedHeaders); Assert.True(_handler.DecodedHeaders.ContainsKey(_literalHeaderFieldString)); Assert.Equal(_headerValueString, _handler.DecodedHeaders[_literalHeaderFieldString]); } @@ -263,7 +263,7 @@ public void LiteralFieldWithoutNameReference_ValueBrokenIntoSeparateBuffers() _decoder.Decode(encoded[..^(_headerValueString.Length / 2)], endHeaders: false, handler: _handler); _decoder.Decode(encoded[^(_headerValueString.Length / 2)..], endHeaders: true, handler: _handler); - Assert.Equal(1, _handler.DecodedHeaders.Count); + Assert.Single(_handler.DecodedHeaders); Assert.True(_handler.DecodedHeaders.ContainsKey(_literalHeaderFieldString)); Assert.Equal(_headerValueString, _handler.DecodedHeaders[_literalHeaderFieldString]); }