Skip to content
This repository was archived by the owner on Dec 18, 2018. It is now read-only.

Make parser "safe" #3068

Closed
wants to merge 10 commits into from
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ private async Task ProcessRequestsAsync()
var buffer = result.Buffer;
while (true)
{
if (!ParseHttpRequest(ref buffer, result.IsCompleted, out var examined))
if (!ParseHttpRequest(ref buffer, result.IsCompleted, out var consumed, out var examined))
{
return;
}
Expand All @@ -72,36 +72,40 @@ private async Task ProcessRequestsAsync()
}

// No more input or incomplete data, Advance the Reader
Reader.AdvanceTo(buffer.Start, examined);
Reader.AdvanceTo(consumed, examined);
break;
}
}
}

private bool ParseHttpRequest(ref ReadOnlySequence<byte> buffer, bool isCompleted, out SequencePosition examined)
private bool ParseHttpRequest(ref ReadOnlySequence<byte> buffer, bool isCompleted, out SequencePosition consumed, out SequencePosition examined)
{
examined = buffer.End;

var consumed = buffer.Start;
consumed = buffer.Start;
var state = _state;
var reader = new BufferReader<byte>(buffer);

if (!buffer.IsEmpty)
if (!reader.End)
{
if (state == State.StartLine)
{
if (Parser.ParseRequestLine(new ParsingAdapter(this), buffer, out consumed, out examined))
if (Parser.ParseRequestLine(new ParsingAdapter(this), ref reader))
{
state = State.Headers;
consumed = reader.Position;
examined = consumed;
}

buffer = buffer.Slice(consumed);
}

if (state == State.Headers)
{
if (Parser.ParseHeaders(new ParsingAdapter(this), buffer, out consumed, out examined, out int consumedBytes))
if (Parser.ParseHeaders(new ParsingAdapter(this), ref reader))
{
state = State.Body;
consumed = reader.Position;
examined = consumed;
}

buffer = buffer.Slice(consumed);
Expand All @@ -121,7 +125,7 @@ private bool ParseHttpRequest(ref ReadOnlySequence<byte> buffer, bool isComplete
return true;
}

public void OnHeader(Span<byte> name, Span<byte> value)
public void OnHeader(ReadOnlySpan<byte> name, ReadOnlySpan<byte> value)
{
}

Expand Down Expand Up @@ -170,10 +174,10 @@ private struct ParsingAdapter : IHttpRequestLineHandler, IHttpHeadersHandler
public ParsingAdapter(BenchmarkApplication requestHandler)
=> RequestHandler = requestHandler;

public void OnHeader(Span<byte> name, Span<byte> value)
public void OnHeader(ReadOnlySpan<byte> name, ReadOnlySpan<byte> value)
=> RequestHandler.OnHeader(name, value);

public void OnStartLine(HttpMethod method, HttpVersion version, Span<byte> target, Span<byte> path, Span<byte> query, Span<byte> customMethod, bool pathEncoded)
public void OnStartLine(HttpMethod method, HttpVersion version, ReadOnlySpan<byte> target, ReadOnlySpan<byte> path, ReadOnlySpan<byte> query, ReadOnlySpan<byte> customMethod, bool pathEncoded)
=> RequestHandler.OnStartLine(method, version, target, path, query, customMethod, pathEncoded);
}
}
Expand Down
2 changes: 1 addition & 1 deletion benchmarkapps/PlatformBenchmarks/BenchmarkApplication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public static class Paths

private RequestType _requestType;

public void OnStartLine(HttpMethod method, HttpVersion version, Span<byte> target, Span<byte> path, Span<byte> query, Span<byte> customMethod, bool pathEncoded)
public void OnStartLine(HttpMethod method, HttpVersion version, ReadOnlySpan<byte> target, ReadOnlySpan<byte> path, ReadOnlySpan<byte> query, ReadOnlySpan<byte> customMethod, bool pathEncoded)
{
var requestType = RequestType.NotRecognized;
if (method == HttpMethod.Get)
Expand Down
12 changes: 6 additions & 6 deletions benchmarks/Kestrel.Performance/Http1ConnectionBenchmark.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,14 @@ private void InsertData(byte[] data)

private void ParseData()
{
if (!_parser.ParseRequestLine(new Adapter(this), _buffer, out var consumed, out var examined))
var reader = new BufferReader<byte>(_buffer);

if (!_parser.ParseRequestLine(new Adapter(this), ref reader))
{
ErrorUtilities.ThrowInvalidRequestHeaders();
}

_buffer = _buffer.Slice(consumed, _buffer.End);

if (!_parser.ParseHeaders(new Adapter(this), _buffer, out consumed, out examined, out var consumedBytes))
if (!_parser.ParseHeaders(new Adapter(this), ref reader))
{
ErrorUtilities.ThrowInvalidRequestHeaders();
}
Expand All @@ -104,10 +104,10 @@ public Adapter(Http1ConnectionBenchmark requestHandler)
RequestHandler = requestHandler;
}

public void OnHeader(Span<byte> name, Span<byte> value)
public void OnHeader(ReadOnlySpan<byte> name, ReadOnlySpan<byte> value)
=> RequestHandler.Connection.OnHeader(name, value);

public void OnStartLine(HttpMethod method, HttpVersion version, Span<byte> target, Span<byte> path, Span<byte> query, Span<byte> customMethod, bool pathEncoded)
public void OnStartLine(HttpMethod method, HttpVersion version, ReadOnlySpan<byte> target, ReadOnlySpan<byte> path, ReadOnlySpan<byte> query, ReadOnlySpan<byte> customMethod, bool pathEncoded)
=> RequestHandler.Connection.OnStartLine(method, version, target, path, query, customMethod, pathEncoded);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,15 @@ public void Http1ConnectionOverheadRequestHeaders()
private void ParseRequest()
{
_http1Connection.Reset();
var reader = new BufferReader<byte>(_buffer);
var length = _buffer.Length;

if (!_http1Connection.TakeStartLine(_buffer, out var consumed, out var examined))
if (!_http1Connection.TakeStartLine(ref reader, length))
{
ErrorUtilities.ThrowInvalidRequestLine();
}

if (!_http1Connection.TakeMessageHeaders(_buffer, out consumed, out examined))
if (!_http1Connection.TakeMessageHeaders(ref reader, length))
{
ErrorUtilities.ThrowInvalidRequestHeaders();
}
Expand All @@ -92,8 +94,10 @@ private void ParseRequest()
private void ParseRequestLine()
{
_http1Connection.Reset();
var reader = new BufferReader<byte>(_buffer);
var length = _buffer.Length;

if (!_http1Connection.TakeStartLine(_buffer, out var consumed, out var examined))
if (!_http1Connection.TakeStartLine(ref reader, length))
{
ErrorUtilities.ThrowInvalidRequestLine();
}
Expand All @@ -102,8 +106,10 @@ private void ParseRequestLine()
private void ParseRequestHeaders()
{
_http1Connection.Reset();
var reader = new BufferReader<byte>(_buffer);
var length = _buffer.Length;

if (!_http1Connection.TakeMessageHeaders(_buffer, out var consumed, out var examined))
if (!_http1Connection.TakeMessageHeaders(ref reader, length))
{
ErrorUtilities.ThrowInvalidRequestHeaders();
}
Expand Down
15 changes: 7 additions & 8 deletions benchmarks/Kestrel.Performance/HttpParserBenchmark.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,24 +51,23 @@ private void InsertData(byte[] data)

private void ParseData()
{
if (!_parser.ParseRequestLine(new Adapter(this), _buffer, out var consumed, out var examined))
var reader = new BufferReader<byte>(_buffer);
if (!_parser.ParseRequestLine(new Adapter(this), ref reader))
{
ErrorUtilities.ThrowInvalidRequestHeaders();
}

_buffer = _buffer.Slice(consumed, _buffer.End);

if (!_parser.ParseHeaders(new Adapter(this), _buffer, out consumed, out examined, out var consumedBytes))
if (!_parser.ParseHeaders(new Adapter(this), ref reader))
{
ErrorUtilities.ThrowInvalidRequestHeaders();
}
}

public void OnStartLine(HttpMethod method, HttpVersion version, Span<byte> target, Span<byte> path, Span<byte> query, Span<byte> customMethod, bool pathEncoded)
public void OnStartLine(HttpMethod method, HttpVersion version, ReadOnlySpan<byte> target, ReadOnlySpan<byte> path, ReadOnlySpan<byte> query, ReadOnlySpan<byte> customMethod, bool pathEncoded)
{
}

public void OnHeader(Span<byte> name, Span<byte> value)
public void OnHeader(ReadOnlySpan<byte> name, ReadOnlySpan<byte> value)
{
}

Expand All @@ -81,10 +80,10 @@ public Adapter(HttpParserBenchmark requestHandler)
RequestHandler = requestHandler;
}

public void OnHeader(Span<byte> name, Span<byte> value)
public void OnHeader(ReadOnlySpan<byte> name, ReadOnlySpan<byte> value)
=> RequestHandler.OnHeader(name, value);

public void OnStartLine(HttpMethod method, HttpVersion version, Span<byte> target, Span<byte> path, Span<byte> query, Span<byte> customMethod, bool pathEncoded)
public void OnStartLine(HttpMethod method, HttpVersion version, ReadOnlySpan<byte> target, ReadOnlySpan<byte> path, ReadOnlySpan<byte> query, ReadOnlySpan<byte> customMethod, bool pathEncoded)
=> RequestHandler.OnStartLine(method, version, target, path, query, customMethod, pathEncoded);
}
}
Expand Down
34 changes: 12 additions & 22 deletions benchmarks/Kestrel.Performance/KnownStringsBenchmark.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public int GetKnownMethod_TRACE()
return GetKnownMethod(data);
}

private int GetKnownMethod(Span<byte> data)
private int GetKnownMethod(ReadOnlySpan<byte> data)
{
int len = 0;
HttpMethod method;
Expand Down Expand Up @@ -129,29 +129,19 @@ public int GetKnownVersion_HTTP1_1()
{
int len = 0;
HttpVersion version;
Span<byte> data = _version;
ReadOnlySpan<byte> data = _version;
for (int i = 0; i < loops; i++)
{
data.GetKnownVersion(out version, out var length);
len += length;
data.GetKnownVersion(out version, out length);
len += length;
data.GetKnownVersion(out version, out length);
len += length;
data.GetKnownVersion(out version, out length);
len += length;
data.GetKnownVersion(out version, out length);
len += length;
data.GetKnownVersion(out version, out length);
len += length;
data.GetKnownVersion(out version, out length);
len += length;
data.GetKnownVersion(out version, out length);
len += length;
data.GetKnownVersion(out version, out length);
len += length;
data.GetKnownVersion(out version, out length);
len += length;
data.GetKnownVersion(out version);
data.GetKnownVersion(out version);
data.GetKnownVersion(out version);
data.GetKnownVersion(out version);
data.GetKnownVersion(out version);
data.GetKnownVersion(out version);
data.GetKnownVersion(out version);
data.GetKnownVersion(out version);
data.GetKnownVersion(out version);
data.GetKnownVersion(out version);
}
return len;
}
Expand Down
25 changes: 9 additions & 16 deletions benchmarks/Kestrel.Performance/Mocks/NullParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,7 @@ public class NullParser<TRequestHandler> : IHttpParser<TRequestHandler> where TR

public static readonly NullParser<Http1ParsingHandler> Instance = new NullParser<Http1ParsingHandler>();

public bool ParseHeaders(TRequestHandler handler, in ReadOnlySequence<byte> buffer, out SequencePosition consumed, out SequencePosition examined, out int consumedBytes)
{
handler.OnHeader(new Span<byte>(_hostHeaderName), new Span<byte>(_hostHeaderValue));
handler.OnHeader(new Span<byte>(_acceptHeaderName), new Span<byte>(_acceptHeaderValue));
handler.OnHeader(new Span<byte>(_connectionHeaderName), new Span<byte>(_connectionHeaderValue));

consumedBytes = 0;
consumed = buffer.Start;
examined = buffer.End;

return true;
}

public bool ParseRequestLine(TRequestHandler handler, in ReadOnlySequence<byte> buffer, out SequencePosition consumed, out SequencePosition examined)
public bool ParseRequestLine(TRequestHandler handler, ref BufferReader<byte> reader)
{
handler.OnStartLine(HttpMethod.Get,
HttpVersion.Http11,
Expand All @@ -44,8 +31,14 @@ public bool ParseRequestLine(TRequestHandler handler, in ReadOnlySequence<byte>
Span<byte>.Empty,
false);

consumed = buffer.Start;
examined = buffer.End;
return true;
}

public bool ParseHeaders(TRequestHandler handler, ref BufferReader<byte> reader)
{
handler.OnHeader(new Span<byte>(_hostHeaderName), new Span<byte>(_hostHeaderValue));
handler.OnHeader(new Span<byte>(_acceptHeaderName), new Span<byte>(_acceptHeaderValue));
handler.OnHeader(new Span<byte>(_connectionHeaderName), new Span<byte>(_connectionHeaderValue));

return true;
}
Expand Down
24 changes: 13 additions & 11 deletions benchmarks/Kestrel.Performance/RequestParsingBenchmark.cs
Original file line number Diff line number Diff line change
Expand Up @@ -148,25 +148,24 @@ private void ParseDataDrainBuffer()
}

var readableBuffer = awaitable.GetAwaiter().GetResult().Buffer;
var reader = new BufferReader<byte>(readableBuffer);
var length = readableBuffer.Length;

do
{
Http1Connection.Reset();

if (!Http1Connection.TakeStartLine(readableBuffer, out var consumed, out var examined))
if (!Http1Connection.TakeStartLine(ref reader, length))
{
ErrorUtilities.ThrowInvalidRequestLine();
}

readableBuffer = readableBuffer.Slice(consumed);

if (!Http1Connection.TakeMessageHeaders(readableBuffer, out consumed, out examined))
if (!Http1Connection.TakeMessageHeaders(ref reader, length))
{
ErrorUtilities.ThrowInvalidRequestHeaders();
}

readableBuffer = readableBuffer.Slice(consumed);
}
while (readableBuffer.Length > 0);
while (!reader.End);

Pipe.Reader.AdvanceTo(readableBuffer.End);
}
Expand All @@ -184,23 +183,26 @@ private void ParseData()

var result = awaitable.GetAwaiter().GetResult();
var readableBuffer = result.Buffer;
var length = readableBuffer.Length;
var reader = new BufferReader<byte>(readableBuffer);

Http1Connection.Reset();

if (!Http1Connection.TakeStartLine(readableBuffer, out var consumed, out var examined))
if (!Http1Connection.TakeStartLine(ref reader, length))
{
ErrorUtilities.ThrowInvalidRequestLine();
}
Pipe.Reader.AdvanceTo(consumed, examined);
Pipe.Reader.AdvanceTo(reader.Position);

result = Pipe.Reader.ReadAsync().GetAwaiter().GetResult();
readableBuffer = result.Buffer;
reader = new BufferReader<byte>(readableBuffer);

if (!Http1Connection.TakeMessageHeaders(readableBuffer, out consumed, out examined))
if (!Http1Connection.TakeMessageHeaders(ref reader, length))
{
ErrorUtilities.ThrowInvalidRequestHeaders();
}
Pipe.Reader.AdvanceTo(consumed, examined);
Pipe.Reader.AdvanceTo(reader.Position);
}
while (true);
}
Expand Down
Loading