private async ValueTask ReadHeadersAsync()

in src/StreamJsonRpc/HeaderDelimitedMessageHandler.cs [411:502]


        private async ValueTask<(int? ContentLength, Encoding? ContentEncoding)?> ReadHeadersAsync(CancellationToken cancellationToken)
        {
            bool IsHeaderName(ReadOnlySequence<byte> buffer, ReadOnlySpan<byte> asciiHeaderName)
            {
                if (asciiHeaderName.Length != buffer.Length)
                {
                    return false;
                }

                foreach (ReadOnlyMemory<byte> segment in buffer)
                {
                    if (!asciiHeaderName.Slice(0, segment.Length).SequenceEqual(segment.Span))
                    {
                        return false;
                    }

                    asciiHeaderName = asciiHeaderName.Slice(segment.Length);
                }

                return true;
            }

            Assumes.NotNull(this.Reader);
            int? contentLengthHeaderValue = null;
            Encoding? contentEncoding = null;

            while (true)
            {
                ReadResult readResult = await this.Reader.ReadAsync(cancellationToken).ConfigureAwait(false);
                if (readResult.Buffer.Length == 0 && readResult.IsCompleted)
                {
                    return default; // remote end disconnected at a reasonable place.
                }

                SequencePosition? lf = readResult.Buffer.PositionOf((byte)'\n');
                if (!lf.HasValue)
                {
                    if (readResult.IsCompleted)
                    {
                        throw new EndOfStreamException();
                    }

                    // Indicate that we can't find what we're looking for and read again.
                    this.Reader.AdvanceTo(readResult.Buffer.Start, readResult.Buffer.End);
                    continue;
                }

                ReadOnlySequence<byte> line = readResult.Buffer.Slice(0, lf.Value);

                // Verify the line ends with an \r (that precedes the \n we already found)
                SequencePosition? cr = line.PositionOf((byte)'\r');
                if (!cr.HasValue || !line.GetPosition(1, cr.Value).Equals(lf))
                {
                    throw new BadRpcHeaderException("Header does not end with expected \r\n character sequence: " + HeaderEncoding.GetString(line.ToArray()));
                }

                // Trim off the \r now that we confirmed it was there.
                line = line.Slice(0, line.Length - 1);

                if (line.Length > 0)
                {
                    SequencePosition? colon = line.PositionOf((byte)':');
                    if (!colon.HasValue)
                    {
                        throw new BadRpcHeaderException("Colon not found in header.");
                    }

                    ReadOnlySequence<byte> headerNameBytes = line.Slice(0, colon.Value);
                    ReadOnlySequence<byte> headerValueBytes = line.Slice(line.GetPosition(1, colon.Value));

                    if (IsHeaderName(headerNameBytes, ContentLengthHeaderName))
                    {
                        contentLengthHeaderValue = GetContentLength(headerValueBytes);
                    }
                    else if (IsHeaderName(headerNameBytes, ContentTypeHeaderName))
                    {
                        contentEncoding = ParseEncodingFromContentTypeHeader(headerValueBytes);
                    }
                }

                // Advance to the next line.
                this.Reader.AdvanceTo(readResult.Buffer.GetPosition(1, lf.Value));

                if (line.Length == 0)
                {
                    // We found the empty line that constitutes the end of the HTTP headers.
                    break;
                }
            }

            return (contentLengthHeaderValue, contentEncoding);
        }