< Summary

Information
Class: System.Net.Http.QPack.QPackEncoder
Assembly: System.Net.Http
File(s): D:\runner\runtime\src\libraries\Common\src\System\Net\Http\aspnetcore\Http3\QPack\QPackEncoder.cs
Line coverage
14%
Covered lines: 29
Uncovered lines: 169
Coverable lines: 198
Total lines: 385
Line coverage: 14.6%
Branch coverage
6%
Covered branches: 4
Total branches: 66
Branch coverage: 6%
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\QPackEncoder.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.Collections.Generic;
 7using System.Diagnostics;
 8using System.Net.Http.HPack;
 9using System.Text;
 10
 11namespace System.Net.Http.QPack
 12{
 13    internal static class QPackEncoder
 14    {
 15        // https://tools.ietf.org/html/draft-ietf-quic-qpack-11#section-4.5.2
 16        //   0   1   2   3   4   5   6   7
 17        // +---+---+---+---+---+---+---+---+
 18        // | 1 | T |      Index (6+)       |
 19        // +---+---+-----------------------+
 20        //
 21        // Note for this method's implementation of above:
 22        // - T is constant 1 here, indicating a static table reference.
 23        public static bool EncodeStaticIndexedHeaderField(int index, Span<byte> destination, out int bytesWritten)
 024        {
 025            if (!destination.IsEmpty)
 026            {
 027                destination[0] = 0b11000000;
 028                return IntegerEncoder.Encode(index, 6, destination, out bytesWritten);
 29            }
 30            else
 031            {
 032                bytesWritten = 0;
 033                return false;
 34            }
 035        }
 36
 37        public static byte[] EncodeStaticIndexedHeaderFieldToArray(int index)
 038        {
 039            Span<byte> buffer = stackalloc byte[IntegerEncoder.MaxInt32EncodedLength];
 40
 041            bool res = EncodeStaticIndexedHeaderField(index, buffer, out int bytesWritten);
 042            Debug.Assert(res);
 43
 044            return buffer.Slice(0, bytesWritten).ToArray();
 045        }
 46
 47        // https://tools.ietf.org/html/draft-ietf-quic-qpack-11#section-4.5.4
 48        //   0   1   2   3   4   5   6   7
 49        // +---+---+---+---+---+---+---+---+
 50        // | 0 | 1 | N | T |Name Index (4+)|
 51        // +---+---+---+---+---------------+
 52        // | H |     Value Length (7+)     |
 53        // +---+---------------------------+
 54        // |  Value String (Length bytes)  |
 55        // +-------------------------------+
 56        //
 57        // Note for this method's implementation of above:
 58        // - N is constant 0 here, indicating intermediates (proxies) can compress the header when fordwarding.
 59        // - T is constant 1 here, indicating a static table reference.
 60        // - H is constant 0 here, as we do not yet perform Huffman coding.
 61        public static bool EncodeLiteralHeaderFieldWithStaticNameReference(int index, string value, Span<byte> destinati
 062        {
 063            return EncodeLiteralHeaderFieldWithStaticNameReference(index, value, valueEncoding: null, destination, out b
 064        }
 65
 66        public static bool EncodeLiteralHeaderFieldWithStaticNameReference(int index, string value, Encoding? valueEncod
 067        {
 68            // Requires at least two bytes (one for name reference header, one for value length)
 069            if (destination.Length >= 2)
 070            {
 071                destination[0] = 0b01010000;
 072                if (IntegerEncoder.Encode(index, 4, destination, out int headerBytesWritten))
 073                {
 074                    destination = destination.Slice(headerBytesWritten);
 75
 076                    if (EncodeValueString(value, valueEncoding, destination, out int valueBytesWritten))
 077                    {
 078                        bytesWritten = headerBytesWritten + valueBytesWritten;
 079                        return true;
 80                    }
 081                }
 082            }
 83
 084            bytesWritten = 0;
 085            return false;
 086        }
 87
 88        /// <summary>
 89        /// Encodes just the name part of a Literal Header Field With Static Name Reference. Must call <see cref="Encode
 90        /// </summary>
 91        public static byte[] EncodeLiteralHeaderFieldWithStaticNameReferenceToArray(int index)
 3792        {
 3793            Span<byte> temp = stackalloc byte[IntegerEncoder.MaxInt32EncodedLength];
 94
 3795            temp[0] = 0b01110000;
 3796            bool res = IntegerEncoder.Encode(index, 4, temp, out int headerBytesWritten);
 3797            Debug.Assert(res);
 98
 3799            return temp.Slice(0, headerBytesWritten).ToArray();
 37100        }
 101
 102        public static byte[] EncodeLiteralHeaderFieldWithStaticNameReferenceToArray(int index, string value)
 0103        {
 0104            Span<byte> temp = value.Length < 256 ? stackalloc byte[256 + IntegerEncoder.MaxInt32EncodedLength * 2] : new
 0105            bool res = EncodeLiteralHeaderFieldWithStaticNameReference(index, value, temp, out int bytesWritten);
 0106            Debug.Assert(res);
 0107            return temp.Slice(0, bytesWritten).ToArray();
 0108        }
 109
 110        // https://tools.ietf.org/html/draft-ietf-quic-qpack-11#section-4.5.6
 111        //   0   1   2   3   4   5   6   7
 112        // +---+---+---+---+---+---+---+---+
 113        // | 0 | 0 | 1 | N | H |NameLen(3+)|
 114        // +---+---+---+---+---+-----------+
 115        // |  Name String (Length bytes)   |
 116        // +---+---------------------------+
 117        // | H |     Value Length (7+)     |
 118        // +---+---------------------------+
 119        // |  Value String (Length bytes)  |
 120        // +-------------------------------+
 121        //
 122        // Note for this method's implementation of above:
 123        // - N is constant 0 here, indicating intermediates (proxies) can compress the header when fordwarding.
 124        // - H is constant 0 here, as we do not yet perform Huffman coding.
 125        public static bool EncodeLiteralHeaderFieldWithoutNameReference(string name, string value, Span<byte> destinatio
 0126        {
 0127            return EncodeLiteralHeaderFieldWithoutNameReference(name, value, valueEncoding: null, destination, out bytes
 0128        }
 129
 130        public static bool EncodeLiteralHeaderFieldWithoutNameReference(string name, string value, Encoding? valueEncodi
 0131        {
 0132            if (EncodeNameString(name, destination, out int nameLength) && EncodeValueString(value, valueEncoding, desti
 0133            {
 0134                bytesWritten = nameLength + valueLength;
 0135                return true;
 136            }
 137            else
 0138            {
 0139                bytesWritten = 0;
 0140                return false;
 141            }
 0142        }
 143
 144        /// <summary>
 145        /// Encodes a Literal Header Field Without Name Reference, building the value by concatenating a collection of s
 146        /// </summary>
 147        public static bool EncodeLiteralHeaderFieldWithoutNameReference(string name, ReadOnlySpan<string> values, byte[]
 0148        {
 0149            if (EncodeNameString(name, destination, out int nameLength) && EncodeValueString(values, separator, valueEnc
 0150            {
 0151                bytesWritten = nameLength + valueLength;
 0152                return true;
 153            }
 154
 0155            bytesWritten = 0;
 0156            return false;
 0157        }
 158
 159        /// <summary>
 160        /// Encodes just the value part of a Literawl Header Field Without Static Name Reference. Must call <see cref="E
 161        /// </summary>
 162        public static byte[] EncodeLiteralHeaderFieldWithoutNameReferenceToArray(string name)
 61163        {
 61164            Span<byte> temp = name.Length < 256 ? stackalloc byte[256 + IntegerEncoder.MaxInt32EncodedLength] : new byte
 165
 61166            bool res = EncodeNameString(name, temp, out int nameLength);
 61167            Debug.Assert(res);
 168
 61169            return temp.Slice(0, nameLength).ToArray();
 61170        }
 171
 172        public static byte[] EncodeLiteralHeaderFieldWithoutNameReferenceToArray(string name, string value)
 0173        {
 0174            Span<byte> temp = (name.Length + value.Length) < 256 ? stackalloc byte[256 + IntegerEncoder.MaxInt32EncodedL
 175
 0176            bool res = EncodeLiteralHeaderFieldWithoutNameReference(name, value, temp, out int bytesWritten);
 0177            Debug.Assert(res);
 178
 0179            return temp.Slice(0, bytesWritten).ToArray();
 0180        }
 181
 182        private static bool EncodeValueString(string s, Encoding? valueEncoding, Span<byte> buffer, out int length)
 0183        {
 0184            if (buffer.Length != 0)
 0185            {
 0186                buffer[0] = 0;
 187
 0188                int encodedStringLength = valueEncoding is null || ReferenceEquals(valueEncoding, Encoding.Latin1)
 0189                    ? s.Length
 0190                    : valueEncoding.GetByteCount(s);
 191
 0192                if (IntegerEncoder.Encode(encodedStringLength, 7, buffer, out int nameLength))
 0193                {
 0194                    buffer = buffer.Slice(nameLength);
 0195                    if (buffer.Length >= encodedStringLength)
 0196                    {
 0197                        if (valueEncoding is null)
 0198                        {
 0199                            EncodeValueStringPart(s, buffer);
 0200                        }
 201                        else
 0202                        {
 0203                            int written = valueEncoding.GetBytes(s, buffer);
 0204                            Debug.Assert(written == encodedStringLength);
 0205                        }
 206
 0207                        length = nameLength + encodedStringLength;
 0208                        return true;
 209                    }
 0210                }
 0211            }
 212
 0213            length = 0;
 0214            return false;
 0215        }
 216
 217        /// <summary>
 218        /// Encodes a value by concatenating a collection of strings, separated by a separator string.
 219        /// </summary>
 220        public static bool EncodeValueString(ReadOnlySpan<string> values, byte[]? separator, Encoding? valueEncoding, Sp
 0221        {
 0222            if (values.Length == 1)
 0223            {
 0224                return EncodeValueString(values[0], valueEncoding, buffer, out length);
 225            }
 226
 0227            if (values.Length == 0)
 0228            {
 229                // TODO: this will be called with a string array from HttpHeaderCollection. Can we ever get a 0-length a
 0230                return EncodeValueString(string.Empty, valueEncoding: null, buffer, out length);
 231            }
 232
 0233            if (buffer.Length > 0)
 0234            {
 0235                Debug.Assert(separator != null);
 0236                Debug.Assert(Ascii.IsValid(separator));
 0237                int valueLength = separator.Length * (values.Length - 1);
 238
 0239                if (valueEncoding is null || ReferenceEquals(valueEncoding, Encoding.Latin1))
 0240                {
 0241                    foreach (string part in values)
 0242                    {
 0243                        valueLength += part.Length;
 0244                    }
 0245                }
 246                else
 0247                {
 0248                    foreach (string part in values)
 0249                    {
 0250                        valueLength += valueEncoding.GetByteCount(part);
 0251                    }
 0252                }
 253
 0254                buffer[0] = 0;
 0255                if (IntegerEncoder.Encode(valueLength, 7, buffer, out int nameLength))
 0256                {
 0257                    buffer = buffer.Slice(nameLength);
 0258                    if (buffer.Length >= valueLength)
 0259                    {
 0260                        if (valueEncoding is null)
 0261                        {
 0262                            string value = values[0];
 0263                            EncodeValueStringPart(value, buffer);
 0264                            buffer = buffer.Slice(value.Length);
 265
 0266                            for (int i = 1; i < values.Length; i++)
 0267                            {
 0268                                separator.CopyTo(buffer);
 0269                                buffer = buffer.Slice(separator.Length);
 270
 0271                                value = values[i];
 0272                                EncodeValueStringPart(value, buffer);
 0273                                buffer = buffer.Slice(value.Length);
 0274                            }
 0275                        }
 276                        else
 0277                        {
 0278                            int written = valueEncoding.GetBytes(values[0], buffer);
 0279                            buffer = buffer.Slice(written);
 280
 0281                            for (int i = 1; i < values.Length; i++)
 0282                            {
 0283                                separator.CopyTo(buffer);
 0284                                buffer = buffer.Slice(separator.Length);
 285
 0286                                written = valueEncoding.GetBytes(values[i], buffer);
 0287                                buffer = buffer.Slice(written);
 0288                            }
 0289                        }
 290
 0291                        length = nameLength + valueLength;
 0292                        return true;
 293                    }
 0294                }
 0295            }
 296
 0297            length = 0;
 0298            return false;
 0299        }
 300
 301        private static void EncodeValueStringPart(string s, Span<byte> buffer)
 0302        {
 0303            Debug.Assert(buffer.Length >= s.Length);
 304
 0305            OperationStatus status = Ascii.FromUtf16(s, buffer, out int bytesWritten);
 306
 0307            if (status == OperationStatus.InvalidData)
 0308            {
 0309                throw new QPackEncodingException(SR.net_http_request_invalid_char_encoding);
 310            }
 311
 0312            Debug.Assert(status == OperationStatus.Done);
 0313            Debug.Assert(bytesWritten == s.Length);
 0314        }
 315
 316        private static bool EncodeNameString(string s, Span<byte> buffer, out int length)
 61317        {
 61318            Debug.Assert(Ascii.IsValid(s));
 319
 61320            if (buffer.Length != 0)
 61321            {
 61322                buffer[0] = 0x30;
 323
 61324                if (IntegerEncoder.Encode(s.Length, 3, buffer, out int nameLength))
 61325                {
 61326                    buffer = buffer.Slice(nameLength);
 327
 61328                    if (buffer.Length >= s.Length)
 61329                    {
 61330                        OperationStatus status = Ascii.ToLower(s, buffer, out int valueBytesWritten);
 61331                        Debug.Assert(status == OperationStatus.Done);
 61332                        Debug.Assert(valueBytesWritten == s.Length);
 333
 61334                        length = nameLength + s.Length;
 61335                        return true;
 336                    }
 0337                }
 0338            }
 339
 0340            length = 0;
 0341            return false;
 61342        }
 343
 344        /*
 345         *     0   1   2   3   4   5   6   7
 346               +---+---+---+---+---+---+---+---+
 347               |   Required Insert Count (8+)  |
 348               +---+---------------------------+
 349               | S |      Delta Base (7+)      |
 350               +---+---------------------------+
 351               |      Compressed Headers     ...
 352               +-------------------------------+
 353         *
 354         */
 355        private static bool EncodeHeaderBlockPrefix(Span<byte> destination, out int bytesWritten)
 356        {
 357            int length;
 358            bytesWritten = 0;
 359            // Required insert count as first int
 360            if (!IntegerEncoder.Encode(0, 8, destination, out length))
 361            {
 362                return false;
 363            }
 364
 365            bytesWritten += length;
 366            destination = destination.Slice(length);
 367
 368            // Delta base
 369            if (destination.IsEmpty)
 370            {
 371                return false;
 372            }
 373
 374            destination[0] = 0x00;
 375            if (!IntegerEncoder.Encode(0, 7, destination, out length))
 376            {
 377                return false;
 378            }
 379
 380            bytesWritten += length;
 381
 382            return true;
 383        }
 384    }
 385}

Methods/Properties

EncodeStaticIndexedHeaderField(System.Int32,System.Span`1<System.Byte>,System.Int32&)
EncodeStaticIndexedHeaderFieldToArray(System.Int32)
EncodeLiteralHeaderFieldWithStaticNameReference(System.Int32,System.String,System.Span`1<System.Byte>,System.Int32&)
EncodeLiteralHeaderFieldWithStaticNameReference(System.Int32,System.String,System.Text.Encoding,System.Span`1<System.Byte>,System.Int32&)
EncodeLiteralHeaderFieldWithStaticNameReferenceToArray(System.Int32)
EncodeLiteralHeaderFieldWithStaticNameReferenceToArray(System.Int32,System.String)
EncodeLiteralHeaderFieldWithoutNameReference(System.String,System.String,System.Span`1<System.Byte>,System.Int32&)
EncodeLiteralHeaderFieldWithoutNameReference(System.String,System.String,System.Text.Encoding,System.Span`1<System.Byte>,System.Int32&)
EncodeLiteralHeaderFieldWithoutNameReference(System.String,System.ReadOnlySpan`1<System.String>,System.Byte[],System.Text.Encoding,System.Span`1<System.Byte>,System.Int32&)
EncodeLiteralHeaderFieldWithoutNameReferenceToArray(System.String)
EncodeLiteralHeaderFieldWithoutNameReferenceToArray(System.String,System.String)
EncodeValueString(System.String,System.Text.Encoding,System.Span`1<System.Byte>,System.Int32&)
EncodeValueString(System.ReadOnlySpan`1<System.String>,System.Byte[],System.Text.Encoding,System.Span`1<System.Byte>,System.Int32&)
EncodeValueStringPart(System.String,System.Span`1<System.Byte>)
EncodeNameString(System.String,System.Span`1<System.Byte>,System.Int32&)