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.");
}
private string GetDecodedStringFromReadOnlySequence(ReadOnlySequence<byte> ros)
private string GetDecodedStringFromReadOnlySequence(in ReadOnlySequence<byte> ros)
{
if (ros.IsSingleSegment)
{

View File

@ -17,7 +17,7 @@ namespace System.Buffers
private static byte[] _numericBytesScratch;
[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)
{

View File

@ -220,7 +220,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
_requestBodyPipe.Reset();
}
private void Copy(ReadOnlySequence<byte> readableBuffer, PipeWriter writableBuffer)
private void Copy(in ReadOnlySequence<byte> readableBuffer, PipeWriter writableBuffer)
{
if (readableBuffer.IsSingleSegment)
{
@ -321,10 +321,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
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;
examined = buffer.Start;
var reader = new SequenceReader<byte>(buffer);
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
// Just drain the data
consumed = buffer.Start;
examined = buffer.Start;
do
@ -432,7 +430,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
} 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);
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;
examined = buffer.Start;
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;
examined = buffer.Start;
if (buffer.Length < 2)
{

View File

@ -131,7 +131,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
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;
examined = buffer.End;
@ -151,10 +151,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
case RequestProcessingStatus.ParsingRequestLine:
if (TakeStartLine(buffer, out consumed, out examined))
{
buffer = buffer.Slice(consumed, buffer.End);
_requestProcessingStatus = RequestProcessingStatus.ParsingHeaders;
goto case RequestProcessingStatus.ParsingHeaders;
TrimAndParseHeaders(buffer, ref consumed, out examined);
return;
}
else
{
@ -167,24 +165,42 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
}
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)
{
buffer = buffer.Slice(buffer.Start, ServerOptions.Limits.MaxRequestLineSize);
overLength = true;
// Input oversize, cap amount checked
return TrimAndTakeStartLine(buffer, out consumed, out examined);
}
var result = _parser.ParseRequestLine(new Http1ParsingHandler(this), buffer, out consumed, out examined);
if (!result && overLength)
return _parser.ParseRequestLine(new Http1ParsingHandler(this), buffer, out consumed, out examined);
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)
@ -192,6 +208,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
// Make sure the buffer is limited
if (buffer.Length > _remainingRequestHeadersBytesAllowed)
{
// Input oversize, cap amount checked
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];
}
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)
{

View File

@ -379,7 +379,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
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
// 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)
{
@ -473,7 +473,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
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)
{
@ -640,7 +640,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
return Task.CompletedTask;
}
private Task ProcessSettingsFrameAsync(ReadOnlySequence<byte> payload)
private Task ProcessSettingsFrameAsync(in ReadOnlySequence<byte> payload)
{
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)
{
@ -818,7 +818,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
return Task.CompletedTask;
}
private Task ProcessContinuationFrameAsync(ReadOnlySequence<byte> payload)
private Task ProcessContinuationFrameAsync(in ReadOnlySequence<byte> payload)
{
if (_currentHeadersStream == null)
{
@ -857,7 +857,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
return Task.CompletedTask;
}
private Task DecodeHeadersAsync(bool endHeaders, ReadOnlySequence<byte> payload)
private Task DecodeHeadersAsync(bool endHeaders, in ReadOnlySequence<byte> payload)
{
try
{
@ -879,7 +879,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
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);

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 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;
@ -69,7 +69,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
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
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();
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.
var dataLength = data.Length;
@ -270,28 +270,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
| 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
_outgoingFrame.PrepareData(streamId);
var dataPayloadLength = (int)_maxFrameSize; // Minus padding
while (data.Length > dataPayloadLength)
if (dataLength > _maxFrameSize) // Minus padding
{
var currentData = data.Slice(0, dataPayloadLength);
_outgoingFrame.PayloadLength = dataPayloadLength; // Plus padding
WriteHeaderUnsynchronized();
foreach (var buffer in currentData)
{
_outputWriter.Write(buffer.Span);
}
// Plus padding
data = data.Slice(dataPayloadLength);
TrimAndWriteDataUnsynchronized(in data, dataLength, endStream);
return;
}
if (endStream)
@ -299,7 +288,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
_outgoingFrame.DataFlags |= Http2DataFrameFlags.END_STREAM;
}
_outgoingFrame.PayloadLength = (int)data.Length; // Plus padding
_outgoingFrame.PayloadLength = (int)dataLength; // Plus padding
WriteHeaderUnsynchronized();
@ -309,6 +298,51 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
}
// 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)
@ -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)
{

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.
if (dataFrame.DataHasPadding)

View File

@ -48,7 +48,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networkin
_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);
return _awaitable;
@ -62,7 +62,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Internal.Networkin
private unsafe void Write(
UvStreamHandle handle,
ReadOnlySequence<byte> buffer,
in ReadOnlySequence<byte> buffer,
Action<UvWriteReq, int, UvException, object> callback,
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)
{
@ -59,7 +59,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.Internal
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.IsSingleSegment);