protected internal override ValueTask ReadHeadersAsync(int version, IHttpHeadersSink headersSink, object?state, CancellationToken cancellationToken)
 {
     if (IsDisposed(version, out ValueTask task))
     {
         return(task);
     }
     return(_request.ReadHeadersAsync(headersSink, state, cancellationToken));
 }
 private bool ReadHeadersImpl(Span <byte> buffer, IHttpHeadersSink headersSink, object?state, out int bytesConsumed)
 {
     if (Avx2.IsSupported)
     {
         return(ReadHeadersAvx2(buffer, headersSink, state, out bytesConsumed));
     }
     return(ReadHeadersPortable(buffer, headersSink, state, out bytesConsumed));
 }
Exemplo n.º 3
0
        private static void OnHeader(IHttpHeadersSink sink, object?state, ReadOnlySpan <byte> headerName, ReadOnlySpan <byte> headerValue, HttpHeaderFlags flags)
        {
            if (headerName.Length > 1 && headerName[0] == ':')
            {
                // TODO: check for pseudo headers.
            }

            sink.OnHeader(state, headerName, headerValue, flags);
        }
Exemplo n.º 4
0
        private static void ParseHeaderNameValue(ReadOnlySpan <byte> line, IHttpHeadersSink sink, object?state)
        {
            Debug.Assert(line.Length > 0);

            int pos = 0;

            while (line[pos] != (byte)':' && line[pos] != (byte)' ')
            {
                pos++;
                if (pos == line.Length)
                {
                    // Invalid header line that doesn't contain ':'.
                    throw new Exception();
                }
            }

            if (pos == 0)
            {
                // Invalid empty header name.
                throw new Exception();
            }

            ReadOnlySpan <byte> name = line.Slice(0, pos);

            // Eat any trailing whitespace
            while (line[pos] == (byte)' ')
            {
                pos++;
                if (pos == line.Length)
                {
                    // Invalid header line that doesn't contain ':'.
                    throw new Exception();
                }
            }

            if (line[pos++] != ':')
            {
                // Invalid header line that doesn't contain ':'.
                throw new Exception();
            }

            // Skip whitespace after colon
            while (pos < line.Length && (line[pos] == (byte)' ' || line[pos] == (byte)'\t'))
            {
                pos++;
            }

            // Note we ignore the return value from TryAddWithoutValidation. If the header can't be added, we silently drop it.
            ReadOnlySpan <byte> value = line.Slice(pos);

            sink.OnHeader(state, name, value);
        }
 /// <summary>
 /// Reads headers, if any. Should be called when <see cref="ReadType"/> is <see cref="HttpReadType.Headers"/> or <see cref="HttpReadType.TrailingHeaders"/>.
 /// </summary>
 /// <param name="headersSink">A sink to retrieve headers.</param>
 /// <param name="state">User state to pass to <see cref="IHttpHeadersSink.OnHeader(object?, ReadOnlySpan{byte}, ReadOnlySpan{byte})"/>.</param>
 /// <param name="cancellationToken">A cancellation token for the asynchronous operation.</param>
 /// <returns>A <see cref="HttpReadType"/> indicating the type of element read from the stream.</returns>
 public ValueTask ReadHeadersAsync(IHttpHeadersSink headersSink, object?state, CancellationToken cancellationToken = default) =>
 _request.ReadHeadersAsync(_requestVersion, headersSink, state, cancellationToken);
        internal unsafe bool ReadHeadersPortable(Span <byte> buffer, IHttpHeadersSink headersSink, object?state, out int bytesConsumed)
        {
            int originalBufferLength = buffer.Length;

            while (true)
            {
                if (buffer.Length == 0)
                {
                    goto needMore;
                }

                int colIdx = buffer.IndexOfAny((byte)':', (byte)'\n');
                if (colIdx == -1)
                {
                    goto needMore;
                }

                if (buffer[colIdx] == '\n')
                {
                    bytesConsumed = originalBufferLength - buffer.Length + colIdx + 1;
                    return(true);
                }

                ReadOnlySpan <byte> headerName = buffer.Slice(0, colIdx);

                int lfIdx = colIdx;

                // Skip OWS.
                byte ch;
                do
                {
                    if (++lfIdx == buffer.Length)
                    {
                        goto needMore;
                    }
                    ch = buffer[lfIdx];
                } while (ch == ' ' || ch == '\t');

                Span <byte> valueStart = buffer.Slice(lfIdx);
                Span <byte> valueIter  = valueStart;

                while (true)
                {
                    lfIdx = valueIter.IndexOf((byte)'\n');
                    if (lfIdx == -1 || (lfIdx + 1) == valueIter.Length)
                    {
                        goto needMore;
                    }

                    int crLfIdx = lfIdx != 0 && valueIter[lfIdx - 1] == '\r'
                        ? lfIdx - 1
                        : lfIdx;

                    if (lfIdx + 1 >= valueIter.Length)
                    {
                        goto needMore;
                    }

                    // Check if header continues on the next line.
                    byte ht = valueIter[lfIdx + 1];
                    if (ht == '\t' || ht == ' ')
                    {
                        // Replace CRLFHT with SPSPSP and loop.
                        valueIter[crLfIdx]   = (byte)' ';
                        valueIter[lfIdx]     = (byte)' ';
                        valueIter[lfIdx + 1] = (byte)' ';
                        valueIter            = valueIter.Slice(lfIdx + 2);
                        continue;
                    }

                    ReadOnlySpan <byte> headerValue = valueStart.Slice(0, valueStart.Length - valueIter.Length + crLfIdx);

                    headersSink.OnHeader(state, headerName, headerValue);
                    ProcessKnownHeaders(headerName, headerValue);

                    buffer = valueStart.Slice(valueStart.Length - valueIter.Length + lfIdx + 1);
                    break;
                }
            }

needMore:
            bytesConsumed = originalBufferLength - buffer.Length;
            return(false);
        }
        /// <remarks>
        /// This method REQUIRES (32-1) bytes of valid address space after <paramref name="buffer"/>.
        /// It also assumes that it can always step backwards to a 32-byte aligned address.
        /// It is built to be used with VectorArrayBuffer, which does this.
        /// </remarks>
        internal unsafe bool ReadHeadersAvx2(Span <byte> buffer, IHttpHeadersSink headersSink, object?state, out int bytesConsumed)
        {
            if (buffer.Length == 0)
            {
                bytesConsumed = 0;
                return(false);
            }

            Vector256 <byte> maskCol = Vector256.Create((byte)':');
            Vector256 <byte> maskLF  = Vector256.Create((byte)'\n');

            int nameStartIdx = 0;

            fixed(byte *vectorBegin = buffer)
            {
                // align to a 32 byte address for optimal loads.
                byte *vectorEnd  = vectorBegin + buffer.Length;
                byte *vectorIter = (byte *)((nint)vectorBegin & ~(32 - 1));

                Vector256 <byte> vector   = Avx.LoadAlignedVector256(vectorIter);
                uint             foundLF  = (uint)Avx2.MoveMask(Avx2.CompareEqual(vector, maskLF));
                uint             foundCol = foundLF | (uint)Avx2.MoveMask(Avx2.CompareEqual(vector, maskCol));

                while (true)
                {
                    int colIdx;
                    do
                    {
                        while ((colIdx = BitOperations.TrailingZeroCount(foundCol)) == Vector256 <byte> .Count)
                        {
                            vectorIter += Vector256 <byte> .Count;
                            if (vectorIter >= vectorEnd)
                            {
                                bytesConsumed = nameStartIdx;
                                return(false);
                            }

                            // vectorIter may be past the end of buffer[^1]. This is why padding is required.

                            vector   = Avx.LoadAlignedVector256(vectorIter);
                            foundLF  = (uint)Avx2.MoveMask(Avx2.CompareEqual(vector, maskLF));
                            foundCol = foundLF | (uint)Avx2.MoveMask(Avx2.CompareEqual(vector, maskCol));
                        }

                        foundCol ^= 1u << colIdx;
                        colIdx    = (int)(vectorIter - vectorBegin) + colIdx;
                    }while (colIdx < nameStartIdx);

                    if (colIdx >= buffer.Length)
                    {
                        bytesConsumed = nameStartIdx;
                        return(false);
                    }

                    if (buffer[colIdx] == '\n')
                    {
                        bytesConsumed = colIdx + 1;
                        return(true);
                    }

                    // Skip OWS.
                    int  valueStartIdx = colIdx;
                    byte ch;
                    do
                    {
                        if (++valueStartIdx == buffer.Length)
                        {
                            bytesConsumed = nameStartIdx;
                            return(false);
                        }
                        ch = buffer[valueStartIdx];
                    } while (ch == ' ' || ch == '\t');

                    int crlfIdx;
                    int lfIdx;
                    while (true)
                    {
                        do
                        {
                            while ((lfIdx = BitOperations.TrailingZeroCount(foundLF)) == Vector256 <byte> .Count)
                            {
                                vectorIter += Vector256 <byte> .Count;
                                if (vectorIter >= vectorEnd)
                                {
                                    bytesConsumed = nameStartIdx;
                                    return(false);
                                }

                                vector   = Avx.LoadAlignedVector256(vectorIter);
                                foundLF  = (uint)Avx2.MoveMask(Avx2.CompareEqual(vector, maskLF));
                                foundCol = foundLF | (uint)Avx2.MoveMask(Avx2.CompareEqual(vector, maskCol));
                            }

                            uint clearMask = ~(1u << lfIdx);
                            foundLF  &= clearMask;
                            foundCol &= clearMask;
                            lfIdx     = (int)(vectorIter - vectorBegin) + lfIdx;
                        }while (lfIdx < colIdx);

                        if (lfIdx >= buffer.Length)
                        {
                            bytesConsumed = nameStartIdx;
                            return(false);
                        }

                        // Check if header continues on the next line.

                        crlfIdx = lfIdx != 0 && buffer[lfIdx - 1] == '\r'
                            ? lfIdx - 1
                            : lfIdx;

                        if (lfIdx + 1 == buffer.Length)
                        {
                            bytesConsumed = nameStartIdx;
                            return(false);
                        }

                        byte ht = buffer[lfIdx + 1];
                        if (ht == '\t' || ht == ' ')
                        {
                            buffer[crlfIdx]   = (byte)' ';
                            buffer[lfIdx]     = (byte)' ';
                            buffer[lfIdx + 1] = (byte)' ';
                            continue;
                        }
                        break;
                    }
                    ;

                    Span <byte> name  = buffer[nameStartIdx..colIdx];
Exemplo n.º 8
0
        private static void OnHeader(IHttpHeadersSink sink, object?state, ulong headerNameIndex, ReadOnlySpan <byte> headerValue, HttpHeaderFlags flags)
        {
            PreparedHeader v = GetHeaderForIndex(headerNameIndex);

            OnHeader(sink, state, v._name._http2Encoded, headerValue, flags);
        }
Exemplo n.º 9
0
        public static int Decode(ReadOnlySpan <byte> buffer, IHttpHeadersSink sink, object?state)
        {
            int originalLength = buffer.Length;

            while (buffer.Length != 0)
            {
                HttpHeaderFlags flags;
                ulong           nameIndex;
                int             headerLength;
                byte            prefixMask;

                byte firstByte = buffer[0];

                switch (BitOperations.LeadingZeroCount(firstByte) - 24)
                {
                case 0:
                    if (HPack.TryDecodeIndexedHeader(firstByte, buffer, out nameIndex, out headerLength))
                    {
                        buffer = buffer.Slice(headerLength);
                        OnHeader(sink, state, nameIndex);
                        continue;
                    }
                    else
                    {
                        return(originalLength - buffer.Length);
                    }

                case 1:
                    prefixMask = HPack.IncrementalIndexingMask;
                    flags      = HttpHeaderFlags.None;
                    break;

                case 2:     // Dynamic table size update.
                    if (HPack.TryDecodeDynamicTableSizeUpdate(firstByte, buffer, out nameIndex, out headerLength))
                    {
                        buffer = buffer.Slice(headerLength);
                        OnDynamicTableSizeUpdate(nameIndex);
                        continue;
                    }
                    else
                    {
                        return(originalLength - buffer.Length);
                    }

                case 3:     // Literal header never indexed.
                    prefixMask = HPack.WithoutIndexingOrNeverIndexMask;
                    flags      = HttpHeaderFlags.NeverCompressed;
                    break;

                default:     // Literal header without indexing.
                    prefixMask = HPack.WithoutIndexingOrNeverIndexMask;
                    flags      = HttpHeaderFlags.None;
                    break;
                }

                if (!HPack.TryDecodeHeader(prefixMask, flags, firstByte, buffer, out nameIndex, out ReadOnlySpan <byte> name, out ReadOnlySpan <byte> value, out flags, out headerLength))
                {
                    return(originalLength - buffer.Length);
                }

                buffer = buffer.Slice(headerLength);

                if (nameIndex != 0)
                {
                    OnHeader(sink, state, nameIndex, value, flags);
                }
                else
                {
                    OnHeader(sink, state, name, value, flags);
                }
            }

            return(originalLength - buffer.Length);
        }
Exemplo n.º 10
0
        private static void OnHeader(IHttpHeadersSink sink, object?state, ulong headerIndex)
        {
            PreparedHeader v = GetHeaderForIndex(headerIndex);

            OnHeader(sink, state, v._name._http2Encoded, v._value, HttpHeaderFlags.None);
        }
Exemplo n.º 11
0
 /// <summary>
 /// Reads headers, if any. Should be called when <see cref="ReadType"/> is <see cref="HttpReadType.Headers"/> or <see cref="HttpReadType.TrailingHeaders"/>.
 /// </summary>
 /// <param name="version">The version of the request to operate on. This must be validated by implementations.</param>
 /// <param name="headersSink">A sink to retrieve headers.</param>
 /// <param name="state">User state to pass to <see cref="IHttpHeadersSink.OnHeader(object?, ReadOnlySpan{byte}, ReadOnlySpan{byte})"/>.</param>
 /// <param name="cancellationToken">A cancellation token for the asynchronous operation.</param>
 /// <returns>A <see cref="HttpReadType"/> indicating the type of element read from the stream.</returns>
 protected internal abstract ValueTask ReadHeadersAsync(int version, IHttpHeadersSink headersSink, object?state, CancellationToken cancellationToken);