Pass ReadOnlySequence<byte> via in (#11052)

This commit is contained in:
Ben Adams 2019-06-28 23:51:52 +02:00 committed by Justin Kotalik
parent bcb59fa5c9
commit 81b757afcc
11 changed files with 109 additions and 62 deletions

View File

@ -284,7 +284,7 @@ namespace Microsoft.AspNetCore.WebUtilities
throw new InvalidDataException($"Form value length limit {ValueLengthLimit} exceeded."); throw new InvalidDataException($"Form value length limit {ValueLengthLimit} exceeded.");
} }
private string GetDecodedStringFromReadOnlySequence(ReadOnlySequence<byte> ros) private string GetDecodedStringFromReadOnlySequence(in ReadOnlySequence<byte> ros)
{ {
if (ros.IsSingleSegment) if (ros.IsSingleSegment)
{ {

View File

@ -17,7 +17,7 @@ namespace System.Buffers
private static byte[] _numericBytesScratch; private static byte[] _numericBytesScratch;
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlySpan<byte> ToSpan(this ReadOnlySequence<byte> buffer) public static ReadOnlySpan<byte> ToSpan(in this ReadOnlySequence<byte> buffer)
{ {
if (buffer.IsSingleSegment) if (buffer.IsSingleSegment)
{ {

View File

@ -220,7 +220,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
_requestBodyPipe.Reset(); _requestBodyPipe.Reset();
} }
private void Copy(ReadOnlySequence<byte> readableBuffer, PipeWriter writableBuffer) private void Copy(in ReadOnlySequence<byte> readableBuffer, PipeWriter writableBuffer)
{ {
if (readableBuffer.IsSingleSegment) if (readableBuffer.IsSingleSegment)
{ {
@ -321,10 +321,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
return _mode == Mode.Complete; return _mode == Mode.Complete;
} }
private void ParseChunkedPrefix(ReadOnlySequence<byte> buffer, out SequencePosition consumed, out SequencePosition examined) private void ParseChunkedPrefix(in ReadOnlySequence<byte> buffer, out SequencePosition consumed, out SequencePosition examined)
{ {
consumed = buffer.Start; consumed = buffer.Start;
examined = buffer.Start;
var reader = new SequenceReader<byte>(buffer); var reader = new SequenceReader<byte>(buffer);
if (!reader.TryRead(out var ch1) || !reader.TryRead(out var ch2)) if (!reader.TryRead(out var ch1) || !reader.TryRead(out var ch2))
@ -383,7 +382,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{ {
// Chunk-extensions not currently parsed // Chunk-extensions not currently parsed
// Just drain the data // Just drain the data
consumed = buffer.Start;
examined = buffer.Start; examined = buffer.Start;
do do
@ -432,7 +430,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
} while (_mode == Mode.Extension); } while (_mode == Mode.Extension);
} }
private void ReadChunkedData(ReadOnlySequence<byte> buffer, PipeWriter writableBuffer, out SequencePosition consumed, out SequencePosition examined) private void ReadChunkedData(in ReadOnlySequence<byte> buffer, PipeWriter writableBuffer, out SequencePosition consumed, out SequencePosition examined)
{ {
var actual = Math.Min(buffer.Length, _inputLength); var actual = Math.Min(buffer.Length, _inputLength);
consumed = buffer.GetPosition(actual); consumed = buffer.GetPosition(actual);
@ -449,10 +447,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
} }
} }
private void ParseChunkedSuffix(ReadOnlySequence<byte> buffer, out SequencePosition consumed, out SequencePosition examined) private void ParseChunkedSuffix(in ReadOnlySequence<byte> buffer, out SequencePosition consumed, out SequencePosition examined)
{ {
consumed = buffer.Start; consumed = buffer.Start;
examined = buffer.Start;
if (buffer.Length < 2) if (buffer.Length < 2)
{ {
@ -478,10 +475,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
} }
} }
private void ParseChunkedTrailer(ReadOnlySequence<byte> buffer, out SequencePosition consumed, out SequencePosition examined) private void ParseChunkedTrailer(in ReadOnlySequence<byte> buffer, out SequencePosition consumed, out SequencePosition examined)
{ {
consumed = buffer.Start; consumed = buffer.Start;
examined = buffer.Start;
if (buffer.Length < 2) if (buffer.Length < 2)
{ {

View File

@ -131,7 +131,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
SendTimeoutResponse(); SendTimeoutResponse();
} }
public void ParseRequest(ReadOnlySequence<byte> buffer, out SequencePosition consumed, out SequencePosition examined) public void ParseRequest(in ReadOnlySequence<byte> buffer, out SequencePosition consumed, out SequencePosition examined)
{ {
consumed = buffer.Start; consumed = buffer.Start;
examined = buffer.End; examined = buffer.End;
@ -151,10 +151,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
case RequestProcessingStatus.ParsingRequestLine: case RequestProcessingStatus.ParsingRequestLine:
if (TakeStartLine(buffer, out consumed, out examined)) if (TakeStartLine(buffer, out consumed, out examined))
{ {
buffer = buffer.Slice(consumed, buffer.End); TrimAndParseHeaders(buffer, ref consumed, out examined);
return;
_requestProcessingStatus = RequestProcessingStatus.ParsingHeaders;
goto case RequestProcessingStatus.ParsingHeaders;
} }
else else
{ {
@ -167,24 +165,42 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
} }
break; break;
} }
void TrimAndParseHeaders(in ReadOnlySequence<byte> buffer, ref SequencePosition consumed, out SequencePosition examined)
{
var trimmedBuffer = buffer.Slice(consumed, buffer.End);
_requestProcessingStatus = RequestProcessingStatus.ParsingHeaders;
if (TakeMessageHeaders(trimmedBuffer, trailers: false, out consumed, out examined))
{
_requestProcessingStatus = RequestProcessingStatus.AppStarted;
}
}
} }
public bool TakeStartLine(ReadOnlySequence<byte> buffer, out SequencePosition consumed, out SequencePosition examined) public bool TakeStartLine(in ReadOnlySequence<byte> buffer, out SequencePosition consumed, out SequencePosition examined)
{ {
var overLength = false; // Make sure the buffer is limited
if (buffer.Length >= ServerOptions.Limits.MaxRequestLineSize) if (buffer.Length >= ServerOptions.Limits.MaxRequestLineSize)
{ {
buffer = buffer.Slice(buffer.Start, ServerOptions.Limits.MaxRequestLineSize); // Input oversize, cap amount checked
overLength = true; return TrimAndTakeStartLine(buffer, out consumed, out examined);
} }
var result = _parser.ParseRequestLine(new Http1ParsingHandler(this), buffer, out consumed, out examined); return _parser.ParseRequestLine(new Http1ParsingHandler(this), buffer, out consumed, out examined);
if (!result && overLength)
bool TrimAndTakeStartLine(in ReadOnlySequence<byte> buffer, out SequencePosition consumed, out SequencePosition examined)
{ {
BadHttpRequestException.Throw(RequestRejectionReason.RequestLineTooLong); var trimmedBuffer = buffer.Slice(buffer.Start, ServerOptions.Limits.MaxRequestLineSize);
}
return result; if (!_parser.ParseRequestLine(new Http1ParsingHandler(this), trimmedBuffer, out consumed, out examined))
{
// We read the maximum allowed but didn't complete the start line.
BadHttpRequestException.Throw(RequestRejectionReason.RequestLineTooLong);
}
return true;
}
} }
public bool TakeMessageHeaders(in ReadOnlySequence<byte> buffer, bool trailers, out SequencePosition consumed, out SequencePosition examined) public bool TakeMessageHeaders(in ReadOnlySequence<byte> buffer, bool trailers, out SequencePosition consumed, out SequencePosition examined)
@ -192,6 +208,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
// Make sure the buffer is limited // Make sure the buffer is limited
if (buffer.Length > _remainingRequestHeadersBytesAllowed) if (buffer.Length > _remainingRequestHeadersBytesAllowed)
{ {
// Input oversize, cap amount checked
return TrimAndTakeMessageHeaders(buffer, trailers, out consumed, out examined); return TrimAndTakeMessageHeaders(buffer, trailers, out consumed, out examined);
} }

View File

@ -108,7 +108,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2.HPack
_headerValueOctets = new byte[maxRequestHeaderFieldSize]; _headerValueOctets = new byte[maxRequestHeaderFieldSize];
} }
public void Decode(ReadOnlySequence<byte> data, bool endHeaders, IHttpHeadersHandler handler) public void Decode(in ReadOnlySequence<byte> data, bool endHeaders, IHttpHeadersHandler handler)
{ {
foreach (var segment in data) foreach (var segment in data)
{ {

View File

@ -379,7 +379,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
return true; return true;
} }
private Task ProcessFrameAsync<TContext>(IHttpApplication<TContext> application, ReadOnlySequence<byte> payload) private Task ProcessFrameAsync<TContext>(IHttpApplication<TContext> application, in ReadOnlySequence<byte> payload)
{ {
// http://httpwg.org/specs/rfc7540.html#rfc.section.5.1.1 // http://httpwg.org/specs/rfc7540.html#rfc.section.5.1.1
// Streams initiated by a client MUST use odd-numbered stream identifiers; ... // Streams initiated by a client MUST use odd-numbered stream identifiers; ...
@ -417,7 +417,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
} }
} }
private Task ProcessDataFrameAsync(ReadOnlySequence<byte> payload) private Task ProcessDataFrameAsync(in ReadOnlySequence<byte> payload)
{ {
if (_currentHeadersStream != null) if (_currentHeadersStream != null)
{ {
@ -473,7 +473,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorStreamClosed(_incomingFrame.Type, _incomingFrame.StreamId), Http2ErrorCode.STREAM_CLOSED); throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorStreamClosed(_incomingFrame.Type, _incomingFrame.StreamId), Http2ErrorCode.STREAM_CLOSED);
} }
private Task ProcessHeadersFrameAsync<TContext>(IHttpApplication<TContext> application, ReadOnlySequence<byte> payload) private Task ProcessHeadersFrameAsync<TContext>(IHttpApplication<TContext> application, in ReadOnlySequence<byte> payload)
{ {
if (_currentHeadersStream != null) if (_currentHeadersStream != null)
{ {
@ -640,7 +640,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
return Task.CompletedTask; return Task.CompletedTask;
} }
private Task ProcessSettingsFrameAsync(ReadOnlySequence<byte> payload) private Task ProcessSettingsFrameAsync(in ReadOnlySequence<byte> payload)
{ {
if (_currentHeadersStream != null) if (_currentHeadersStream != null)
{ {
@ -711,7 +711,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
} }
} }
private Task ProcessPingFrameAsync(ReadOnlySequence<byte> payload) private Task ProcessPingFrameAsync(in ReadOnlySequence<byte> payload)
{ {
if (_currentHeadersStream != null) if (_currentHeadersStream != null)
{ {
@ -818,7 +818,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
return Task.CompletedTask; return Task.CompletedTask;
} }
private Task ProcessContinuationFrameAsync(ReadOnlySequence<byte> payload) private Task ProcessContinuationFrameAsync(in ReadOnlySequence<byte> payload)
{ {
if (_currentHeadersStream == null) if (_currentHeadersStream == null)
{ {
@ -857,7 +857,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
return Task.CompletedTask; return Task.CompletedTask;
} }
private Task DecodeHeadersAsync(bool endHeaders, ReadOnlySequence<byte> payload) private Task DecodeHeadersAsync(bool endHeaders, in ReadOnlySequence<byte> payload)
{ {
try try
{ {
@ -879,7 +879,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
return Task.CompletedTask; return Task.CompletedTask;
} }
private Task DecodeTrailersAsync(bool endHeaders, ReadOnlySequence<byte> payload) private Task DecodeTrailersAsync(bool endHeaders, in ReadOnlySequence<byte> payload)
{ {
_hpackDecoder.Decode(payload, endHeaders, handler: this); _hpackDecoder.Decode(payload, endHeaders, handler: this);

View File

@ -31,7 +31,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
public const int SettingSize = 6; // 2 bytes for the id, 4 bytes for the value. public const int SettingSize = 6; // 2 bytes for the id, 4 bytes for the value.
public static bool ReadFrame(ReadOnlySequence<byte> readableBuffer, Http2Frame frame, uint maxFrameSize, out ReadOnlySequence<byte> framePayload) public static bool ReadFrame(in ReadOnlySequence<byte> readableBuffer, Http2Frame frame, uint maxFrameSize, out ReadOnlySequence<byte> framePayload)
{ {
framePayload = ReadOnlySequence<byte>.Empty; framePayload = ReadOnlySequence<byte>.Empty;
@ -69,7 +69,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
return true; return true;
} }
private static int ReadExtendedFields(Http2Frame frame, ReadOnlySequence<byte> readableBuffer) private static int ReadExtendedFields(Http2Frame frame, in ReadOnlySequence<byte> readableBuffer)
{ {
// Copy in any extra fields for the given frame type // Copy in any extra fields for the given frame type
var extendedHeaderLength = GetPayloadFieldsLength(frame); var extendedHeaderLength = GetPayloadFieldsLength(frame);
@ -217,7 +217,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
} }
} }
public static IList<Http2PeerSetting> ReadSettings(ReadOnlySequence<byte> payload) public static IList<Http2PeerSetting> ReadSettings(in ReadOnlySequence<byte> payload)
{ {
var data = payload.ToSpan(); var data = payload.ToSpan();
Debug.Assert(data.Length % SettingSize == 0, "Invalid settings payload length"); Debug.Assert(data.Length % SettingSize == 0, "Invalid settings payload length");

View File

@ -235,7 +235,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
} }
} }
public ValueTask<FlushResult> WriteDataAsync(int streamId, StreamOutputFlowControl flowControl, ReadOnlySequence<byte> data, bool endStream) public ValueTask<FlushResult> WriteDataAsync(int streamId, StreamOutputFlowControl flowControl, in ReadOnlySequence<byte> data, bool endStream)
{ {
// The Length property of a ReadOnlySequence can be expensive, so we cache the value. // The Length property of a ReadOnlySequence can be expensive, so we cache the value.
var dataLength = data.Length; var dataLength = data.Length;
@ -270,28 +270,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
| Padding (*) ... | Padding (*) ...
+---------------------------------------------------------------+ +---------------------------------------------------------------+
*/ */
private void WriteDataUnsynchronized(int streamId, ReadOnlySequence<byte> data, long dataLength, bool endStream) private void WriteDataUnsynchronized(int streamId, in ReadOnlySequence<byte> data, long dataLength, bool endStream)
{ {
Debug.Assert(dataLength == data.Length);
// Note padding is not implemented // Note padding is not implemented
_outgoingFrame.PrepareData(streamId); _outgoingFrame.PrepareData(streamId);
var dataPayloadLength = (int)_maxFrameSize; // Minus padding if (dataLength > _maxFrameSize) // Minus padding
while (data.Length > dataPayloadLength)
{ {
var currentData = data.Slice(0, dataPayloadLength); TrimAndWriteDataUnsynchronized(in data, dataLength, endStream);
_outgoingFrame.PayloadLength = dataPayloadLength; // Plus padding return;
WriteHeaderUnsynchronized();
foreach (var buffer in currentData)
{
_outputWriter.Write(buffer.Span);
}
// Plus padding
data = data.Slice(dataPayloadLength);
} }
if (endStream) if (endStream)
@ -299,7 +288,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
_outgoingFrame.DataFlags |= Http2DataFrameFlags.END_STREAM; _outgoingFrame.DataFlags |= Http2DataFrameFlags.END_STREAM;
} }
_outgoingFrame.PayloadLength = (int)data.Length; // Plus padding _outgoingFrame.PayloadLength = (int)dataLength; // Plus padding
WriteHeaderUnsynchronized(); WriteHeaderUnsynchronized();
@ -309,6 +298,51 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
} }
// Plus padding // Plus padding
return;
void TrimAndWriteDataUnsynchronized(in ReadOnlySequence<byte> data, long dataLength, bool endStream)
{
Debug.Assert(dataLength == data.Length);
var dataPayloadLength = (int)_maxFrameSize; // Minus padding
Debug.Assert(dataLength > dataPayloadLength);
var remainingData = data;
do
{
var currentData = remainingData.Slice(0, dataPayloadLength);
_outgoingFrame.PayloadLength = dataPayloadLength; // Plus padding
WriteHeaderUnsynchronized();
foreach (var buffer in currentData)
{
_outputWriter.Write(buffer.Span);
}
// Plus padding
dataLength -= dataPayloadLength;
remainingData = remainingData.Slice(dataPayloadLength);
} while (dataLength > dataPayloadLength);
if (endStream)
{
_outgoingFrame.DataFlags |= Http2DataFrameFlags.END_STREAM;
}
_outgoingFrame.PayloadLength = (int)dataLength; // Plus padding
WriteHeaderUnsynchronized();
foreach (var buffer in remainingData)
{
_outputWriter.Write(buffer.Span);
}
// Plus padding
}
} }
private async ValueTask<FlushResult> WriteDataAsync(int streamId, StreamOutputFlowControl flowControl, ReadOnlySequence<byte> data, long dataLength, bool endStream) private async ValueTask<FlushResult> WriteDataAsync(int streamId, StreamOutputFlowControl flowControl, ReadOnlySequence<byte> data, long dataLength, bool endStream)
@ -498,7 +532,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
| | | |
+---------------------------------------------------------------+ +---------------------------------------------------------------+
*/ */
public ValueTask<FlushResult> WritePingAsync(Http2PingFrameFlags flags, ReadOnlySequence<byte> payload) public ValueTask<FlushResult> WritePingAsync(Http2PingFrameFlags flags, in ReadOnlySequence<byte> payload)
{ {
lock (_writeLock) lock (_writeLock)
{ {

View File

@ -320,7 +320,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
} }
} }
public Task OnDataAsync(Http2Frame dataFrame, ReadOnlySequence<byte> payload) public Task OnDataAsync(Http2Frame dataFrame, in ReadOnlySequence<byte> payload)
{ {
// Since padding isn't buffered, immediately count padding bytes as read for flow control purposes. // Since padding isn't buffered, immediately count padding bytes as read for flow control purposes.
if (dataFrame.DataHasPadding) if (dataFrame.DataHasPadding)

View File

@ -48,7 +48,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networkin
_bufs = handle + requestSize; _bufs = handle + requestSize;
} }
public LibuvAwaitable<UvWriteReq> WriteAsync(UvStreamHandle handle, ReadOnlySequence<byte> buffer) public LibuvAwaitable<UvWriteReq> WriteAsync(UvStreamHandle handle, in ReadOnlySequence<byte> buffer)
{ {
Write(handle, buffer, LibuvAwaitable<UvWriteReq>.Callback, _awaitable); Write(handle, buffer, LibuvAwaitable<UvWriteReq>.Callback, _awaitable);
return _awaitable; return _awaitable;
@ -62,7 +62,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networkin
private unsafe void Write( private unsafe void Write(
UvStreamHandle handle, UvStreamHandle handle,
ReadOnlySequence<byte> buffer, in ReadOnlySequence<byte> buffer,
Action<UvWriteReq, int, UvException, object> callback, Action<UvWriteReq, int, UvException, object> callback,
object state) object state)
{ {

View File

@ -19,7 +19,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.Internal
{ {
} }
public SocketAwaitableEventArgs SendAsync(ReadOnlySequence<byte> buffers) public SocketAwaitableEventArgs SendAsync(in ReadOnlySequence<byte> buffers)
{ {
if (buffers.IsSingleSegment) if (buffers.IsSingleSegment)
{ {
@ -59,7 +59,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.Internal
return _awaitableEventArgs; return _awaitableEventArgs;
} }
private List<ArraySegment<byte>> GetBufferList(ReadOnlySequence<byte> buffer) private List<ArraySegment<byte>> GetBufferList(in ReadOnlySequence<byte> buffer)
{ {
Debug.Assert(!buffer.IsEmpty); Debug.Assert(!buffer.IsEmpty);
Debug.Assert(!buffer.IsSingleSegment); Debug.Assert(!buffer.IsSingleSegment);