< Summary

Information
Class: System.Net.Http.HPack.HPackDecoder
Assembly: System.Net.Http
File(s): D:\runner\runtime\src\libraries\Common\src\System\Net\Http\aspnetcore\Http2\Hpack\HPackDecoder.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 389
Coverable lines: 389
Total lines: 681
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 107
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\Http2\Hpack\HPackDecoder.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.Numerics;
 8#if KESTREL
 9using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
 10#endif
 11
 12namespace System.Net.Http.HPack
 13{
 14    internal sealed class HPackDecoder
 15    {
 16        private enum State : byte
 17        {
 18            Ready,
 19            HeaderFieldIndex,
 20            HeaderNameIndex,
 21            HeaderNameLength,
 22            HeaderNameLengthContinue,
 23            HeaderName,
 24            HeaderValueLength,
 25            HeaderValueLengthContinue,
 26            HeaderValue,
 27            DynamicTableSizeUpdate
 28        }
 29
 30        // https://datatracker.ietf.org/doc/html/rfc9113#name-defined-settings
 31        // Initial value for SETTINGS_HEADER_TABLE_SIZE is 4,096 octets.
 32        public const int DefaultHeaderTableSize = 4096;
 33
 34        // This is the initial size. Buffers will be dynamically resized as needed.
 35        public const int DefaultStringOctetsSize = 32;
 36
 37        public const int DefaultMaxHeadersLength = 64 * 1024;
 38
 39        // http://httpwg.org/specs/rfc7541.html#rfc.section.6.1
 40        //   0   1   2   3   4   5   6   7
 41        // +---+---+---+---+---+---+---+---+
 42        // | 1 |        Index (7+)         |
 43        // +---+---------------------------+
 44        private const byte IndexedHeaderFieldMask = 0x80;
 45
 46        // http://httpwg.org/specs/rfc7541.html#rfc.section.6.2.1
 47        //   0   1   2   3   4   5   6   7
 48        // +---+---+---+---+---+---+---+---+
 49        // | 0 | 1 |      Index (6+)       |
 50        // +---+---+-----------------------+
 51        private const byte LiteralHeaderFieldWithIncrementalIndexingMask = 0xc0;
 52
 53        // http://httpwg.org/specs/rfc7541.html#rfc.section.6.2.2
 54        //   0   1   2   3   4   5   6   7
 55        // +---+---+---+---+---+---+---+---+
 56        // | 0 | 0 | 0 | 0 |  Index (4+)   |
 57        // +---+---+-----------------------+
 58        private const byte LiteralHeaderFieldWithoutIndexingMask = 0xf0;
 59
 60        // http://httpwg.org/specs/rfc7541.html#rfc.section.6.2.3
 61        //   0   1   2   3   4   5   6   7
 62        // +---+---+---+---+---+---+---+---+
 63        // | 0 | 0 | 0 | 1 |  Index (4+)   |
 64        // +---+---+-----------------------+
 65        private const byte LiteralHeaderFieldNeverIndexedMask = 0xf0;
 66
 67        // http://httpwg.org/specs/rfc7541.html#rfc.section.6.3
 68        //   0   1   2   3   4   5   6   7
 69        // +---+---+---+---+---+---+---+---+
 70        // | 0 | 0 | 1 |   Max size (5+)   |
 71        // +---+---------------------------+
 72        private const byte DynamicTableSizeUpdateMask = 0xe0;
 73
 74        // http://httpwg.org/specs/rfc7541.html#rfc.section.5.2
 75        //   0   1   2   3   4   5   6   7
 76        // +---+---+---+---+---+---+---+---+
 77        // | H |    String Length (7+)     |
 78        // +---+---------------------------+
 79        private const byte HuffmanMask = 0x80;
 80
 81        private const int IndexedHeaderFieldPrefix = 7;
 82        private const int LiteralHeaderFieldWithIncrementalIndexingPrefix = 6;
 83        private const int LiteralHeaderFieldWithoutIndexingPrefix = 4;
 84        private const int LiteralHeaderFieldNeverIndexedPrefix = 4;
 85        private const int DynamicTableSizeUpdatePrefix = 5;
 86        private const int StringLengthPrefix = 7;
 87
 88        private readonly int _maxDynamicTableSize;
 89        private readonly int _maxHeadersLength;
 90        private readonly DynamicTable _dynamicTable;
 91        private IntegerDecoder _integerDecoder;
 92        private byte[] _stringOctets;
 93        private byte[] _headerNameOctets;
 94        private byte[] _headerValueOctets;
 95        private (int start, int length)? _headerNameRange;
 96        private (int start, int length)? _headerValueRange;
 97
 098        private State _state = State.Ready;
 99        private byte[]? _headerName;
 100        private int _headerStaticIndex;
 101        private int _stringIndex;
 102        private int _stringLength;
 103        private int _headerNameLength;
 104        private int _headerValueLength;
 105        private bool _index;
 106        private bool _huffman;
 107        private bool _headersObserved;
 108
 109        public HPackDecoder(int maxDynamicTableSize = DefaultHeaderTableSize, int maxHeadersLength = DefaultMaxHeadersLe
 0110            : this(maxDynamicTableSize, maxHeadersLength, new DynamicTable(maxDynamicTableSize))
 0111        {
 0112        }
 113
 114        // For testing.
 0115        internal HPackDecoder(int maxDynamicTableSize, int maxHeadersLength, DynamicTable dynamicTable)
 0116        {
 0117            _maxDynamicTableSize = maxDynamicTableSize;
 0118            _maxHeadersLength = maxHeadersLength;
 0119            _dynamicTable = dynamicTable;
 120
 0121            _stringOctets = new byte[DefaultStringOctetsSize];
 0122            _headerNameOctets = new byte[DefaultStringOctetsSize];
 0123            _headerValueOctets = new byte[DefaultStringOctetsSize];
 0124        }
 125
 126        public void Decode(in ReadOnlySequence<byte> data, bool endHeaders, IHttpStreamHeadersHandler handler)
 127        {
 128            foreach (ReadOnlyMemory<byte> segment in data)
 129            {
 130                DecodeInternal(segment.Span, handler);
 131            }
 132
 133            CheckIncompleteHeaderBlock(endHeaders);
 134        }
 135
 136        public void Decode(ReadOnlySpan<byte> data, bool endHeaders, IHttpStreamHeadersHandler handler)
 0137        {
 0138            DecodeInternal(data, handler);
 0139            CheckIncompleteHeaderBlock(endHeaders);
 0140        }
 141
 142        private void DecodeInternal(ReadOnlySpan<byte> data, IHttpStreamHeadersHandler handler)
 0143        {
 0144            int currentIndex = 0;
 145
 146            do
 0147            {
 0148                switch (_state)
 149                {
 150                    case State.Ready:
 0151                        Parse(data, ref currentIndex, handler);
 0152                        break;
 153                    case State.HeaderFieldIndex:
 0154                        ParseHeaderFieldIndex(data, ref currentIndex, handler);
 0155                        break;
 156                    case State.HeaderNameIndex:
 0157                        ParseHeaderNameIndex(data, ref currentIndex, handler);
 0158                        break;
 159                    case State.HeaderNameLength:
 0160                        ParseHeaderNameLength(data, ref currentIndex, handler);
 0161                        break;
 162                    case State.HeaderNameLengthContinue:
 0163                        ParseHeaderNameLengthContinue(data, ref currentIndex, handler);
 0164                        break;
 165                    case State.HeaderName:
 0166                        ParseHeaderName(data, ref currentIndex, handler);
 0167                        break;
 168                    case State.HeaderValueLength:
 0169                        ParseHeaderValueLength(data, ref currentIndex, handler);
 0170                        break;
 171                    case State.HeaderValueLengthContinue:
 0172                        ParseHeaderValueLengthContinue(data, ref currentIndex, handler);
 0173                        break;
 174                    case State.HeaderValue:
 0175                        ParseHeaderValue(data, ref currentIndex, handler);
 0176                        break;
 177                    case State.DynamicTableSizeUpdate:
 0178                        ParseDynamicTableSizeUpdate(data, ref currentIndex);
 0179                        break;
 180                    default:
 181                        // Can't happen
 0182                        Debug.Fail("HPACK decoder reach an invalid state");
 183                        throw new NotImplementedException(_state.ToString());
 184                }
 0185            }
 186            // Parse methods each check the length. This check is to see whether there is still data available
 187            // and to continue parsing.
 0188            while (currentIndex < data.Length);
 189
 190            // If a header range was set, but the value was not in the data, then copy the range
 191            // to the name buffer. Must copy because the data will be replaced and the range
 192            // will no longer be valid.
 0193            if (_headerNameRange != null)
 0194            {
 0195                EnsureStringCapacity(ref _headerNameOctets, _headerNameLength);
 0196                _headerName = _headerNameOctets;
 197
 0198                ReadOnlySpan<byte> headerBytes = data.Slice(_headerNameRange.GetValueOrDefault().start, _headerNameRange
 0199                headerBytes.CopyTo(_headerName);
 0200                _headerNameRange = null;
 0201            }
 0202        }
 203
 204        private void ParseDynamicTableSizeUpdate(ReadOnlySpan<byte> data, ref int currentIndex)
 0205        {
 0206            if (TryDecodeInteger(data, ref currentIndex, out int intResult))
 0207            {
 0208                SetDynamicHeaderTableSize(intResult);
 0209                _state = State.Ready;
 0210            }
 0211        }
 212
 213        private void ParseHeaderValueLength(ReadOnlySpan<byte> data, ref int currentIndex, IHttpStreamHeadersHandler han
 0214        {
 0215            if (currentIndex < data.Length)
 0216            {
 0217                byte b = data[currentIndex++];
 218
 0219                _huffman = IsHuffmanEncoded(b);
 220
 0221                if (_integerDecoder.BeginTryDecode((byte)(b & ~HuffmanMask), StringLengthPrefix, out int intResult))
 0222                {
 0223                    OnStringLength(intResult, nextState: State.HeaderValue);
 224
 0225                    if (intResult == 0)
 0226                    {
 0227                        OnString(nextState: State.Ready);
 0228                        ProcessHeaderValue(data, handler);
 0229                    }
 230                    else
 0231                    {
 0232                        ParseHeaderValue(data, ref currentIndex, handler);
 0233                    }
 0234                }
 235                else
 0236                {
 0237                    _state = State.HeaderValueLengthContinue;
 0238                    ParseHeaderValueLengthContinue(data, ref currentIndex, handler);
 0239                }
 0240            }
 0241        }
 242
 243        private void ParseHeaderNameLengthContinue(ReadOnlySpan<byte> data, ref int currentIndex, IHttpStreamHeadersHand
 0244        {
 0245            if (TryDecodeInteger(data, ref currentIndex, out int intResult))
 0246            {
 247                // IntegerDecoder disallows overlong encodings, where an integer is encoded with more bytes than is stri
 248                // 0 should always be represented by a single byte, so we shouldn't need to check for it in the continua
 0249                Debug.Assert(intResult != 0, "A header name length of 0 should never be encoded with a continuation byte
 250
 0251                OnStringLength(intResult, nextState: State.HeaderName);
 0252                ParseHeaderName(data, ref currentIndex, handler);
 0253            }
 0254        }
 255
 256        private void ParseHeaderValueLengthContinue(ReadOnlySpan<byte> data, ref int currentIndex, IHttpStreamHeadersHan
 0257        {
 0258            if (TryDecodeInteger(data, ref currentIndex, out int intResult))
 0259            {
 260                // 0 should always be represented by a single byte, so we shouldn't need to check for it in the continua
 0261                Debug.Assert(intResult != 0, "A header value length of 0 should never be encoded with a continuation byt
 262
 0263                OnStringLength(intResult, nextState: State.HeaderValue);
 0264                ParseHeaderValue(data, ref currentIndex, handler);
 0265            }
 0266        }
 267
 268        private void ParseHeaderFieldIndex(ReadOnlySpan<byte> data, ref int currentIndex, IHttpStreamHeadersHandler hand
 0269        {
 0270            if (TryDecodeInteger(data, ref currentIndex, out int intResult))
 0271            {
 0272                OnIndexedHeaderField(intResult, handler);
 0273            }
 0274        }
 275
 276        private void ParseHeaderNameIndex(ReadOnlySpan<byte> data, ref int currentIndex, IHttpStreamHeadersHandler handl
 0277        {
 0278            if (TryDecodeInteger(data, ref currentIndex, out int intResult))
 0279            {
 0280                OnIndexedHeaderName(intResult);
 0281                ParseHeaderValueLength(data, ref currentIndex, handler);
 0282            }
 0283        }
 284
 285        private void ParseHeaderNameLength(ReadOnlySpan<byte> data, ref int currentIndex, IHttpStreamHeadersHandler hand
 0286        {
 0287            if (currentIndex < data.Length)
 0288            {
 0289                byte b = data[currentIndex++];
 290
 0291                _huffman = IsHuffmanEncoded(b);
 292
 0293                if (_integerDecoder.BeginTryDecode((byte)(b & ~HuffmanMask), StringLengthPrefix, out int intResult))
 0294                {
 0295                    if (intResult == 0)
 0296                    {
 0297                        throw new HPackDecodingException(SR.Format(SR.net_http_invalid_header_name, ""));
 298                    }
 299
 0300                    OnStringLength(intResult, nextState: State.HeaderName);
 0301                    ParseHeaderName(data, ref currentIndex, handler);
 0302                }
 303                else
 0304                {
 0305                    _state = State.HeaderNameLengthContinue;
 0306                    ParseHeaderNameLengthContinue(data, ref currentIndex, handler);
 0307                }
 0308            }
 0309        }
 310
 311        private void Parse(ReadOnlySpan<byte> data, ref int currentIndex, IHttpStreamHeadersHandler handler)
 0312        {
 0313            if (currentIndex < data.Length)
 0314            {
 0315                Debug.Assert(_state == State.Ready, "Should be ready to parse a new header.");
 316
 0317                byte b = data[currentIndex++];
 318
 0319                switch (BitOperations.LeadingZeroCount(b) - 24) // byte 'b' is extended to uint, so will have 24 extra 0
 320                {
 321                    case 0: // Indexed Header Field
 0322                        {
 0323                            _headersObserved = true;
 324
 0325                            int val = b & ~IndexedHeaderFieldMask;
 326
 0327                            if (_integerDecoder.BeginTryDecode((byte)val, IndexedHeaderFieldPrefix, out int intResult))
 0328                            {
 0329                                OnIndexedHeaderField(intResult, handler);
 0330                            }
 331                            else
 0332                            {
 0333                                _state = State.HeaderFieldIndex;
 0334                                ParseHeaderFieldIndex(data, ref currentIndex, handler);
 0335                            }
 0336                            break;
 337                        }
 338                    case 1: // Literal Header Field with Incremental Indexing
 0339                        ParseLiteralHeaderField(
 0340                            data,
 0341                            ref currentIndex,
 0342                            b,
 0343                            LiteralHeaderFieldWithIncrementalIndexingMask,
 0344                            LiteralHeaderFieldWithIncrementalIndexingPrefix,
 0345                            index: true,
 0346                            handler);
 0347                        break;
 348                    case 4:
 349                    default: // Literal Header Field without Indexing
 0350                        ParseLiteralHeaderField(
 0351                            data,
 0352                            ref currentIndex,
 0353                            b,
 0354                            LiteralHeaderFieldWithoutIndexingMask,
 0355                            LiteralHeaderFieldWithoutIndexingPrefix,
 0356                            index: false,
 0357                            handler);
 0358                        break;
 359                    case 3: // Literal Header Field Never Indexed
 0360                        ParseLiteralHeaderField(
 0361                            data,
 0362                            ref currentIndex,
 0363                            b,
 0364                            LiteralHeaderFieldNeverIndexedMask,
 0365                            LiteralHeaderFieldNeverIndexedPrefix,
 0366                            index: false,
 0367                            handler);
 0368                        break;
 369                    case 2: // Dynamic Table Size Update
 0370                        {
 371                            // https://tools.ietf.org/html/rfc7541#section-4.2
 372                            // This dynamic table size
 373                            // update MUST occur at the beginning of the first header block
 374                            // following the change to the dynamic table size.
 0375                            if (_headersObserved)
 0376                            {
 0377                                throw new HPackDecodingException(SR.net_http_hpack_late_dynamic_table_size_update);
 378                            }
 379
 0380                            if (_integerDecoder.BeginTryDecode((byte)(b & ~DynamicTableSizeUpdateMask), DynamicTableSize
 0381                            {
 0382                                SetDynamicHeaderTableSize(intResult);
 0383                            }
 384                            else
 0385                            {
 0386                                _state = State.DynamicTableSizeUpdate;
 0387                                ParseDynamicTableSizeUpdate(data, ref currentIndex);
 0388                            }
 0389                            break;
 390                        }
 391                }
 0392            }
 0393        }
 394
 395        private void ParseLiteralHeaderField(ReadOnlySpan<byte> data, ref int currentIndex, byte b, byte mask, byte inde
 0396        {
 0397            _headersObserved = true;
 398
 0399            _index = index;
 0400            int val = b & ~mask;
 401
 0402            if (val == 0)
 0403            {
 0404                _state = State.HeaderNameLength;
 0405                ParseHeaderNameLength(data, ref currentIndex, handler);
 0406            }
 407            else
 0408            {
 0409                if (_integerDecoder.BeginTryDecode((byte)val, indexPrefix, out int intResult))
 0410                {
 0411                    OnIndexedHeaderName(intResult);
 0412                    ParseHeaderValueLength(data, ref currentIndex, handler);
 0413                }
 414                else
 0415                {
 0416                    _state = State.HeaderNameIndex;
 0417                    ParseHeaderNameIndex(data, ref currentIndex, handler);
 0418                }
 0419            }
 0420        }
 421
 422        private void ParseHeaderName(ReadOnlySpan<byte> data, ref int currentIndex, IHttpStreamHeadersHandler handler)
 0423        {
 424            // Read remaining chars, up to the length of the current data
 0425            int count = Math.Min(_stringLength - _stringIndex, data.Length - currentIndex);
 426
 427            // Check whether the whole string is available in the data and no decompression required.
 428            // If string is good then mark its range.
 429            // NOTE: it may need to be copied to buffer later the if value is not current data.
 0430            if (count == _stringLength && !_huffman)
 0431            {
 432                // Fast path. Store the range rather than copying.
 0433                _headerNameRange = (start: currentIndex, count);
 0434                _headerNameLength = _stringLength;
 0435                currentIndex += count;
 436
 0437                _state = State.HeaderValueLength;
 0438                ParseHeaderValueLength(data, ref currentIndex, handler);
 0439            }
 0440            else if (count == 0)
 0441            {
 442                // no-op
 0443            }
 444            else
 0445            {
 446                // Copy string to temporary buffer.
 447                // _stringOctets was already
 0448                data.Slice(currentIndex, count).CopyTo(_stringOctets.AsSpan(_stringIndex));
 0449                _stringIndex += count;
 0450                currentIndex += count;
 451
 0452                if (_stringIndex == _stringLength)
 0453                {
 0454                    OnString(nextState: State.HeaderValueLength);
 0455                    ParseHeaderValueLength(data, ref currentIndex, handler);
 0456                }
 0457            }
 0458        }
 459
 460        private void ParseHeaderValue(ReadOnlySpan<byte> data, ref int currentIndex, IHttpStreamHeadersHandler handler)
 0461        {
 462            // Read remaining chars, up to the length of the current data
 0463            int count = Math.Min(_stringLength - _stringIndex, data.Length - currentIndex);
 464
 465            // Check whether the whole string is available in the data and no decompressed required.
 466            // If string is good then mark its range.
 0467            if (count == _stringLength && !_huffman)
 0468            {
 469                // Fast path. Store the range rather than copying.
 0470                _headerValueRange = (start: currentIndex, count);
 0471                currentIndex += count;
 472
 0473                _state = State.Ready;
 0474                ProcessHeaderValue(data, handler);
 0475            }
 476            else
 0477            {
 478                // Copy string to temporary buffer.
 0479                data.Slice(currentIndex, count).CopyTo(_stringOctets.AsSpan(_stringIndex));
 0480                _stringIndex += count;
 0481                currentIndex += count;
 482
 0483                if (_stringIndex == _stringLength)
 0484                {
 0485                    OnString(nextState: State.Ready);
 0486                    ProcessHeaderValue(data, handler);
 0487                }
 0488            }
 0489        }
 490
 491        private void CheckIncompleteHeaderBlock(bool endHeaders)
 0492        {
 0493            if (endHeaders)
 0494            {
 0495                if (_state != State.Ready)
 0496                {
 0497                    throw new HPackDecodingException(SR.net_http_hpack_incomplete_header_block);
 498                }
 499
 0500                _headersObserved = false;
 0501            }
 0502        }
 503
 504        private void ProcessHeaderValue(ReadOnlySpan<byte> data, IHttpStreamHeadersHandler handler)
 0505        {
 0506            ReadOnlySpan<byte> headerValueSpan = _headerValueRange == null
 0507                ? _headerValueOctets.AsSpan(0, _headerValueLength)
 0508                : data.Slice(_headerValueRange.GetValueOrDefault().start, _headerValueRange.GetValueOrDefault().length);
 509
 0510            if (_headerStaticIndex > 0)
 0511            {
 0512                handler.OnStaticIndexedHeader(_headerStaticIndex, headerValueSpan);
 513
 0514                if (_index)
 0515                {
 0516                    _dynamicTable.Insert(_headerStaticIndex, H2StaticTable.Get(_headerStaticIndex - 1).Name, headerValue
 0517                }
 0518            }
 519            else
 0520            {
 0521                ReadOnlySpan<byte> headerNameSpan = _headerNameRange == null
 0522                    ? _headerName.AsSpan(0, _headerNameLength)
 0523                    : data.Slice(_headerNameRange.GetValueOrDefault().start, _headerNameRange.GetValueOrDefault().length
 524
 0525                handler.OnHeader(headerNameSpan, headerValueSpan);
 526
 0527                if (_index)
 0528                {
 0529                    _dynamicTable.Insert(headerNameSpan, headerValueSpan);
 0530                }
 0531            }
 532
 0533            _headerStaticIndex = 0;
 0534            _headerNameRange = null;
 0535            _headerValueRange = null;
 0536        }
 537
 538        public void CompleteDecode()
 0539        {
 0540            if (_state != State.Ready)
 0541            {
 542                // Incomplete header block
 0543                throw new HPackDecodingException(SR.net_http_hpack_unexpected_end);
 544            }
 0545        }
 546
 547        private void OnIndexedHeaderField(int index, IHttpStreamHeadersHandler handler)
 0548        {
 0549            if (index <= H2StaticTable.Count)
 0550            {
 0551                handler.OnStaticIndexedHeader(index);
 0552            }
 553            else
 0554            {
 0555                ref readonly HeaderField header = ref GetDynamicHeader(index);
 0556                handler.OnDynamicIndexedHeader(header.StaticTableIndex, header.Name, header.Value);
 0557            }
 558
 0559            _state = State.Ready;
 0560        }
 561
 562        private void OnIndexedHeaderName(int index)
 0563        {
 0564            if (index <= H2StaticTable.Count)
 0565            {
 0566                _headerStaticIndex = index;
 0567            }
 568            else
 0569            {
 0570                _headerName = GetDynamicHeader(index).Name;
 0571                _headerNameLength = _headerName.Length;
 0572            }
 0573            _state = State.HeaderValueLength;
 0574        }
 575
 576        private void OnStringLength(int length, State nextState)
 0577        {
 0578            if (length > _stringOctets.Length)
 0579            {
 0580                if (length > _maxHeadersLength)
 0581                {
 0582                    throw new HPackDecodingException(SR.Format(SR.net_http_headers_exceeded_length, _maxHeadersLength));
 583                }
 584
 0585                _stringOctets = new byte[Math.Max(length, Math.Min(_stringOctets.Length * 2, _maxHeadersLength))];
 0586            }
 587
 0588            _stringLength = length;
 0589            _stringIndex = 0;
 0590            _state = nextState;
 0591        }
 592
 593        private void OnString(State nextState)
 0594        {
 595            int Decode(ref byte[] dst)
 0596            {
 0597                if (_huffman)
 0598                {
 0599                    return Huffman.Decode(new ReadOnlySpan<byte>(_stringOctets, 0, _stringLength), ref dst);
 600                }
 601                else
 0602                {
 0603                    EnsureStringCapacity(ref dst);
 0604                    Buffer.BlockCopy(_stringOctets, 0, dst, 0, _stringLength);
 0605                    return _stringLength;
 606                }
 0607            }
 608
 609            try
 0610            {
 0611                if (_state == State.HeaderName)
 0612                {
 0613                    _headerNameLength = Decode(ref _headerNameOctets);
 0614                    _headerName = _headerNameOctets;
 0615                }
 616                else
 0617                {
 0618                    _headerValueLength = Decode(ref _headerValueOctets);
 0619                }
 0620            }
 0621            catch (HuffmanDecodingException ex)
 0622            {
 0623                throw new HPackDecodingException(SR.net_http_hpack_huffman_decode_failed, ex);
 624            }
 625
 0626            _state = nextState;
 0627        }
 628
 629        private void EnsureStringCapacity(ref byte[] dst, int stringLength = -1)
 0630        {
 0631            stringLength = stringLength >= 0 ? stringLength : _stringLength;
 0632            if (dst.Length < stringLength)
 0633            {
 0634                dst = new byte[Math.Max(stringLength, Math.Min(dst.Length * 2, _maxHeadersLength))];
 0635            }
 0636        }
 637
 638        private bool TryDecodeInteger(ReadOnlySpan<byte> data, ref int currentIndex, out int result)
 0639        {
 0640            for (; currentIndex < data.Length; currentIndex++)
 0641            {
 0642                if (_integerDecoder.TryDecode(data[currentIndex], out result))
 0643                {
 0644                    currentIndex++;
 0645                    return true;
 646                }
 0647            }
 648
 0649            result = default;
 0650            return false;
 0651        }
 652
 653        private static bool IsHuffmanEncoded(byte b)
 0654        {
 0655            return (b & HuffmanMask) != 0;
 0656        }
 657
 658        private ref readonly HeaderField GetDynamicHeader(int index)
 0659        {
 660            try
 0661            {
 0662                return ref _dynamicTable[index - H2StaticTable.Count - 1];
 663            }
 0664            catch (IndexOutOfRangeException)
 0665            {
 666                // Header index out of range.
 0667                throw new HPackDecodingException(SR.Format(SR.net_http_hpack_invalid_index, index));
 668            }
 0669        }
 670
 671        private void SetDynamicHeaderTableSize(int size)
 0672        {
 0673            if (size > _maxDynamicTableSize)
 0674            {
 0675                throw new HPackDecodingException(SR.Format(SR.net_http_hpack_large_table_size_update, size, _maxDynamicT
 676            }
 677
 0678            _dynamicTable.UpdateMaxSize(size);
 0679        }
 680    }
 681}

Methods/Properties

.ctor(System.Int32,System.Int32,System.Net.Http.HPack.DynamicTable)
.ctor(System.Int32,System.Int32)
Decode(System.ReadOnlySpan`1<System.Byte>,System.Boolean,System.Net.Http.IHttpStreamHeadersHandler)
DecodeInternal(System.ReadOnlySpan`1<System.Byte>,System.Net.Http.IHttpStreamHeadersHandler)
ParseDynamicTableSizeUpdate(System.ReadOnlySpan`1<System.Byte>,System.Int32&)
ParseHeaderValueLength(System.ReadOnlySpan`1<System.Byte>,System.Int32&,System.Net.Http.IHttpStreamHeadersHandler)
ParseHeaderNameLengthContinue(System.ReadOnlySpan`1<System.Byte>,System.Int32&,System.Net.Http.IHttpStreamHeadersHandler)
ParseHeaderValueLengthContinue(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)
ParseHeaderNameLength(System.ReadOnlySpan`1<System.Byte>,System.Int32&,System.Net.Http.IHttpStreamHeadersHandler)
Parse(System.ReadOnlySpan`1<System.Byte>,System.Int32&,System.Net.Http.IHttpStreamHeadersHandler)
ParseLiteralHeaderField(System.ReadOnlySpan`1<System.Byte>,System.Int32&,System.Byte,System.Byte,System.Byte,System.Boolean,System.Net.Http.IHttpStreamHeadersHandler)
ParseHeaderName(System.ReadOnlySpan`1<System.Byte>,System.Int32&,System.Net.Http.IHttpStreamHeadersHandler)
ParseHeaderValue(System.ReadOnlySpan`1<System.Byte>,System.Int32&,System.Net.Http.IHttpStreamHeadersHandler)
CheckIncompleteHeaderBlock(System.Boolean)
ProcessHeaderValue(System.ReadOnlySpan`1<System.Byte>,System.Net.Http.IHttpStreamHeadersHandler)
CompleteDecode()
OnIndexedHeaderField(System.Int32,System.Net.Http.IHttpStreamHeadersHandler)
OnIndexedHeaderName(System.Int32)
OnStringLength(System.Int32,System.Net.Http.HPack.HPackDecoder/State)
OnString(System.Net.Http.HPack.HPackDecoder/State)
Decode(System.Byte[]&)
EnsureStringCapacity(System.Byte[]&,System.Int32)
TryDecodeInteger(System.ReadOnlySpan`1<System.Byte>,System.Int32&,System.Int32&)
IsHuffmanEncoded(System.Byte)
GetDynamicHeader(System.Int32)
SetDynamicHeaderTableSize(System.Int32)