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);
        }
示例#2
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);
        }
        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);
        }