< Summary

Information
Class: System.Net.Http.QPack.QPackDecoder
Assembly: System.Net.Http
File(s): D:\runner\runtime\src\libraries\Common\src\System\Net\Http\aspnetcore\Http3\QPack\QPackDecoder.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 437
Coverable lines: 437
Total lines: 757
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 128
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

D:\runner\runtime\src\libraries\Common\src\System\Net\Http\aspnetcore\Http3\QPack\QPackDecoder.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4#nullable enable
 5using System.Buffers;
 6using System.Diagnostics;
 7using System.Diagnostics.CodeAnalysis;
 8using System.Net.Http.HPack;
 9using System.Numerics;
 10
 11#if KESTREL
 12using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
 13#endif
 14
 15namespace System.Net.Http.QPack
 16{
 17    internal sealed class QPackDecoder : IDisposable
 18    {
 19        private enum State
 20        {
 21            RequiredInsertCount,
 22            RequiredInsertCountContinue,
 23            Base,
 24            BaseContinue,
 25            CompressedHeaders,
 26            HeaderFieldIndex,
 27            HeaderNameIndex,
 28            HeaderNameLength,
 29            HeaderName,
 30            HeaderValueLength,
 31            HeaderValueLengthContinue,
 32            HeaderValue,
 33            PostBaseIndex,
 34            HeaderNameIndexPostBase
 35        }
 36
 37        //0   1   2   3   4   5   6   7
 38        //+---+---+---+---+---+---+---+---+
 39        //|   Required Insert Count(8+)   |
 40        //+---+---------------------------+
 41        //| S |      Delta Base(7+)       |
 42        //+---+---------------------------+
 43        //|      Compressed Headers     ...
 44        private const int RequiredInsertCountPrefix = 8;
 45        private const int BaseMask = 0x80;
 46        private const int BasePrefix = 7;
 47        //+-------------------------------+
 48
 49        //https://tools.ietf.org/html/draft-ietf-quic-qpack-09#section-4.5.2
 50        //0   1   2   3   4   5   6   7
 51        //+---+---+---+---+---+---+---+---+
 52        //| 1 | S |      Index(6+)       |
 53        //+---+---+-----------------------+
 54        private const byte IndexedHeaderStaticMask = 0x40;
 55        private const byte IndexedHeaderStaticRepresentation = 0x40;
 56        private const byte IndexedHeaderFieldPrefixMask = 0x3F;
 57        private const int IndexedHeaderFieldPrefix = 6;
 58
 59        //0   1   2   3   4   5   6   7
 60        //+---+---+---+---+---+---+---+---+
 61        //| 0 | 0 | 0 | 1 |  Index(4+)   |
 62        //+---+---+---+---+---------------+
 63        private const byte PostBaseIndexMask = 0xF0;
 64        private const int PostBaseIndexPrefix = 4;
 65
 66        //0   1   2   3   4   5   6   7
 67        //+---+---+---+---+---+---+---+---+
 68        //| 0 | 1 | N | S |Name Index(4+)|
 69        //+---+---+---+---+---------------+
 70        //| H |     Value Length(7+)     |
 71        //+---+---------------------------+
 72        //|  Value String(Length bytes)  |
 73        //+-------------------------------+
 74        private const byte LiteralHeaderFieldStaticMask = 0x10;
 75        private const byte LiteralHeaderFieldPrefixMask = 0x0F;
 76        private const int LiteralHeaderFieldPrefix = 4;
 77
 78        //0   1   2   3   4   5   6   7
 79        //+---+---+---+---+---+---+---+---+
 80        //| 0 | 0 | 0 | 0 | N |NameIdx(3+)|
 81        //+---+---+---+---+---+-----------+
 82        //| H |     Value Length(7+)     |
 83        //+---+---------------------------+
 84        //|  Value String(Length bytes)  |
 85        //+-------------------------------+
 86        private const byte LiteralHeaderFieldPostBasePrefixMask = 0x07;
 87        private const int LiteralHeaderFieldPostBasePrefix = 3;
 88
 89        //0   1   2   3   4   5   6   7
 90        //+---+---+---+---+---+---+---+---+
 91        //| 0 | 0 | 1 | N | H |NameLen(3+)|
 92        //+---+---+---+---+---+-----------+
 93        //|  Name String(Length bytes)   |
 94        //+---+---------------------------+
 95        //| H |     Value Length(7+)     |
 96        //+---+---------------------------+
 97        //|  Value String(Length bytes)  |
 98        //+-------------------------------+
 99        private const byte LiteralHeaderFieldWithoutNameReferenceHuffmanMask = 0x08;
 100        private const byte LiteralHeaderFieldWithoutNameReferencePrefixMask = 0x07;
 101        private const int LiteralHeaderFieldWithoutNameReferencePrefix = 3;
 102
 103        private const int StringLengthPrefix = 7;
 104        private const byte HuffmanMask = 0x80;
 105
 106        private const int HeaderStaticIndexUnset = -1; // Static index starts at 0
 107
 108        private readonly int _maxHeadersLength;
 0109        private State _state = State.RequiredInsertCount;
 110
 111        // s is used for whatever s is in each field. This has multiple definition
 112        private bool _huffman;
 113
 114        private byte[]? _headerName;
 115        private int _headerStaticIndex;
 116        private int _headerNameLength;
 117        private int _headerValueLength;
 118        private int _stringLength;
 119        private int _stringIndex;
 120
 121        private IntegerDecoder _integerDecoder;
 122        private byte[]? _stringOctets;
 123        private byte[]? _headerNameOctets;
 124        private byte[]? _headerValueOctets;
 125        private (int start, int length)? _headerNameRange;
 126        private (int start, int length)? _headerValueRange;
 127
 0128        private static ArrayPool<byte> Pool => ArrayPool<byte>.Shared;
 129
 0130        public QPackDecoder(int maxHeadersLength)
 0131        {
 0132            _maxHeadersLength = maxHeadersLength;
 0133            _headerStaticIndex = HeaderStaticIndexUnset;
 0134        }
 135
 136        public void Dispose()
 0137        {
 0138            if (_stringOctets != null)
 0139            {
 0140                Pool.Return(_stringOctets);
 0141                _stringOctets = null!;
 0142            }
 143
 0144            if (_headerNameOctets != null)
 0145            {
 0146                Pool.Return(_headerNameOctets);
 0147                _headerNameOctets = null!;
 0148            }
 149
 0150            if (_headerValueOctets != null)
 0151            {
 0152                Pool.Return(_headerValueOctets);
 0153                _headerValueOctets = null!;
 0154            }
 0155        }
 156
 157        /// <summary>
 158        /// Reset the decoder state back to its initial value. Resetting state is required when reusing a decoder with m
 159        /// header frames. For example, decoding a response's headers and trailers.
 160        /// </summary>
 161        public void Reset()
 0162        {
 0163            _state = State.RequiredInsertCount;
 0164        }
 165
 166        public void Decode(in ReadOnlySequence<byte> data, bool endHeaders, IHttpStreamHeadersHandler handler)
 167        {
 168            foreach (ReadOnlyMemory<byte> segment in data)
 169            {
 170                DecodeInternal(segment.Span, handler);
 171            }
 172            CheckIncompleteHeaderBlock(endHeaders);
 173        }
 174
 175        public void Decode(ReadOnlySpan<byte> data, bool endHeaders, IHttpStreamHeadersHandler handler)
 0176        {
 0177            DecodeInternal(data, handler);
 0178            CheckIncompleteHeaderBlock(endHeaders);
 0179        }
 180
 181        private void DecodeInternal(ReadOnlySpan<byte> data, IHttpStreamHeadersHandler handler)
 0182        {
 0183            int currentIndex = 0;
 184
 185            do
 0186            {
 0187                switch (_state)
 188                {
 189                    case State.RequiredInsertCount:
 0190                        ParseRequiredInsertCount(data, ref currentIndex, handler);
 0191                        break;
 192                    case State.RequiredInsertCountContinue:
 0193                        ParseRequiredInsertCountContinue(data, ref currentIndex, handler);
 0194                        break;
 195                    case State.Base:
 0196                        ParseBase(data, ref currentIndex, handler);
 0197                        break;
 198                    case State.BaseContinue:
 0199                        ParseBaseContinue(data, ref currentIndex, handler);
 0200                        break;
 201                    case State.CompressedHeaders:
 0202                        ParseCompressedHeaders(data, ref currentIndex, handler);
 0203                        break;
 204                    case State.HeaderFieldIndex:
 0205                        ParseHeaderFieldIndex(data, ref currentIndex, handler);
 0206                        break;
 207                    case State.HeaderNameIndex:
 0208                        ParseHeaderNameIndex(data, ref currentIndex, handler);
 0209                        break;
 210                    case State.HeaderNameLength:
 0211                        ParseHeaderNameLength(data, ref currentIndex, handler);
 0212                        break;
 213                    case State.HeaderName:
 0214                        ParseHeaderName(data, ref currentIndex, handler);
 0215                        break;
 216                    case State.HeaderValueLength:
 0217                        ParseHeaderValueLength(data, ref currentIndex, handler);
 0218                        break;
 219                    case State.HeaderValueLengthContinue:
 0220                        ParseHeaderValueLengthContinue(data, ref currentIndex, handler);
 0221                        break;
 222                    case State.HeaderValue:
 0223                        ParseHeaderValue(data, ref currentIndex, handler);
 0224                        break;
 225                    case State.PostBaseIndex:
 0226                        ParsePostBaseIndex(data, ref currentIndex);
 0227                        break;
 228                    case State.HeaderNameIndexPostBase:
 0229                        ParseHeaderNameIndexPostBase(data, ref currentIndex);
 0230                        break;
 231                    default:
 232                        // Can't happen
 0233                        Debug.Fail("QPACK decoder reach an invalid state");
 234                        throw new NotImplementedException(_state.ToString());
 235                }
 0236            }
 237            // Parse methods each check the length. This check is to see whether there is still data available
 238            // and to continue parsing.
 0239            while (currentIndex < data.Length);
 240
 241            // If a header range was set, but the value was not in the data, then copy the range
 242            // to the name buffer. Must copy because the data will be replaced and the range
 243            // will no longer be valid.
 0244            if (_headerNameRange != null)
 0245            {
 0246                EnsureStringCapacity(ref _headerNameOctets, _headerNameLength, existingLength: 0);
 0247                _headerName = _headerNameOctets;
 248
 0249                ReadOnlySpan<byte> headerBytes = data.Slice(_headerNameRange.GetValueOrDefault().start, _headerNameRange
 0250                headerBytes.CopyTo(_headerName);
 0251                _headerNameRange = null;
 0252            }
 0253        }
 254
 255        private void ParseHeaderNameIndexPostBase(ReadOnlySpan<byte> data, ref int currentIndex)
 0256        {
 0257            if (TryDecodeInteger(data, ref currentIndex, out int intResult))
 0258            {
 0259                OnIndexedHeaderNamePostBase(intResult);
 0260            }
 0261        }
 262
 263        private void ParsePostBaseIndex(ReadOnlySpan<byte> data, ref int currentIndex)
 0264        {
 0265            if (TryDecodeInteger(data, ref currentIndex, out _))
 0266            {
 0267                OnPostBaseIndex();
 0268            }
 0269        }
 270
 271        private void ParseHeaderNameLength(ReadOnlySpan<byte> data, ref int currentIndex, IHttpStreamHeadersHandler hand
 0272        {
 0273            if (TryDecodeInteger(data, ref currentIndex, out int intResult))
 0274            {
 0275                if (intResult == 0)
 0276                {
 0277                    throw new QPackDecodingException(SR.Format(SR.net_http_invalid_header_name, ""));
 278                }
 0279                OnStringLength(intResult, nextState: State.HeaderName);
 0280                ParseHeaderName(data, ref currentIndex, handler);
 0281            }
 0282        }
 283
 284        private void ParseHeaderName(ReadOnlySpan<byte> data, ref int currentIndex, IHttpStreamHeadersHandler handler)
 0285        {
 286            // Read remaining chars, up to the length of the current data
 0287            int count = Math.Min(_stringLength - _stringIndex, data.Length - currentIndex);
 288
 289            // Check whether the whole string is available in the data and no decompression required.
 290            // If string is good then mark its range.
 291            // NOTE: it may need to be copied to buffer later the if value is not current data.
 0292            if (count == _stringLength && !_huffman)
 0293            {
 294                // Fast path. Store the range rather than copying.
 0295                _headerNameRange = (start: currentIndex, count);
 0296                _headerNameLength = _stringLength;
 0297                currentIndex += count;
 298
 0299                _state = State.HeaderValueLength;
 0300                ParseHeaderValueLength(data, ref currentIndex, handler);
 0301            }
 0302            else if (count == 0)
 0303            {
 304                // no-op
 0305            }
 306            else
 0307            {
 308                // Copy string to temporary buffer.
 0309                EnsureStringCapacity(ref _stringOctets, _stringIndex + count, existingLength: _stringIndex);
 0310                data.Slice(currentIndex, count).CopyTo(_stringOctets.AsSpan(_stringIndex));
 311
 0312                _stringIndex += count;
 0313                currentIndex += count;
 314
 0315                if (_stringIndex == _stringLength)
 0316                {
 0317                    OnString(nextState: State.HeaderValueLength);
 0318                    ParseHeaderValueLength(data, ref currentIndex, handler);
 0319                }
 0320            }
 0321        }
 322
 323        private void ParseHeaderFieldIndex(ReadOnlySpan<byte> data, ref int currentIndex, IHttpStreamHeadersHandler hand
 0324        {
 0325            if (TryDecodeInteger(data, ref currentIndex, out int intResult))
 0326            {
 0327                OnIndexedHeaderField(intResult, handler);
 0328            }
 0329        }
 330
 331        private void ParseHeaderNameIndex(ReadOnlySpan<byte> data, ref int currentIndex, IHttpStreamHeadersHandler handl
 0332        {
 0333            if (TryDecodeInteger(data, ref currentIndex, out int intResult))
 0334            {
 0335                OnIndexedHeaderName(intResult);
 0336                ParseHeaderValueLength(data, ref currentIndex, handler);
 0337            }
 0338        }
 339
 340        private void ParseHeaderValueLength(ReadOnlySpan<byte> data, ref int currentIndex, IHttpStreamHeadersHandler han
 0341        {
 0342            if (currentIndex < data.Length)
 0343            {
 0344                byte b = data[currentIndex++];
 345
 0346                _huffman = IsHuffmanEncoded(b);
 347
 0348                if (_integerDecoder.BeginTryDecode((byte)(b & ~HuffmanMask), StringLengthPrefix, out int intResult))
 0349                {
 0350                    OnStringLength(intResult, nextState: State.HeaderValue);
 351
 0352                    if (intResult == 0)
 0353                    {
 0354                        _state = State.CompressedHeaders;
 0355                        ProcessHeaderValue(data, handler);
 0356                    }
 357                    else
 0358                    {
 0359                        ParseHeaderValue(data, ref currentIndex, handler);
 0360                    }
 0361                }
 362                else
 0363                {
 0364                    _state = State.HeaderValueLengthContinue;
 0365                    ParseHeaderValueLengthContinue(data, ref currentIndex, handler);
 0366                }
 0367            }
 0368        }
 369
 370        private void ParseHeaderValue(ReadOnlySpan<byte> data, ref int currentIndex, IHttpStreamHeadersHandler handler)
 0371        {
 372            // Read remaining chars, up to the length of the current data
 0373            int count = Math.Min(_stringLength - _stringIndex, data.Length - currentIndex);
 374
 375            // Check whether the whole string is available in the data and no decompressed required.
 376            // If string is good then mark its range.
 0377            if (count == _stringLength && !_huffman)
 0378            {
 379                // Fast path. Store the range rather than copying.
 0380                _headerValueRange = (start: currentIndex, count);
 0381                currentIndex += count;
 382
 0383                _state = State.CompressedHeaders;
 0384                ProcessHeaderValue(data, handler);
 0385            }
 0386            else if (count == 0)
 0387            {
 388                // no-op
 0389            }
 390            else
 0391            {
 392                // Copy string to temporary buffer.
 0393                EnsureStringCapacity(ref _stringOctets, _stringIndex + count, existingLength: _stringIndex);
 0394                data.Slice(currentIndex, count).CopyTo(_stringOctets.AsSpan(_stringIndex));
 395
 0396                _stringIndex += count;
 0397                currentIndex += count;
 398
 0399                if (_stringIndex == _stringLength)
 0400                {
 0401                    OnString(nextState: State.CompressedHeaders);
 0402                    ProcessHeaderValue(data, handler);
 0403                }
 0404            }
 0405        }
 406
 407        private void ParseHeaderValueLengthContinue(ReadOnlySpan<byte> data, ref int currentIndex, IHttpStreamHeadersHan
 0408        {
 0409            if (TryDecodeInteger(data, ref currentIndex, out int intResult))
 0410            {
 0411                if (intResult == 0)
 0412                {
 0413                    _state = State.CompressedHeaders;
 0414                    ProcessHeaderValue(data, handler);
 0415                }
 416                else
 0417                {
 0418                    OnStringLength(intResult, nextState: State.HeaderValue);
 0419                    ParseHeaderValue(data, ref currentIndex, handler);
 0420                }
 0421            }
 0422        }
 423
 424        private void ParseCompressedHeaders(ReadOnlySpan<byte> data, ref int currentIndex, IHttpStreamHeadersHandler han
 0425        {
 0426            if (currentIndex < data.Length)
 0427            {
 0428                Debug.Assert(_state == State.CompressedHeaders, "Should be ready to parse a new header.");
 429
 0430                byte b = data[currentIndex++];
 431                int prefixInt;
 432                int intResult;
 433
 0434                switch (BitOperations.LeadingZeroCount(b) - 24) // byte 'b' is extended to uint, so will have 24 extra 0
 435                {
 436                    case 0: // Indexed Header Field
 0437                        prefixInt = IndexedHeaderFieldPrefixMask & b;
 438
 0439                        bool useStaticTable = (b & IndexedHeaderStaticMask) == IndexedHeaderStaticRepresentation;
 440
 0441                        if (!useStaticTable)
 0442                        {
 0443                            ThrowDynamicTableNotSupported();
 0444                        }
 445
 0446                        if (_integerDecoder.BeginTryDecode((byte)prefixInt, IndexedHeaderFieldPrefix, out intResult))
 0447                        {
 0448                            OnIndexedHeaderField(intResult, handler);
 0449                        }
 450                        else
 0451                        {
 0452                            _state = State.HeaderFieldIndex;
 0453                            ParseHeaderFieldIndex(data, ref currentIndex, handler);
 0454                        }
 0455                        break;
 456                    case 1: // Literal Header Field With Name Reference
 0457                        useStaticTable = (LiteralHeaderFieldStaticMask & b) == LiteralHeaderFieldStaticMask;
 458
 0459                        if (!useStaticTable)
 0460                        {
 0461                            ThrowDynamicTableNotSupported();
 0462                        }
 463
 0464                        prefixInt = b & LiteralHeaderFieldPrefixMask;
 0465                        if (_integerDecoder.BeginTryDecode((byte)prefixInt, LiteralHeaderFieldPrefix, out intResult))
 0466                        {
 0467                            OnIndexedHeaderName(intResult);
 0468                            ParseHeaderValueLength(data, ref currentIndex, handler);
 0469                        }
 470                        else
 0471                        {
 0472                            _state = State.HeaderNameIndex;
 0473                            ParseHeaderNameIndex(data, ref currentIndex, handler);
 0474                        }
 0475                        break;
 476                    case 2: // Literal Header Field Without Name Reference
 0477                        _huffman = (b & LiteralHeaderFieldWithoutNameReferenceHuffmanMask) != 0;
 0478                        prefixInt = b & LiteralHeaderFieldWithoutNameReferencePrefixMask;
 479
 0480                        if (_integerDecoder.BeginTryDecode((byte)prefixInt, LiteralHeaderFieldWithoutNameReferencePrefix
 0481                        {
 0482                            if (intResult == 0)
 0483                            {
 0484                                throw new QPackDecodingException(SR.Format(SR.net_http_invalid_header_name, ""));
 485                            }
 0486                            OnStringLength(intResult, State.HeaderName);
 0487                            ParseHeaderName(data, ref currentIndex, handler);
 0488                        }
 489                        else
 0490                        {
 0491                            _state = State.HeaderNameLength;
 0492                            ParseHeaderNameLength(data, ref currentIndex, handler);
 0493                        }
 0494                        break;
 495                    case 3: // Indexed Header Field With Post-Base Index
 0496                        prefixInt = ~PostBaseIndexMask & b;
 0497                        if (_integerDecoder.BeginTryDecode((byte)prefixInt, PostBaseIndexPrefix, out _))
 0498                        {
 0499                            OnPostBaseIndex();
 0500                        }
 501                        else
 0502                        {
 0503                            _state = State.PostBaseIndex;
 0504                            ParsePostBaseIndex(data, ref currentIndex);
 0505                        }
 0506                        break;
 507                    default: // Literal Header Field With Post-Base Name Reference (at least 4 zeroes, maybe more)
 0508                        prefixInt = b & LiteralHeaderFieldPostBasePrefixMask;
 0509                        if (_integerDecoder.BeginTryDecode((byte)prefixInt, LiteralHeaderFieldPostBasePrefix, out intRes
 0510                        {
 0511                            OnIndexedHeaderNamePostBase(intResult);
 0512                        }
 513                        else
 0514                        {
 0515                            _state = State.HeaderNameIndexPostBase;
 0516                            ParseHeaderNameIndexPostBase(data, ref currentIndex);
 0517                        }
 0518                        break;
 519                }
 0520            }
 0521        }
 522
 523        private void ParseRequiredInsertCountContinue(ReadOnlySpan<byte> data, ref int currentIndex, IHttpStreamHeadersH
 0524        {
 0525            if (TryDecodeInteger(data, ref currentIndex, out int intResult))
 0526            {
 0527                OnRequiredInsertCount(intResult);
 0528                ParseBase(data, ref currentIndex, handler);
 0529            }
 0530        }
 531
 532        private void ParseBase(ReadOnlySpan<byte> data, ref int currentIndex, IHttpStreamHeadersHandler handler)
 0533        {
 0534            if (currentIndex < data.Length)
 0535            {
 0536                byte b = data[currentIndex++];
 0537                int prefixInt = ~BaseMask & b;
 538
 0539                if (_integerDecoder.BeginTryDecode((byte)prefixInt, BasePrefix, out int intResult))
 0540                {
 0541                    OnBase(intResult);
 0542                    ParseCompressedHeaders(data, ref currentIndex, handler);
 0543                }
 544                else
 0545                {
 0546                    _state = State.BaseContinue;
 0547                    ParseBaseContinue(data, ref currentIndex, handler);
 0548                }
 0549            }
 0550        }
 551
 552        private void ParseBaseContinue(ReadOnlySpan<byte> data, ref int currentIndex, IHttpStreamHeadersHandler handler)
 0553        {
 0554            if (TryDecodeInteger(data, ref currentIndex, out int intResult))
 0555            {
 0556                OnBase(intResult);
 0557                ParseCompressedHeaders(data, ref currentIndex, handler);
 0558            }
 0559        }
 560
 561        private void ParseRequiredInsertCount(ReadOnlySpan<byte> data, ref int currentIndex, IHttpStreamHeadersHandler h
 0562        {
 0563            if (currentIndex < data.Length)
 0564            {
 0565                byte b = data[currentIndex++];
 566
 0567                if (_integerDecoder.BeginTryDecode(b, RequiredInsertCountPrefix, out int intResult))
 0568                {
 0569                    OnRequiredInsertCount(intResult);
 0570                    ParseBase(data, ref currentIndex, handler);
 0571                }
 572                else
 0573                {
 0574                    _state = State.RequiredInsertCountContinue;
 0575                    ParseRequiredInsertCountContinue(data, ref currentIndex, handler);
 0576                }
 0577            }
 0578        }
 579
 580        private void CheckIncompleteHeaderBlock(bool endHeaders)
 0581        {
 0582            if (endHeaders)
 0583            {
 0584                if (_state != State.CompressedHeaders)
 0585                {
 0586                    throw new QPackDecodingException(SR.net_http_hpack_incomplete_header_block);
 587                }
 0588            }
 0589        }
 590
 591        private void ProcessHeaderValue(ReadOnlySpan<byte> data, IHttpStreamHeadersHandler handler)
 0592        {
 0593            ReadOnlySpan<byte> headerValueSpan = _headerValueRange == null
 0594                ? _headerValueOctets.AsSpan(0, _headerValueLength)
 0595                : data.Slice(_headerValueRange.GetValueOrDefault().start, _headerValueRange.GetValueOrDefault().length);
 596
 0597            if (_headerStaticIndex != HeaderStaticIndexUnset)
 0598            {
 0599                handler.OnStaticIndexedHeader(_headerStaticIndex, headerValueSpan);
 0600            }
 601            else
 0602            {
 0603                ReadOnlySpan<byte> headerNameSpan = _headerNameRange == null
 0604                    ? _headerName.AsSpan(0, _headerNameLength)
 0605                    : data.Slice(_headerNameRange.GetValueOrDefault().start, _headerNameRange.GetValueOrDefault().length
 606
 0607                handler.OnHeader(headerNameSpan, headerValueSpan);
 0608            }
 609
 0610            _headerStaticIndex = HeaderStaticIndexUnset;
 0611            _headerNameRange = null;
 0612            _headerNameLength = 0;
 0613            _headerValueRange = null;
 0614            _headerValueLength = 0;
 0615        }
 616
 617        private void OnStringLength(int length, State nextState)
 0618        {
 0619            if (length > _maxHeadersLength)
 0620            {
 0621                throw new QPackDecodingException(SR.Format(SR.net_http_headers_exceeded_length, _maxHeadersLength));
 622            }
 623
 0624            _stringLength = length;
 0625            _stringIndex = 0;
 0626            _state = nextState;
 0627        }
 628
 629        private void OnString(State nextState)
 0630        {
 631            int Decode(ref byte[]? dst)
 0632            {
 0633                EnsureStringCapacity(ref dst, _stringLength, existingLength: 0);
 634
 0635                if (_huffman)
 0636                {
 0637                    return Huffman.Decode(new ReadOnlySpan<byte>(_stringOctets, 0, _stringLength), ref dst);
 638                }
 639                else
 0640                {
 0641                    Buffer.BlockCopy(_stringOctets, 0, dst, 0, _stringLength);
 0642                    return _stringLength;
 643                }
 0644            }
 645
 0646            Debug.Assert(_stringOctets != null, "String buffer should have a value.");
 647
 648            try
 0649            {
 0650                if (_state == State.HeaderName)
 0651                {
 0652                    _headerNameLength = Decode(ref _headerNameOctets);
 0653                    _headerName = _headerNameOctets;
 0654                }
 655                else
 0656                {
 0657                    _headerValueLength = Decode(ref _headerValueOctets);
 0658                }
 0659            }
 0660            catch (HuffmanDecodingException ex)
 0661            {
 0662                throw new QPackDecodingException(SR.net_http_hpack_huffman_decode_failed, ex);
 663            }
 664
 0665            _state = nextState;
 0666        }
 667
 668        private static void EnsureStringCapacity([NotNull] ref byte[]? buffer, int requiredLength, int existingLength)
 0669        {
 0670            if (buffer == null)
 0671            {
 0672                buffer = Pool.Rent(requiredLength);
 0673            }
 0674            else if (buffer.Length < requiredLength)
 0675            {
 0676                byte[] newBuffer = Pool.Rent(requiredLength);
 0677                if (existingLength > 0)
 0678                {
 0679                    buffer.AsMemory(0, existingLength).CopyTo(newBuffer);
 0680                }
 681
 0682                Pool.Return(buffer);
 0683                buffer = newBuffer;
 0684            }
 0685        }
 686
 687        private bool TryDecodeInteger(ReadOnlySpan<byte> data, ref int currentIndex, out int result)
 0688        {
 0689            for (; currentIndex < data.Length; currentIndex++)
 0690            {
 0691                if (_integerDecoder.TryDecode(data[currentIndex], out result))
 0692                {
 0693                    currentIndex++;
 0694                    return true;
 695                }
 0696            }
 697
 0698            result = default;
 0699            return false;
 0700        }
 701
 702        private static bool IsHuffmanEncoded(byte b)
 0703        {
 0704            return (b & HuffmanMask) != 0;
 0705        }
 706
 707        private void OnIndexedHeaderName(int index)
 0708        {
 0709            _headerStaticIndex = index;
 0710            _state = State.HeaderValueLength;
 0711        }
 712
 713        private static void OnIndexedHeaderNamePostBase(int _ /*index*/)
 0714        {
 0715            ThrowDynamicTableNotSupported();
 716            // TODO update with postbase index
 717            // _index = index;
 718            // _state = State.HeaderValueLength;
 0719        }
 720
 721        private static void OnPostBaseIndex()
 0722        {
 0723            ThrowDynamicTableNotSupported();
 724            // TODO
 725            // _state = State.CompressedHeaders;
 0726        }
 727
 728        private void OnBase(int deltaBase)
 0729        {
 0730            if (deltaBase != 0)
 0731            {
 0732                ThrowDynamicTableNotSupported();
 0733            }
 0734            _state = State.CompressedHeaders;
 0735        }
 736
 737        private void OnRequiredInsertCount(int requiredInsertCount)
 0738        {
 0739            if (requiredInsertCount != 0)
 0740            {
 0741                ThrowDynamicTableNotSupported();
 0742            }
 0743            _state = State.Base;
 0744        }
 745
 746        private void OnIndexedHeaderField(int index, IHttpStreamHeadersHandler handler)
 0747        {
 0748            handler.OnStaticIndexedHeader(index);
 0749            _state = State.CompressedHeaders;
 0750        }
 751
 752        private static void ThrowDynamicTableNotSupported()
 0753        {
 0754            throw new QPackDecodingException(SR.net_http_qpack_no_dynamic_table);
 755        }
 756    }
 757}

Methods/Properties

.ctor(System.Int32)
Pool()
Dispose()
Reset()
Decode(System.ReadOnlySpan`1<System.Byte>,System.Boolean,System.Net.Http.IHttpStreamHeadersHandler)
DecodeInternal(System.ReadOnlySpan`1<System.Byte>,System.Net.Http.IHttpStreamHeadersHandler)
ParseHeaderNameIndexPostBase(System.ReadOnlySpan`1<System.Byte>,System.Int32&)
ParsePostBaseIndex(System.ReadOnlySpan`1<System.Byte>,System.Int32&)
ParseHeaderNameLength(System.ReadOnlySpan`1<System.Byte>,System.Int32&,System.Net.Http.IHttpStreamHeadersHandler)
ParseHeaderName(System.ReadOnlySpan`1<System.Byte>,System.Int32&,System.Net.Http.IHttpStreamHeadersHandler)
ParseHeaderFieldIndex(System.ReadOnlySpan`1<System.Byte>,System.Int32&,System.Net.Http.IHttpStreamHeadersHandler)
ParseHeaderNameIndex(System.ReadOnlySpan`1<System.Byte>,System.Int32&,System.Net.Http.IHttpStreamHeadersHandler)
ParseHeaderValueLength(System.ReadOnlySpan`1<System.Byte>,System.Int32&,System.Net.Http.IHttpStreamHeadersHandler)
ParseHeaderValue(System.ReadOnlySpan`1<System.Byte>,System.Int32&,System.Net.Http.IHttpStreamHeadersHandler)
ParseHeaderValueLengthContinue(System.ReadOnlySpan`1<System.Byte>,System.Int32&,System.Net.Http.IHttpStreamHeadersHandler)
ParseCompressedHeaders(System.ReadOnlySpan`1<System.Byte>,System.Int32&,System.Net.Http.IHttpStreamHeadersHandler)
ParseRequiredInsertCountContinue(System.ReadOnlySpan`1<System.Byte>,System.Int32&,System.Net.Http.IHttpStreamHeadersHandler)
ParseBase(System.ReadOnlySpan`1<System.Byte>,System.Int32&,System.Net.Http.IHttpStreamHeadersHandler)
ParseBaseContinue(System.ReadOnlySpan`1<System.Byte>,System.Int32&,System.Net.Http.IHttpStreamHeadersHandler)
ParseRequiredInsertCount(System.ReadOnlySpan`1<System.Byte>,System.Int32&,System.Net.Http.IHttpStreamHeadersHandler)
CheckIncompleteHeaderBlock(System.Boolean)
ProcessHeaderValue(System.ReadOnlySpan`1<System.Byte>,System.Net.Http.IHttpStreamHeadersHandler)
OnStringLength(System.Int32,System.Net.Http.QPack.QPackDecoder/State)
OnString(System.Net.Http.QPack.QPackDecoder/State)
Decode(System.Byte[]&)
EnsureStringCapacity(System.Byte[]&,System.Int32,System.Int32)
TryDecodeInteger(System.ReadOnlySpan`1<System.Byte>,System.Int32&,System.Int32&)
IsHuffmanEncoded(System.Byte)
OnIndexedHeaderName(System.Int32)
OnIndexedHeaderNamePostBase(System.Int32)
OnPostBaseIndex()
OnBase(System.Int32)
OnRequiredInsertCount(System.Int32)
OnIndexedHeaderField(System.Int32,System.Net.Http.IHttpStreamHeadersHandler)
ThrowDynamicTableNotSupported()