diff --git a/src/Microsoft.AspNetCore.Server.Kestrel.Core/Adapter/Internal/StreamSocketOutput.cs b/src/Microsoft.AspNetCore.Server.Kestrel.Core/Adapter/Internal/StreamSocketOutput.cs index 7fb38dc67c..d7e7529f6d 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel.Core/Adapter/Internal/StreamSocketOutput.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel.Core/Adapter/Internal/StreamSocketOutput.cs @@ -41,19 +41,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal return; } - var writableBuffer = _pipe.Writer.Alloc(); - + var writableBuffer = _pipe.Writer.Alloc(1); + var writer = new WritableBufferWriter(writableBuffer); if (buffer.Count > 0) { if (chunk) { - ChunkWriter.WriteBeginChunkBytes(ref writableBuffer, buffer.Count); - writableBuffer.WriteFast(buffer); - ChunkWriter.WriteEndChunkBytes(ref writableBuffer); + ChunkWriter.WriteBeginChunkBytes(ref writer, buffer.Count); + writer.Write(buffer.Array, buffer.Offset, buffer.Count); + ChunkWriter.WriteEndChunkBytes(ref writer); } else { - writableBuffer.WriteFast(buffer); + writer.Write(buffer.Array, buffer.Offset, buffer.Count); } } @@ -92,7 +92,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal return; } - var buffer = _pipe.Writer.Alloc(); + var buffer = _pipe.Writer.Alloc(1); callback(buffer, state); buffer.Commit(); } diff --git a/src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/Http/ChunkWriter.cs b/src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/Http/ChunkWriter.cs index 06ad5877ad..bf34855007 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/Http/ChunkWriter.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/Http/ChunkWriter.cs @@ -47,16 +47,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http return new ArraySegment(bytes, offset, 10 - offset); } - public static int WriteBeginChunkBytes(ref WritableBuffer start, int dataCount) + public static int WriteBeginChunkBytes(ref WritableBufferWriter start, int dataCount) { var chunkSegment = BeginChunkBytes(dataCount); - start.WriteFast(chunkSegment); + start.Write(chunkSegment.Array, chunkSegment.Offset, chunkSegment.Count); return chunkSegment.Count; } - public static void WriteEndChunkBytes(ref WritableBuffer start) + public static void WriteEndChunkBytes(ref WritableBufferWriter start) { - start.WriteFast(_endChunkBytes); + start.Write(_endChunkBytes.Array, _endChunkBytes.Offset, _endChunkBytes.Count); } } } diff --git a/src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/Http/Frame.cs b/src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/Http/Frame.cs index 53c723081e..d21c4c5563 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/Http/Frame.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/Http/Frame.cs @@ -993,12 +993,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http private static void WriteResponseHeaders(WritableBuffer writableBuffer, Frame frame) { + var writer = new WritableBufferWriter(writableBuffer); + var responseHeaders = frame.FrameResponseHeaders; - writableBuffer.WriteFast(_bytesHttpVersion11); + writer.Write(_bytesHttpVersion11); var statusBytes = ReasonPhrases.ToStatusBytes(frame.StatusCode, frame.ReasonPhrase); - writableBuffer.WriteFast(statusBytes); - responseHeaders.CopyTo(ref writableBuffer); - writableBuffer.WriteFast(_bytesEndHeaders); + writer.Write(statusBytes); + responseHeaders.CopyTo(ref writer); + writer.Write(_bytesEndHeaders); } public void ParseRequest(ReadableBuffer buffer, out ReadCursor consumed, out ReadCursor examined) diff --git a/src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/Http/FrameHeaders.Generated.cs b/src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/Http/FrameHeaders.Generated.cs index dd2f346ecc..2841efcc87 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/Http/FrameHeaders.Generated.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/Http/FrameHeaders.Generated.cs @@ -7753,7 +7753,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http return true; } - protected void CopyToFast(ref WritableBuffer output) + protected void CopyToFast(ref WritableBufferWriter output) { var tempBits = _bits | (_contentLength.HasValue ? -9223372036854775808L : 0); @@ -7761,7 +7761,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http { if (_headers._rawConnection != null) { - output.WriteFast(_headers._rawConnection); + output.Write(_headers._rawConnection); } else { @@ -7771,8 +7771,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http var value = _headers._Connection[i]; if (value != null) { - output.WriteFast(_headerBytes, 17, 14); - output.WriteAsciiNoValidation(value); + output.Write(_headerBytes, 17, 14); + PipelineExtensions.WriteAsciiNoValidation(ref output, value); } } } @@ -7787,7 +7787,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http { if (_headers._rawDate != null) { - output.WriteFast(_headers._rawDate); + output.Write(_headers._rawDate); } else { @@ -7797,8 +7797,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http var value = _headers._Date[i]; if (value != null) { - output.WriteFast(_headerBytes, 31, 8); - output.WriteAsciiNoValidation(value); + output.Write(_headerBytes, 31, 8); + PipelineExtensions.WriteAsciiNoValidation(ref output, value); } } } @@ -7818,8 +7818,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http var value = _headers._ContentType[i]; if (value != null) { - output.WriteFast(_headerBytes, 133, 16); - output.WriteAsciiNoValidation(value); + output.Write(_headerBytes, 133, 16); + PipelineExtensions.WriteAsciiNoValidation(ref output, value); } } } @@ -7834,7 +7834,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http { if (_headers._rawServer != null) { - output.WriteFast(_headers._rawServer); + output.Write(_headers._rawServer); } else { @@ -7844,8 +7844,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http var value = _headers._Server[i]; if (value != null) { - output.WriteFast(_headerBytes, 350, 10); - output.WriteAsciiNoValidation(value); + output.Write(_headerBytes, 350, 10); + PipelineExtensions.WriteAsciiNoValidation(ref output, value); } } } @@ -7858,8 +7858,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http } if ((tempBits & -9223372036854775808L) != 0) { - output.WriteFast(_headerBytes, 592, 18); - output.WriteNumeric((ulong)ContentLength.Value); + output.Write(_headerBytes, 592, 18); + PipelineExtensions.WriteNumeric(ref output, (ulong)ContentLength.Value); if((tempBits & ~-9223372036854775808L) == 0) { @@ -7876,8 +7876,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http var value = _headers._CacheControl[i]; if (value != null) { - output.WriteFast(_headerBytes, 0, 17); - output.WriteAsciiNoValidation(value); + output.Write(_headerBytes, 0, 17); + PipelineExtensions.WriteAsciiNoValidation(ref output, value); } } } @@ -7897,8 +7897,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http var value = _headers._KeepAlive[i]; if (value != null) { - output.WriteFast(_headerBytes, 39, 14); - output.WriteAsciiNoValidation(value); + output.Write(_headerBytes, 39, 14); + PipelineExtensions.WriteAsciiNoValidation(ref output, value); } } } @@ -7918,8 +7918,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http var value = _headers._Pragma[i]; if (value != null) { - output.WriteFast(_headerBytes, 53, 10); - output.WriteAsciiNoValidation(value); + output.Write(_headerBytes, 53, 10); + PipelineExtensions.WriteAsciiNoValidation(ref output, value); } } } @@ -7939,8 +7939,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http var value = _headers._Trailer[i]; if (value != null) { - output.WriteFast(_headerBytes, 63, 11); - output.WriteAsciiNoValidation(value); + output.Write(_headerBytes, 63, 11); + PipelineExtensions.WriteAsciiNoValidation(ref output, value); } } } @@ -7955,7 +7955,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http { if (_headers._rawTransferEncoding != null) { - output.WriteFast(_headers._rawTransferEncoding); + output.Write(_headers._rawTransferEncoding); } else { @@ -7965,8 +7965,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http var value = _headers._TransferEncoding[i]; if (value != null) { - output.WriteFast(_headerBytes, 74, 21); - output.WriteAsciiNoValidation(value); + output.Write(_headerBytes, 74, 21); + PipelineExtensions.WriteAsciiNoValidation(ref output, value); } } } @@ -7986,8 +7986,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http var value = _headers._Upgrade[i]; if (value != null) { - output.WriteFast(_headerBytes, 95, 11); - output.WriteAsciiNoValidation(value); + output.Write(_headerBytes, 95, 11); + PipelineExtensions.WriteAsciiNoValidation(ref output, value); } } } @@ -8007,8 +8007,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http var value = _headers._Via[i]; if (value != null) { - output.WriteFast(_headerBytes, 106, 7); - output.WriteAsciiNoValidation(value); + output.Write(_headerBytes, 106, 7); + PipelineExtensions.WriteAsciiNoValidation(ref output, value); } } } @@ -8028,8 +8028,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http var value = _headers._Warning[i]; if (value != null) { - output.WriteFast(_headerBytes, 113, 11); - output.WriteAsciiNoValidation(value); + output.Write(_headerBytes, 113, 11); + PipelineExtensions.WriteAsciiNoValidation(ref output, value); } } } @@ -8049,8 +8049,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http var value = _headers._Allow[i]; if (value != null) { - output.WriteFast(_headerBytes, 124, 9); - output.WriteAsciiNoValidation(value); + output.Write(_headerBytes, 124, 9); + PipelineExtensions.WriteAsciiNoValidation(ref output, value); } } } @@ -8070,8 +8070,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http var value = _headers._ContentEncoding[i]; if (value != null) { - output.WriteFast(_headerBytes, 149, 20); - output.WriteAsciiNoValidation(value); + output.Write(_headerBytes, 149, 20); + PipelineExtensions.WriteAsciiNoValidation(ref output, value); } } } @@ -8091,8 +8091,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http var value = _headers._ContentLanguage[i]; if (value != null) { - output.WriteFast(_headerBytes, 169, 20); - output.WriteAsciiNoValidation(value); + output.Write(_headerBytes, 169, 20); + PipelineExtensions.WriteAsciiNoValidation(ref output, value); } } } @@ -8112,8 +8112,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http var value = _headers._ContentLocation[i]; if (value != null) { - output.WriteFast(_headerBytes, 189, 20); - output.WriteAsciiNoValidation(value); + output.Write(_headerBytes, 189, 20); + PipelineExtensions.WriteAsciiNoValidation(ref output, value); } } } @@ -8133,8 +8133,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http var value = _headers._ContentMD5[i]; if (value != null) { - output.WriteFast(_headerBytes, 209, 15); - output.WriteAsciiNoValidation(value); + output.Write(_headerBytes, 209, 15); + PipelineExtensions.WriteAsciiNoValidation(ref output, value); } } } @@ -8154,8 +8154,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http var value = _headers._ContentRange[i]; if (value != null) { - output.WriteFast(_headerBytes, 224, 17); - output.WriteAsciiNoValidation(value); + output.Write(_headerBytes, 224, 17); + PipelineExtensions.WriteAsciiNoValidation(ref output, value); } } } @@ -8175,8 +8175,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http var value = _headers._Expires[i]; if (value != null) { - output.WriteFast(_headerBytes, 241, 11); - output.WriteAsciiNoValidation(value); + output.Write(_headerBytes, 241, 11); + PipelineExtensions.WriteAsciiNoValidation(ref output, value); } } } @@ -8196,8 +8196,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http var value = _headers._LastModified[i]; if (value != null) { - output.WriteFast(_headerBytes, 252, 17); - output.WriteAsciiNoValidation(value); + output.Write(_headerBytes, 252, 17); + PipelineExtensions.WriteAsciiNoValidation(ref output, value); } } } @@ -8217,8 +8217,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http var value = _headers._AcceptRanges[i]; if (value != null) { - output.WriteFast(_headerBytes, 269, 17); - output.WriteAsciiNoValidation(value); + output.Write(_headerBytes, 269, 17); + PipelineExtensions.WriteAsciiNoValidation(ref output, value); } } } @@ -8238,8 +8238,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http var value = _headers._Age[i]; if (value != null) { - output.WriteFast(_headerBytes, 286, 7); - output.WriteAsciiNoValidation(value); + output.Write(_headerBytes, 286, 7); + PipelineExtensions.WriteAsciiNoValidation(ref output, value); } } } @@ -8259,8 +8259,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http var value = _headers._ETag[i]; if (value != null) { - output.WriteFast(_headerBytes, 293, 8); - output.WriteAsciiNoValidation(value); + output.Write(_headerBytes, 293, 8); + PipelineExtensions.WriteAsciiNoValidation(ref output, value); } } } @@ -8280,8 +8280,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http var value = _headers._Location[i]; if (value != null) { - output.WriteFast(_headerBytes, 301, 12); - output.WriteAsciiNoValidation(value); + output.Write(_headerBytes, 301, 12); + PipelineExtensions.WriteAsciiNoValidation(ref output, value); } } } @@ -8301,8 +8301,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http var value = _headers._ProxyAuthenticate[i]; if (value != null) { - output.WriteFast(_headerBytes, 313, 22); - output.WriteAsciiNoValidation(value); + output.Write(_headerBytes, 313, 22); + PipelineExtensions.WriteAsciiNoValidation(ref output, value); } } } @@ -8322,8 +8322,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http var value = _headers._RetryAfter[i]; if (value != null) { - output.WriteFast(_headerBytes, 335, 15); - output.WriteAsciiNoValidation(value); + output.Write(_headerBytes, 335, 15); + PipelineExtensions.WriteAsciiNoValidation(ref output, value); } } } @@ -8343,8 +8343,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http var value = _headers._SetCookie[i]; if (value != null) { - output.WriteFast(_headerBytes, 360, 14); - output.WriteAsciiNoValidation(value); + output.Write(_headerBytes, 360, 14); + PipelineExtensions.WriteAsciiNoValidation(ref output, value); } } } @@ -8364,8 +8364,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http var value = _headers._Vary[i]; if (value != null) { - output.WriteFast(_headerBytes, 374, 8); - output.WriteAsciiNoValidation(value); + output.Write(_headerBytes, 374, 8); + PipelineExtensions.WriteAsciiNoValidation(ref output, value); } } } @@ -8385,8 +8385,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http var value = _headers._WWWAuthenticate[i]; if (value != null) { - output.WriteFast(_headerBytes, 382, 20); - output.WriteAsciiNoValidation(value); + output.Write(_headerBytes, 382, 20); + PipelineExtensions.WriteAsciiNoValidation(ref output, value); } } } @@ -8406,8 +8406,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http var value = _headers._AccessControlAllowCredentials[i]; if (value != null) { - output.WriteFast(_headerBytes, 402, 36); - output.WriteAsciiNoValidation(value); + output.Write(_headerBytes, 402, 36); + PipelineExtensions.WriteAsciiNoValidation(ref output, value); } } } @@ -8427,8 +8427,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http var value = _headers._AccessControlAllowHeaders[i]; if (value != null) { - output.WriteFast(_headerBytes, 438, 32); - output.WriteAsciiNoValidation(value); + output.Write(_headerBytes, 438, 32); + PipelineExtensions.WriteAsciiNoValidation(ref output, value); } } } @@ -8448,8 +8448,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http var value = _headers._AccessControlAllowMethods[i]; if (value != null) { - output.WriteFast(_headerBytes, 470, 32); - output.WriteAsciiNoValidation(value); + output.Write(_headerBytes, 470, 32); + PipelineExtensions.WriteAsciiNoValidation(ref output, value); } } } @@ -8469,8 +8469,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http var value = _headers._AccessControlAllowOrigin[i]; if (value != null) { - output.WriteFast(_headerBytes, 502, 31); - output.WriteAsciiNoValidation(value); + output.Write(_headerBytes, 502, 31); + PipelineExtensions.WriteAsciiNoValidation(ref output, value); } } } @@ -8490,8 +8490,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http var value = _headers._AccessControlExposeHeaders[i]; if (value != null) { - output.WriteFast(_headerBytes, 533, 33); - output.WriteAsciiNoValidation(value); + output.Write(_headerBytes, 533, 33); + PipelineExtensions.WriteAsciiNoValidation(ref output, value); } } } @@ -8511,8 +8511,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http var value = _headers._AccessControlMaxAge[i]; if (value != null) { - output.WriteFast(_headerBytes, 566, 26); - output.WriteAsciiNoValidation(value); + output.Write(_headerBytes, 566, 26); + PipelineExtensions.WriteAsciiNoValidation(ref output, value); } } } diff --git a/src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/Http/FrameResponseHeaders.cs b/src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/Http/FrameResponseHeaders.cs index f0dc531e48..f5093f691f 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/Http/FrameResponseHeaders.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/Http/FrameResponseHeaders.cs @@ -34,7 +34,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http return GetEnumerator(); } - public void CopyTo(ref WritableBuffer output) + public void CopyTo(ref WritableBufferWriter output) { CopyToFast(ref output); if (MaybeUnknown != null) @@ -45,10 +45,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http { if (value != null) { - output.WriteFast(_CrLf); - output.WriteAsciiNoValidation(kv.Key); - output.WriteFast(_colonSpace); - output.WriteAsciiNoValidation(value); + output.Write(_CrLf); + PipelineExtensions.WriteAsciiNoValidation(ref output, kv.Key); + output.Write(_colonSpace); + PipelineExtensions.WriteAsciiNoValidation(ref output, value); } } } diff --git a/src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/Http/OutputProducer.cs b/src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/Http/OutputProducer.cs index 83294c7ed5..261db5c1d7 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/Http/OutputProducer.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/Http/OutputProducer.cs @@ -26,7 +26,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http private readonly IPipeWriter _pipe; private readonly Frame _frame; - // https://github.com/dotnet/corefxlab/issues/1334 + // https://github.com/dotnet/corefxlab/issues/1334 // Pipelines don't support multiple awaiters on flush // this is temporary until it does private TaskCompletionSource _flushTcs; @@ -59,20 +59,20 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http return TaskCache.CompletedTask; } - writableBuffer = _pipe.Alloc(); - + writableBuffer = _pipe.Alloc(1); + var writer = new WritableBufferWriter(writableBuffer); if (buffer.Count > 0) { if (chunk) { - ChunkWriter.WriteBeginChunkBytes(ref writableBuffer, buffer.Count); + ChunkWriter.WriteBeginChunkBytes(ref writer, buffer.Count); } - writableBuffer.WriteFast(buffer); + writer.Write(buffer.Array, buffer.Offset, buffer.Count); if (chunk) { - ChunkWriter.WriteEndChunkBytes(ref writableBuffer); + ChunkWriter.WriteEndChunkBytes(ref writer); } } @@ -95,7 +95,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http private Task FlushAsyncAwaited(WritableBufferAwaitable awaitable) { - // https://github.com/dotnet/corefxlab/issues/1334 + // https://github.com/dotnet/corefxlab/issues/1334 // Since the flush awaitable doesn't currently support multiple awaiters // we need to use a task to track the callbacks. // All awaiters get the same task @@ -157,7 +157,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http return; } - var buffer = _pipe.Alloc(); + var buffer = _pipe.Alloc(1); callback(buffer, state); buffer.Commit(); } diff --git a/src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/Http/PipelineExtensions.cs b/src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/Http/PipelineExtensions.cs index 19ce278bea..d9b27dfbbf 100644 --- a/src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/Http/PipelineExtensions.cs +++ b/src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/Http/PipelineExtensions.cs @@ -95,98 +95,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http return result; } - // Temporary until the fast write implementation propagates from corefx - public unsafe static void WriteFast(this WritableBuffer buffer, byte[] source) - { - buffer.WriteFast(source, 0, source.Length); - } - - public unsafe static void WriteFast(this WritableBuffer buffer, ArraySegment source) - { - buffer.WriteFast(source.Array, source.Offset, source.Count); - } - - public unsafe static void WriteFast(this WritableBuffer buffer, byte[] source, int offset, int length) - { - var dest = buffer.Buffer.Span; - var destLength = dest.Length; - - if (destLength == 0) - { - buffer.Ensure(); - - // Get the new span and length - dest = buffer.Buffer.Span; - destLength = dest.Length; - } - - var sourceLength = length; - if (sourceLength <= destLength) - { - ref byte pSource = ref source[offset]; - ref byte pDest = ref dest.DangerousGetPinnableReference(); - Unsafe.CopyBlockUnaligned(ref pDest, ref pSource, (uint)sourceLength); - buffer.Advance(sourceLength); - return; - } - - buffer.WriteMultiBuffer(source, offset, length); - } - - private static unsafe void WriteMultiBuffer(this WritableBuffer buffer, byte[] source, int offset, int length) - { - var remaining = length; - - while (remaining > 0) - { - var writable = Math.Min(remaining, buffer.Buffer.Length); - - buffer.Ensure(writable); - - if (writable == 0) - { - continue; - } - - ref byte pSource = ref source[offset]; - ref byte pDest = ref buffer.Buffer.Span.DangerousGetPinnableReference(); - - Unsafe.CopyBlockUnaligned(ref pDest, ref pSource, (uint)writable); - - remaining -= writable; - offset += writable; - - buffer.Advance(writable); - } - } - - /// - /// Write string characters as ASCII without validating that characters fall in the ASCII range - /// - /// - /// ASCII character validation is done by - /// - /// the buffer - /// The string to write - public unsafe static void WriteAsciiNoValidation(this WritableBuffer buffer, string data) + public unsafe static void WriteAsciiNoValidation(ref WritableBufferWriter buffer, string data) { if (string.IsNullOrEmpty(data)) { return; } - var dest = buffer.Buffer.Span; + var dest = buffer.Span; var destLength = dest.Length; var sourceLength = data.Length; - if (destLength == 0) - { - buffer.Ensure(); - - dest = buffer.Buffer.Span; - destLength = dest.Length; - } - // Fast path, try copying to the available memory directly if (sourceLength <= destLength) { @@ -200,26 +119,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http } else { - buffer.WriteAsciiMultiWrite(data); + WriteAsciiMultiWrite(ref buffer, data); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe static void WriteNumeric(this WritableBuffer buffer, ulong number) + public unsafe static void WriteNumeric(ref WritableBufferWriter buffer, ulong number) { const byte AsciiDigitStart = (byte)'0'; - var span = buffer.Buffer.Span; + var span = buffer.Span; var bytesLeftInBlock = span.Length; - if (bytesLeftInBlock == 0) - { - buffer.Ensure(); - - span = buffer.Buffer.Span; - bytesLeftInBlock = span.Length; - } - // Fast path, try copying to the available memory directly var simpleWrite = true; fixed (byte* output = &span.DangerousGetPinnableReference()) @@ -258,12 +169,12 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http if (!simpleWrite) { - buffer.WriteNumericMultiWrite(number); + WriteNumericMultiWrite(ref buffer, number); } } [MethodImpl(MethodImplOptions.NoInlining)] - private static unsafe void WriteNumericMultiWrite(this WritableBuffer buffer, ulong number) + private static void WriteNumericMultiWrite(ref WritableBufferWriter buffer, ulong number) { const byte AsciiDigitStart = (byte)'0'; @@ -280,11 +191,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http while (value != 0); var length = _maxULongByteLength - position; - buffer.WriteFast(new ArraySegment(byteBuffer, position, length)); + buffer.Write(byteBuffer, position, length); } [MethodImpl(MethodImplOptions.NoInlining)] - private unsafe static void WriteAsciiMultiWrite(this WritableBuffer buffer, string data) + private unsafe static void WriteAsciiMultiWrite(ref WritableBufferWriter buffer, string data) { var remaining = data.Length; @@ -294,16 +205,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http while (remaining > 0) { - var writable = Math.Min(remaining, buffer.Buffer.Length); - - buffer.Ensure(writable); + var writable = Math.Min(remaining, buffer.Span.Length); if (writable == 0) { + buffer.Ensure(); continue; } - fixed (byte* output = &buffer.Buffer.Span.DangerousGetPinnableReference()) + fixed (byte* output = &buffer.Span.DangerousGetPinnableReference()) { EncodeAsciiCharsToBytes(inputSlice, output, writable); } diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/PipelineExtensionTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/PipelineExtensionTests.cs index ec85ef3b5e..a580b92e06 100644 --- a/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/PipelineExtensionTests.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Core.Tests/PipelineExtensionTests.cs @@ -33,13 +33,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests [InlineData(ulong.MinValue)] [InlineData(ulong.MaxValue)] [InlineData(4_8_15_16_23_42)] - public async Task WritesNumericToAscii(ulong number) + public void WritesNumericToAscii(ulong number) { - var writer = _pipe.Writer.Alloc(); - writer.WriteNumeric(number); - await writer.FlushAsync(); + var writerBuffer = _pipe.Writer.Alloc(); + var writer = new WritableBufferWriter(writerBuffer); + PipelineExtensions.WriteNumeric(ref writer, number); + writerBuffer.FlushAsync().GetAwaiter().GetResult(); - var reader = await _pipe.Reader.ReadAsync(); + var reader = _pipe.Reader.ReadAsync().GetAwaiter().GetResult(); var numAsStr = number.ToString(); var expected = Encoding.ASCII.GetBytes(numAsStr); AssertExtensions.Equal(expected, reader.Buffer.Slice(0, numAsStr.Length).ToArray()); @@ -51,16 +52,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests [InlineData(_ulongMaxValueLength - 1)] public void WritesNumericAcrossSpanBoundaries(int gapSize) { - var writer = _pipe.Writer.Alloc(100); + var writerBuffer = _pipe.Writer.Alloc(100); + var writer = new WritableBufferWriter(writerBuffer); // almost fill up the first block - var spacer = new Span(new byte[writer.Buffer.Length - gapSize]); + var spacer = new byte[writer.Span.Length - gapSize]; writer.Write(spacer); - var bufferLength = writer.Buffer.Length; - writer.WriteNumeric(ulong.MaxValue); - Assert.NotEqual(bufferLength, writer.Buffer.Length); + var bufferLength = writer.Span.Length; + PipelineExtensions.WriteNumeric(ref writer, ulong.MaxValue); + Assert.NotEqual(bufferLength, writer.Span.Length); - writer.FlushAsync().GetAwaiter().GetResult(); + writerBuffer.FlushAsync().GetAwaiter().GetResult(); var reader = _pipe.Reader.ReadAsync().GetAwaiter().GetResult(); var numAsString = ulong.MaxValue.ToString(); @@ -79,12 +81,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests // null or empty [InlineData("", new byte[0])] [InlineData(null, new byte[0])] - public async Task EncodesAsAscii(string input, byte[] expected) + public void EncodesAsAscii(string input, byte[] expected) { - var writer = _pipe.Writer.Alloc(); - writer.WriteAsciiNoValidation(input); - await writer.FlushAsync(); - var reader = await _pipe.Reader.ReadAsync(); + var writerBuffer = _pipe.Writer.Alloc(); + var writer = new WritableBufferWriter(writerBuffer); + PipelineExtensions.WriteAsciiNoValidation(ref writer, input); + writerBuffer.FlushAsync().GetAwaiter().GetResult(); + var reader = _pipe.Reader.ReadAsync().GetAwaiter().GetResult(); if (expected.Length > 0) { @@ -103,30 +106,32 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests [InlineData("𤭢𐐝")] // non-ascii characters stored in 16 bits [InlineData("ñ٢⛄⛵")] - public async Task WriteAsciiNoValidationWritesOnlyOneBytePerChar(string input) + public void WriteAsciiNoValidationWritesOnlyOneBytePerChar(string input) { // WriteAscii doesn't validate if characters are in the ASCII range // but it shouldn't produce more than one byte per character - var writer = _pipe.Writer.Alloc(); - writer.WriteAsciiNoValidation(input); - await writer.FlushAsync(); - var reader = await _pipe.Reader.ReadAsync(); + var writerBuffer = _pipe.Writer.Alloc(); + var writer = new WritableBufferWriter(writerBuffer); + PipelineExtensions.WriteAsciiNoValidation(ref writer, input); + writerBuffer.FlushAsync().GetAwaiter().GetResult(); + var reader = _pipe.Reader.ReadAsync().GetAwaiter().GetResult(); Assert.Equal(input.Length, reader.Buffer.Length); } [Fact] - public async Task WriteAsciiNoValidation() + public void WriteAsciiNoValidation() { const byte maxAscii = 0x7f; - var writer = _pipe.Writer.Alloc(); + var writerBuffer = _pipe.Writer.Alloc(); + var writer = new WritableBufferWriter(writerBuffer); for (var i = 0; i < maxAscii; i++) { - writer.WriteAsciiNoValidation(new string((char)i, 1)); + PipelineExtensions.WriteAsciiNoValidation(ref writer, new string((char)i, 1)); } - await writer.FlushAsync(); + writerBuffer.FlushAsync().GetAwaiter().GetResult(); - var reader = await _pipe.Reader.ReadAsync(); + var reader = _pipe.Reader.ReadAsync().GetAwaiter().GetResult(); var data = reader.Buffer.Slice(0, maxAscii).ToArray(); for (var i = 0; i < maxAscii; i++) { @@ -147,17 +152,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests public void WritesAsciiAcrossBlockBoundaries(int stringLength, int gapSize) { var testString = new string(' ', stringLength); - var writer = _pipe.Writer.Alloc(100); + var writerBuffer = _pipe.Writer.Alloc(100); + var writer = new WritableBufferWriter(writerBuffer); // almost fill up the first block - var spacer = new Span(new byte[writer.Buffer.Length - gapSize]); + var spacer = new byte[writer.Span.Length - gapSize]; writer.Write(spacer); - Assert.Equal(gapSize, writer.Buffer.Span.Length); + Assert.Equal(gapSize, writer.Span.Length); - var bufferLength = writer.Buffer.Length; - writer.WriteAsciiNoValidation(testString); - Assert.NotEqual(bufferLength, writer.Buffer.Length); + var bufferLength = writer.Span.Length; + PipelineExtensions.WriteAsciiNoValidation(ref writer, testString); + Assert.NotEqual(bufferLength, writer.Span.Length); - writer.FlushAsync().GetAwaiter().GetResult(); + writerBuffer.FlushAsync().GetAwaiter().GetResult(); var reader = _pipe.Reader.ReadAsync().GetAwaiter().GetResult(); var written = reader.Buffer.Slice(spacer.Length, stringLength); diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.Performance/RequestParsingBenchmark.cs b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/RequestParsingBenchmark.cs index 78844fe659..d759bccc02 100644 --- a/test/Microsoft.AspNetCore.Server.Kestrel.Performance/RequestParsingBenchmark.cs +++ b/test/Microsoft.AspNetCore.Server.Kestrel.Performance/RequestParsingBenchmark.cs @@ -120,7 +120,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance private void InsertData(byte[] bytes) { var buffer = Pipe.Writer.Alloc(2048); - buffer.WriteFast(bytes); + buffer.Write(bytes); // There should not be any backpressure and task completes immediately buffer.FlushAsync().GetAwaiter().GetResult(); } diff --git a/tools/CodeGenerator/KnownHeaders.cs b/tools/CodeGenerator/KnownHeaders.cs index 19f7ef5796..b0cdf0b530 100644 --- a/tools/CodeGenerator/KnownHeaders.cs +++ b/tools/CodeGenerator/KnownHeaders.cs @@ -522,7 +522,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http return true; }} {(loop.ClassName == "FrameResponseHeaders" ? $@" - protected void CopyToFast(ref WritableBuffer output) + protected void CopyToFast(ref WritableBufferWriter output) {{ var tempBits = _bits | (_contentLength.HasValue ? {1L << 63}L : 0); {Each(loop.Headers.Where(header => header.Identifier != "ContentLength").OrderBy(h => !h.PrimaryHeader), header => $@" @@ -530,7 +530,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http {{ {(header.EnhancedSetter == false ? "" : $@" if (_headers._raw{header.Identifier} != null) {{ - output.WriteFast(_headers._raw{header.Identifier}); + output.Write(_headers._raw{header.Identifier}); }} else ")} {{ @@ -540,8 +540,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http var value = _headers._{header.Identifier}[i]; if (value != null) {{ - output.WriteFast(_headerBytes, {header.BytesOffset}, {header.BytesCount}); - output.WriteAsciiNoValidation(value); + output.Write(_headerBytes, {header.BytesOffset}, {header.BytesCount}); + PipelineExtensions.WriteAsciiNoValidation(ref output, value); }} }} }} @@ -554,8 +554,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http }}{(header.Identifier == "Server" ? $@" if ((tempBits & {1L << 63}L) != 0) {{ - output.WriteFast(_headerBytes, {loop.Headers.First(x => x.Identifier == "ContentLength").BytesOffset}, {loop.Headers.First(x => x.Identifier == "ContentLength").BytesCount}); - output.WriteNumeric((ulong)ContentLength.Value); + output.Write(_headerBytes, {loop.Headers.First(x => x.Identifier == "ContentLength").BytesOffset}, {loop.Headers.First(x => x.Identifier == "ContentLength").BytesCount}); + PipelineExtensions.WriteNumeric(ref output, (ulong)ContentLength.Value); if((tempBits & ~{1L << 63}L) == 0) {{