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);
}