From 2572256d3f13bb06f099899e54a9863d50e4eda9 Mon Sep 17 00:00:00 2001 From: Stephen Halter Date: Fri, 20 Nov 2015 01:56:46 -0800 Subject: [PATCH 1/5] Use MemoryPool2 for SocketOutput Buffers Add ProducingStart and ProducingComplete methods to ISocketOutput. These new methods can help prevent double buffering when encoding. --- .../Filter/FilteredStreamAdapter.cs | 2 +- .../Filter/StreamSocketOutput.cs | 27 ++- .../Http/Connection.cs | 4 +- .../Http/ISocketOutput.cs | 18 ++ .../Http/SocketInput.cs | 4 +- .../Http/SocketOutput.cs | 217 ++++++++++++++---- .../Infrastructure/MemoryPool2.cs | 2 +- .../Infrastructure/MemoryPoolBlock2.cs | 15 +- .../Infrastructure/MemoryPoolIterator2.cs | 42 ++++ .../Networking/UvWriteReq.cs | 19 +- .../MemoryPoolBlock2Tests.cs | 35 ++- .../MultipleLoopTests.cs | 12 +- .../NetworkingTests.cs | 18 +- .../SocketOutputTests.cs | 63 ++++- .../TestHelpers/MockLibuv.cs | 6 +- 15 files changed, 408 insertions(+), 76 deletions(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Filter/FilteredStreamAdapter.cs b/src/Microsoft.AspNet.Server.Kestrel/Filter/FilteredStreamAdapter.cs index 2e0872f704..e3d2eb5603 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Filter/FilteredStreamAdapter.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Filter/FilteredStreamAdapter.cs @@ -22,7 +22,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Filter IKestrelTrace logger) { SocketInput = new SocketInput(memory); - SocketOutput = new StreamSocketOutput(filteredStream); + SocketOutput = new StreamSocketOutput(filteredStream, memory); _log = logger; _filteredStream = filteredStream; diff --git a/src/Microsoft.AspNet.Server.Kestrel/Filter/StreamSocketOutput.cs b/src/Microsoft.AspNet.Server.Kestrel/Filter/StreamSocketOutput.cs index fe9499265f..ba7c57dcf4 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Filter/StreamSocketOutput.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Filter/StreamSocketOutput.cs @@ -13,10 +13,13 @@ namespace Microsoft.AspNet.Server.Kestrel.Filter public class StreamSocketOutput : ISocketOutput { private readonly Stream _outputStream; + private readonly MemoryPool2 _memory; + private MemoryPoolBlock2 _producingBlock; - public StreamSocketOutput(Stream outputStream) + public StreamSocketOutput(Stream outputStream, MemoryPool2 memory) { _outputStream = outputStream; + _memory = memory; } void ISocketOutput.Write(ArraySegment buffer, bool immediate) @@ -30,5 +33,27 @@ namespace Microsoft.AspNet.Server.Kestrel.Filter _outputStream.Write(buffer.Array, buffer.Offset, buffer.Count); return TaskUtilities.CompletedTask; } + + public MemoryPoolIterator2 ProducingStart() + { + _producingBlock = _memory.Lease(); + return new MemoryPoolIterator2(_producingBlock); + } + + public void ProducingComplete(MemoryPoolIterator2 end, int count) + { + var block = _producingBlock; + while (block != end.Block) + { + _outputStream.Write(block.Data.Array, block.Data.Offset, block.Data.Count); + + var returnBlock = block; + block = block.Next; + returnBlock.Pool?.Return(returnBlock); + } + + _outputStream.Write(end.Block.Array, end.Block.Data.Offset, end.Index); + end.Block.Pool?.Return(end.Block); + } } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs index 4af0676761..03c2a619b9 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs @@ -42,7 +42,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http _connectionId = Interlocked.Increment(ref _lastConnectionId); _rawSocketInput = new SocketInput(Memory2); - _rawSocketOutput = new SocketOutput(Thread, _socket, this, _connectionId, Log); + _rawSocketOutput = new SocketOutput(Thread, _socket, Memory2, this, _connectionId, Log); } public void Start() @@ -116,7 +116,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http // called from a libuv thread. ThreadPool.QueueUserWorkItem(state => { - var connection = (Connection)this; + var connection = (Connection)state; connection._frame.Abort(); }, this); } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/ISocketOutput.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/ISocketOutput.cs index 4edffa3055..1ec17d269b 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/ISocketOutput.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/ISocketOutput.cs @@ -4,6 +4,7 @@ using System; using System.Threading; using System.Threading.Tasks; +using Microsoft.AspNet.Server.Kestrel.Infrastructure; namespace Microsoft.AspNet.Server.Kestrel.Http { @@ -14,5 +15,22 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { void Write(ArraySegment buffer, bool immediate = true); Task WriteAsync(ArraySegment buffer, bool immediate = true, CancellationToken cancellationToken = default(CancellationToken)); + + /// + /// Returns an iterator pointing to the tail of the response buffer. Response data can be appended + /// manually or by using . + /// Be careful to ensure all appended blocks are backed by a . + /// + MemoryPoolIterator2 ProducingStart(); + + /// + /// Commits the response data appended to the iterator returned from . + /// All the data up to will be included in the response. + /// A write operation isn't guaranteed to be scheduled unless + /// or is called afterwards. + /// + /// Points to the end of the committed data. + /// The number of bytes added to the response. + void ProducingComplete(MemoryPoolIterator2 end, int count); } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/SocketInput.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/SocketInput.cs index ae74bffb24..b13d678130 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/SocketInput.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/SocketInput.cs @@ -64,7 +64,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { _pinned = _tail; var data = new ArraySegment(_pinned.Data.Array, _pinned.End, _pinned.Data.Offset + _pinned.Data.Count - _pinned.End); - var dataPtr = _pinned.Pin(); + var dataPtr = _pinned.Pin() + _pinned.End; return new IncomingBuffer { Data = data, @@ -77,7 +77,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return new IncomingBuffer { Data = _pinned.Data, - DataPtr = _pinned.Pin() + DataPtr = _pinned.Pin() + _pinned.End }; } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs index d1e353388f..be47d8e720 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs @@ -23,8 +23,18 @@ namespace Microsoft.AspNet.Server.Kestrel.Http private readonly long _connectionId; private readonly IKestrelTrace _log; + // This locks all access to _tail, _isProducing and _returnFromOnProducingComplete. + // _head does not require a lock, since it is only used in the ctor and uv thread. + private readonly object _returnLock = new object(); + + private MemoryPoolBlock2 _head; + private MemoryPoolBlock2 _tail; + + private bool _isProducing; + private MemoryPoolBlock2 _returnFromOnProducingComplete; + // This locks access to to all of the below fields - private readonly object _lockObj = new object(); + private readonly object _contextLock = new object(); // The number of write operations that have been scheduled so far // but have not completed. @@ -38,6 +48,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http public SocketOutput( KestrelThread thread, UvStreamHandle socket, + MemoryPool2 memory, Connection connection, long connectionId, IKestrelTrace log) @@ -48,6 +59,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http _connectionId = connectionId; _log = log; _tasksPending = new Queue>(); + + _head = memory.Lease(); + _tail = _head; } public Task WriteAsync( @@ -56,28 +70,20 @@ namespace Microsoft.AspNet.Server.Kestrel.Http bool socketShutdownSend = false, bool socketDisconnect = false) { - //TODO: need buffering that works - if (buffer.Array != null) - { - var copy = new byte[buffer.Count]; - Array.Copy(buffer.Array, buffer.Offset, copy, 0, buffer.Count); - buffer = new ArraySegment(copy); - _log.ConnectionWrite(_connectionId, buffer.Count); - } + var tail = ProducingStart(); + tail = tail.CopyFrom(buffer); + // We do our own accounting below + ProducingComplete(tail, count: 0); TaskCompletionSource tcs = null; - lock (_lockObj) + lock (_contextLock) { if (_nextWriteContext == null) { _nextWriteContext = new WriteContext(this); } - if (buffer.Array != null) - { - _nextWriteContext.Buffers.Enqueue(buffer); - } if (socketShutdownSend) { _nextWriteContext.SocketShutdownSend = true; @@ -138,6 +144,58 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } + public MemoryPoolIterator2 ProducingStart() + { + lock (_returnLock) + { + Debug.Assert(!_isProducing); + _isProducing = true; + + if (_tail == null) + { + throw new IOException("The socket has been closed."); + } + + return new MemoryPoolIterator2(_tail, _tail.End); + } + } + + public void ProducingComplete(MemoryPoolIterator2 end, int count) + { + lock (_returnLock) + { + Debug.Assert(_isProducing); + _isProducing = false; + + if (_returnFromOnProducingComplete == null) + { + _tail = end.Block; + _tail.End = end.Index; + + if (count != 0) + { + lock (_contextLock) + { + _numBytesPreCompleted += count; + } + } + } + else + { + var block = _returnFromOnProducingComplete; + while (block != null) + { + var returnBlock = block; + block = block.Next; + + returnBlock.Pool?.Return(returnBlock); + } + + _returnFromOnProducingComplete = null; + } + } + } + private void ScheduleWrite() { _thread.Post(_this => _this.WriteAllPending(), this); @@ -148,7 +206,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { WriteContext writingContext; - lock (_lockObj) + lock (_contextLock) { if (_nextWriteContext != null) { @@ -168,7 +226,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } catch { - lock (_lockObj) + lock (_contextLock) { // Lock instead of using Interlocked.Decrement so _writesSending // doesn't change in the middle of executing other synchronized code. @@ -180,7 +238,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } // This is called on the libuv event loop - private void OnWriteCompleted(Queue> writtenBuffers, int status, Exception error) + private void OnWriteCompleted(int bytesWritten, int status, Exception error) { _log.ConnectionWriteCallback(_connectionId, status); @@ -192,7 +250,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http _connection.Abort(); } - lock (_lockObj) + lock (_contextLock) { if (_nextWriteContext != null) { @@ -203,13 +261,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http _writesPending--; } - foreach (var writeBuffer in writtenBuffers) - { - // _numBytesPreCompleted can temporarily go negative in the event there are - // completed writes that we haven't triggered callbacks for yet. - _numBytesPreCompleted -= writeBuffer.Count; - } - + // _numBytesPreCompleted can temporarily go negative in the event there are + // completed writes that we haven't triggered callbacks for yet. + _numBytesPreCompleted -= bytesWritten; + // bytesLeftToBuffer can be greater than _maxBytesPreCompleted // This allows large writes to complete once they've actually finished. var bytesLeftToBuffer = _maxBytesPreCompleted - _numBytesPreCompleted; @@ -225,20 +280,48 @@ namespace Microsoft.AspNet.Server.Kestrel.Http if (_lastWriteError == null) { ThreadPool.QueueUserWorkItem( - (o) => ((TaskCompletionSource)o).SetResult(null), + (o) => ((TaskCompletionSource)o).SetResult(null), tcs); } else { // error is closure captured ThreadPool.QueueUserWorkItem( - (o) => ((TaskCompletionSource)o).SetException(_lastWriteError), + (o) => ((TaskCompletionSource)o).SetException(_lastWriteError), tcs); } } + } + } - // Now that the while loop has completed the following invariants should hold true: - Debug.Assert(_numBytesPreCompleted >= 0); + // This is called on the libuv event loop + private void ReturnAllBlocks() + { + lock (_returnLock) + { + var block = _head; + while (block != _tail) + { + var returnBlock = block; + block = block.Next; + + returnBlock.Unpin(); + returnBlock.Pool?.Return(returnBlock); + } + + _tail.Unpin(); + + if (_isProducing) + { + _returnFromOnProducingComplete = _tail; + } + else + { + _tail.Pool?.Return(_tail); + } + + _head = null; + _tail = null; } } @@ -263,9 +346,13 @@ namespace Microsoft.AspNet.Server.Kestrel.Http private class WriteContext { + private MemoryPoolIterator2 _lockedStart; + private MemoryPoolIterator2 _lockedEnd; + private int _bufferCount; + private int _byteCount; + public SocketOutput Self; - public Queue> Buffers; public bool SocketShutdownSend; public bool SocketDisconnect; @@ -277,7 +364,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Http public WriteContext(SocketOutput self) { Self = self; - Buffers = new Queue>(); } /// @@ -285,30 +371,28 @@ namespace Microsoft.AspNet.Server.Kestrel.Http /// public void DoWriteIfNeeded() { - if (Buffers.Count == 0 || Self._socket.IsClosed) + LockWrite(); + + if (_byteCount == 0 || Self._socket.IsClosed) { DoShutdownIfNeeded(); return; } - var buffers = new ArraySegment[Buffers.Count]; - - var i = 0; - foreach (var buffer in Buffers) - { - buffers[i++] = buffer; - } - var writeReq = new UvWriteReq(Self._log); writeReq.Init(Self._thread.Loop); - writeReq.Write(Self._socket, new ArraySegment>(buffers), (_writeReq, status, error, state) => + writeReq.Write(Self._socket, _lockedStart, _lockedEnd, _bufferCount, (_writeReq, status, error, state) => { _writeReq.Dispose(); var _this = (WriteContext)state; + _this.ReturnFullyWrittenBlocks(); _this.WriteStatus = status; _this.WriteError = error; _this.DoShutdownIfNeeded(); }, this); + + Self._head = _lockedEnd.Block; + Self._head.Start = _lockedEnd.Index; } /// @@ -348,13 +432,62 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } Self._socket.Dispose(); + Self.ReturnAllBlocks(); Self._log.ConnectionStop(Self._connectionId); Complete(); } public void Complete() { - Self.OnWriteCompleted(Buffers, WriteStatus, WriteError); + Self.OnWriteCompleted(_byteCount, WriteStatus, WriteError); + } + + private void ReturnFullyWrittenBlocks() + { + var block = _lockedStart.Block; + while (block != _lockedEnd.Block) + { + var returnBlock = block; + block = block.Next; + + returnBlock.Unpin(); + returnBlock.Pool?.Return(returnBlock); + } + } + + private void LockWrite() + { + var head = Self._head; + var tail = Self._tail; + + if (head == null || tail == null) + { + // ReturnAllBlocks has already bee called. Nothing to do here. + // Write will no-op since _byteCount will remain 0. + return; + } + + _lockedStart = new MemoryPoolIterator2(head, head.Start); + _lockedEnd = new MemoryPoolIterator2(tail, tail.End); + + if (_lockedStart.Block == _lockedEnd.Block) + { + _byteCount = _lockedEnd.Index - _lockedStart.Index; + _bufferCount = 1; + return; + } + + _byteCount = _lockedStart.Block.Data.Offset + _lockedStart.Block.Data.Count - _lockedStart.Index; + _bufferCount = 1; + + for (var block = _lockedStart.Block.Next; block != _lockedEnd.Block; block = block.Next) + { + _byteCount += block.Data.Count; + _bufferCount++; + } + + _byteCount += _lockedEnd.Index - _lockedEnd.Block.Data.Offset; + _bufferCount++; } } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPool2.cs b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPool2.cs index 37d64ddcaf..446f03c2b3 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPool2.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPool2.cs @@ -59,7 +59,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Infrastructure /// The block returned must be at least this size. It may be larger than this minimum size, and if so, /// the caller may write to the block's entire size rather than being limited to the minumumSize requested. /// The block that is reserved for the called. It must be passed to Return when it is no longer being used. - public MemoryPoolBlock2 Lease(int minimumSize) + public MemoryPoolBlock2 Lease(int minimumSize = _blockLength) { if (minimumSize > _blockLength) { diff --git a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolBlock2.cs b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolBlock2.cs index a93e03186d..2bcd21d9a2 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolBlock2.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolBlock2.cs @@ -98,24 +98,27 @@ namespace Microsoft.AspNet.Server.Kestrel.Infrastructure } /// - /// Called to ensure that a block is pinned, and return the pointer to native memory just after - /// the range of "active" bytes. This is where arriving data is read into. + /// Called to ensure that a block is pinned, and return the pointer to the native address + /// of the first byte of this block's Data memory. Arriving data is read into Pin() + End. + /// Outgoing data is read from Pin() + Start. /// /// public IntPtr Pin() { - Debug.Assert(!_pinHandle.IsAllocated); - if (_dataArrayPtr != IntPtr.Zero) { // this is a slab managed block - use the native address of the slab which is always locked - return _dataArrayPtr + End; + return _dataArrayPtr; + } + else if (_pinHandle.IsAllocated) + { + return _pinHandle.AddrOfPinnedObject(); } else { // this is one-time-use memory - lock the managed memory until Unpin is called _pinHandle = GCHandle.Alloc(Data.Array, GCHandleType.Pinned); - return _pinHandle.AddrOfPinnedObject() + End; + return _pinHandle.AddrOfPinnedObject(); } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolIterator2.cs b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolIterator2.cs index 7bf514707e..6c5c1f205a 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolIterator2.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolIterator2.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Diagnostics; using System.Linq; using System.Numerics; @@ -530,5 +531,46 @@ namespace Microsoft.AspNet.Server.Kestrel.Infrastructure } } } + + public MemoryPoolIterator2 CopyFrom(ArraySegment buffer) + { + Debug.Assert(_block != null); + Debug.Assert(_block.Pool != null); + Debug.Assert(_block.Next == null); + Debug.Assert(_block.End == _index); + + var pool = _block.Pool; + var block = _block; + var blockIndex = _index; + + var bufferIndex = buffer.Offset; + var remaining = buffer.Count; + + while (remaining > 0) + { + var bytesLeftInBlock = block.Data.Offset + block.Data.Count - blockIndex; + + if (bytesLeftInBlock == 0) + { + var nextBlock = pool.Lease(); + block.Next = nextBlock; + block = nextBlock; + + blockIndex = block.Data.Offset; + bytesLeftInBlock = block.Data.Count; + } + + var bytesToCopy = Math.Min(remaining, bytesLeftInBlock); + + Buffer.BlockCopy(buffer.Array, bufferIndex, block.Array, blockIndex, bytesToCopy); + + blockIndex += bytesToCopy; + bufferIndex += bytesToCopy; + remaining -= bytesToCopy; + block.End = blockIndex; + } + + return new MemoryPoolIterator2(block, blockIndex); + } } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Networking/UvWriteReq.cs b/src/Microsoft.AspNet.Server.Kestrel/Networking/UvWriteReq.cs index 103ddfc6ee..7330ef3c15 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Networking/UvWriteReq.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Networking/UvWriteReq.cs @@ -41,7 +41,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking public unsafe void Write( UvStreamHandle handle, - ArraySegment> bufs, + MemoryPoolIterator2 start, + MemoryPoolIterator2 end, + int nBuffers, Action callback, object state) { @@ -51,7 +53,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking _pins.Add(GCHandle.Alloc(this, GCHandleType.Normal)); var pBuffers = (Libuv.uv_buf_t*)_bufs; - var nBuffers = bufs.Count; if (nBuffers > BUFFER_COUNT) { // create and pin buffer array when it's larger than the pre-allocated one @@ -61,16 +62,18 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking pBuffers = (Libuv.uv_buf_t*)gcHandle.AddrOfPinnedObject(); } + var block = start.Block; for (var index = 0; index < nBuffers; index++) { - // create and pin each segment being written - var buf = bufs.Array[bufs.Offset + index]; + var blockStart = block == start.Block ? start.Index : block.Data.Offset; + var blockEnd = block == end.Block ? end.Index : block.Data.Offset + block.Data.Count; - var gcHandle = GCHandle.Alloc(buf.Array, GCHandleType.Pinned); - _pins.Add(gcHandle); + // create and pin each segment being written pBuffers[index] = Libuv.buf_init( - gcHandle.AddrOfPinnedObject() + buf.Offset, - buf.Count); + block.Pin() + blockStart, + blockEnd - blockStart); + + block = block.Next; } _callback = callback; diff --git a/test/Microsoft.AspNet.Server.KestrelTests/MemoryPoolBlock2Tests.cs b/test/Microsoft.AspNet.Server.KestrelTests/MemoryPoolBlock2Tests.cs index db3b03802f..5f445ef487 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/MemoryPoolBlock2Tests.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/MemoryPoolBlock2Tests.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; using Microsoft.AspNet.Server.Kestrel.Infrastructure; using Xunit; @@ -152,6 +153,38 @@ namespace Microsoft.AspNet.Server.KestrelTests } } + [Fact] + public void CopyFromCorrectlyTraversesBlocks() + { + using (var pool = new MemoryPool2()) + { + var block1 = pool.Lease(128); + var iterator = block1.GetIterator(); + var bufferSize = block1.Data.Count * 3; + var buffer = new byte[bufferSize]; + + for (int i = 0; i < bufferSize; i++) + { + buffer[i] = (byte)(i % 73); + } + + Assert.Null(block1.Next); + + var end = iterator.CopyFrom(new ArraySegment(buffer)); + + Assert.NotNull(block1.Next); + + for (int i = 0; i < bufferSize; i++) + { + Assert.Equal(i % 73, iterator.Take()); + } + + Assert.Equal(-1, iterator.Take()); + Assert.Equal(iterator.Block, end.Block); + Assert.Equal(iterator.Index, end.Index); + } + } + [Fact] public void IsEndCorrectlyTraversesBlocks() { diff --git a/test/Microsoft.AspNet.Server.KestrelTests/MultipleLoopTests.cs b/test/Microsoft.AspNet.Server.KestrelTests/MultipleLoopTests.cs index f57c899f6a..44a5757f80 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/MultipleLoopTests.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/MultipleLoopTests.cs @@ -62,14 +62,24 @@ namespace Microsoft.AspNet.Server.KestrelTests var writeRequest = new UvWriteReq(new KestrelTrace(new TestKestrelTrace())); writeRequest.Init(loop); + var block = MemoryPoolBlock2.Create( + new ArraySegment(new byte[] { 1, 2, 3, 4 }), + dataPtr: IntPtr.Zero, + pool: null, + slab: null); + var start = new MemoryPoolIterator2(block, 0); + var end = new MemoryPoolIterator2(block, block.Data.Count); writeRequest.Write( serverConnectionPipe, - new ArraySegment>(new ArraySegment[] { new ArraySegment(new byte[] { 1, 2, 3, 4 }) }), + start, + end, + 1, (_3, status2, error2, _4) => { writeRequest.Dispose(); serverConnectionPipe.Dispose(); serverListenPipe.Dispose(); + block.Unpin(); }, null); diff --git a/test/Microsoft.AspNet.Server.KestrelTests/NetworkingTests.cs b/test/Microsoft.AspNet.Server.KestrelTests/NetworkingTests.cs index fa8a3ada30..edaa50870e 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/NetworkingTests.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/NetworkingTests.cs @@ -201,12 +201,22 @@ namespace Microsoft.AspNet.Server.KestrelTests { var req = new UvWriteReq(new KestrelTrace(new TestKestrelTrace())); req.Init(loop); + var block = MemoryPoolBlock2.Create( + new ArraySegment(new byte[] { 65, 66, 67, 68, 69 }), + dataPtr: IntPtr.Zero, + pool: null, + slab: null); + var start = new MemoryPoolIterator2(block, 0); + var end = new MemoryPoolIterator2(block, block.Data.Count); req.Write( tcp2, - new ArraySegment>( - new[] { new ArraySegment(new byte[] { 65, 66, 67, 68, 69 }) } - ), - (_1, _2, _3, _4) => { }, + start, + end, + 1, + (_1, _2, _3, _4) => + { + block.Unpin(); + }, null); } } diff --git a/test/Microsoft.AspNet.Server.KestrelTests/SocketOutputTests.cs b/test/Microsoft.AspNet.Server.KestrelTests/SocketOutputTests.cs index 69224073dc..629c35407c 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/SocketOutputTests.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/SocketOutputTests.cs @@ -34,13 +34,14 @@ namespace Microsoft.AspNet.Server.KestrelTests }; using (var kestrelEngine = new KestrelEngine(mockLibuv, new TestServiceContext())) + using (var memory = new MemoryPool2()) { kestrelEngine.Start(count: 1); var kestrelThread = kestrelEngine.Threads[0]; var socket = new MockSocket(kestrelThread.Loop.ThreadId, new TestKestrelTrace()); var trace = new KestrelTrace(new TestKestrelTrace()); - var socketOutput = new SocketOutput(kestrelThread, socket, null, 0, trace); + var socketOutput = new SocketOutput(kestrelThread, socket, memory, null, 0, trace); // I doubt _maxBytesPreCompleted will ever be over a MB. If it is, we should change this test. var bufferSize = 1048576; @@ -79,13 +80,14 @@ namespace Microsoft.AspNet.Server.KestrelTests }; using (var kestrelEngine = new KestrelEngine(mockLibuv, new TestServiceContext())) + using (var memory = new MemoryPool2()) { kestrelEngine.Start(count: 1); var kestrelThread = kestrelEngine.Threads[0]; var socket = new MockSocket(kestrelThread.Loop.ThreadId, new TestKestrelTrace()); var trace = new KestrelTrace(new TestKestrelTrace()); - var socketOutput = new SocketOutput(kestrelThread, socket, null, 0, trace); + var socketOutput = new SocketOutput(kestrelThread, socket, memory, null, 0, trace); var bufferSize = maxBytesPreCompleted; var buffer = new ArraySegment(new byte[bufferSize], 0, bufferSize); @@ -134,13 +136,14 @@ namespace Microsoft.AspNet.Server.KestrelTests }; using (var kestrelEngine = new KestrelEngine(mockLibuv, new TestServiceContext())) + using (var memory = new MemoryPool2()) { kestrelEngine.Start(count: 1); var kestrelThread = kestrelEngine.Threads[0]; var socket = new MockSocket(kestrelThread.Loop.ThreadId, new TestKestrelTrace()); var trace = new KestrelTrace(new TestKestrelTrace()); - var socketOutput = new SocketOutput(kestrelThread, socket, null, 0, trace); + var socketOutput = new SocketOutput(kestrelThread, socket, memory, null, 0, trace); var bufferSize = maxBytesPreCompleted; @@ -213,13 +216,14 @@ namespace Microsoft.AspNet.Server.KestrelTests }; using (var kestrelEngine = new KestrelEngine(mockLibuv, new TestServiceContext())) + using (var memory = new MemoryPool2()) { kestrelEngine.Start(count: 1); var kestrelThread = kestrelEngine.Threads[0]; var socket = new MockSocket(kestrelThread.Loop.ThreadId, new TestKestrelTrace()); var trace = new KestrelTrace(new TestKestrelTrace()); - var socketOutput = new SocketOutput(kestrelThread, socket, null, 0, trace); + var socketOutput = new SocketOutput(kestrelThread, socket, memory, null, 0, trace); var bufferSize = maxBytesPreCompleted; var buffer = new ArraySegment(new byte[bufferSize], 0, bufferSize); @@ -270,6 +274,57 @@ namespace Microsoft.AspNet.Server.KestrelTests } } + [Fact] + public void ProducingStartAndProducingCompleteCanBeUsedDirectly() + { + int nBuffers = 0; + var nBufferWh = new ManualResetEventSlim(); + + var mockLibuv = new MockLibuv + { + OnWrite = (socket, buffers, triggerCompleted) => + { + nBuffers = buffers; + nBufferWh.Set(); + triggerCompleted(0); + return 0; + } + }; + + using (var kestrelEngine = new KestrelEngine(mockLibuv, new TestServiceContext())) + using (var memory = new MemoryPool2()) + { + kestrelEngine.Start(count: 1); + + var kestrelThread = kestrelEngine.Threads[0]; + var socket = new MockSocket(kestrelThread.Loop.ThreadId, new TestKestrelTrace()); + var trace = new KestrelTrace(new TestKestrelTrace()); + var socketOutput = new SocketOutput(kestrelThread, socket, memory, null, 0, trace); + + // block 1 + var start = socketOutput.ProducingStart(); + start.Block.End = start.Block.Data.Offset + start.Block.Data.Count; + var totalBytes = start.Block.Data.Count; + + // block 2 + var block2 = memory.Lease(); + block2.End = block2.Data.Offset + block2.Data.Count; + start.Block.Next = block2; + totalBytes += block2.Data.Count; + + var end = new MemoryPoolIterator2(block2, block2.End); + + socketOutput.ProducingComplete(end, totalBytes); + + // A call to Write is required to ensure a write is scheduled + socketOutput.WriteAsync(default(ArraySegment)); + + Assert.True(nBufferWh.Wait(1000)); + Assert.Equal(2, nBuffers); + } + } + + private class MockSocket : UvStreamHandle { public MockSocket(int threadId, IKestrelTrace logger) : base(logger) diff --git a/test/Microsoft.AspNet.Server.KestrelTests/TestHelpers/MockLibuv.cs b/test/Microsoft.AspNet.Server.KestrelTests/TestHelpers/MockLibuv.cs index 1134ae8fab..9a05659186 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/TestHelpers/MockLibuv.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/TestHelpers/MockLibuv.cs @@ -12,7 +12,7 @@ namespace Microsoft.AspNet.Server.KestrelTests.TestHelpers private bool _stopLoop; private readonly ManualResetEventSlim _loopWh = new ManualResetEventSlim(); - private Func>, Action, int> _onWrite; + private Func, int> _onWrite; unsafe public MockLibuv() { @@ -68,7 +68,7 @@ namespace Microsoft.AspNet.Server.KestrelTests.TestHelpers _uv_walk = (loop, callback, ignore) => 0; } - public Func>, Action, int> OnWrite + public Func, int> OnWrite { get { @@ -82,7 +82,7 @@ namespace Microsoft.AspNet.Server.KestrelTests.TestHelpers unsafe private int UvWrite(UvRequest req, UvStreamHandle handle, uv_buf_t* bufs, int nbufs, uv_write_cb cb) { - return _onWrite(handle, new ArraySegment>(), status => cb(req.InternalGetHandle(), status)); + return _onWrite(handle, nbufs, status => cb(req.InternalGetHandle(), status)); } } } From 0dbf1083536bcc6a71f1b1389235fbc6612f152e Mon Sep 17 00:00:00 2001 From: Stephen Halter Date: Fri, 20 Nov 2015 12:59:53 -0800 Subject: [PATCH 2/5] Smarter unpinning of MemoryPoolBlock2 in SocketOutput --- src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs | 6 +++--- .../Infrastructure/MemoryPoolBlock2.cs | 6 ++---- .../Networking/UvWriteReq.cs | 8 ++++++++ 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs index be47d8e720..404cb601d5 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs @@ -305,12 +305,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http var returnBlock = block; block = block.Next; - returnBlock.Unpin(); returnBlock.Pool?.Return(returnBlock); } - _tail.Unpin(); - if (_isProducing) { _returnFromOnProducingComplete = _tail; @@ -381,6 +378,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http var writeReq = new UvWriteReq(Self._log); writeReq.Init(Self._thread.Loop); + writeReq.Write(Self._socket, _lockedStart, _lockedEnd, _bufferCount, (_writeReq, status, error, state) => { _writeReq.Dispose(); @@ -453,6 +451,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http returnBlock.Unpin(); returnBlock.Pool?.Return(returnBlock); } + + _lockedEnd.Block.Unpin(); } private void LockWrite() diff --git a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolBlock2.cs b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolBlock2.cs index 2bcd21d9a2..26156c6cc0 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolBlock2.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolBlock2.cs @@ -105,15 +105,13 @@ namespace Microsoft.AspNet.Server.Kestrel.Infrastructure /// public IntPtr Pin() { + Debug.Assert(!_pinHandle.IsAllocated); + if (_dataArrayPtr != IntPtr.Zero) { // this is a slab managed block - use the native address of the slab which is always locked return _dataArrayPtr; } - else if (_pinHandle.IsAllocated) - { - return _pinHandle.AddrOfPinnedObject(); - } else { // this is one-time-use memory - lock the managed memory until Unpin is called diff --git a/src/Microsoft.AspNet.Server.Kestrel/Networking/UvWriteReq.cs b/src/Microsoft.AspNet.Server.Kestrel/Networking/UvWriteReq.cs index 7330ef3c15..2695036f32 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Networking/UvWriteReq.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Networking/UvWriteReq.cs @@ -85,6 +85,14 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking _callback = null; _state = null; Unpin(this); + + var block = start.Block; + for (var index = 0; index < nBuffers; index++) + { + block.Unpin(); + block = block.Next; + } + throw; } } From a3173c487a1adb72a908e69e0a88a51c3b8f2cc1 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 11 Nov 2015 17:20:31 +0000 Subject: [PATCH 3/5] Precomputed header bytes --- .../Http/DateHeaderValueManager.cs | 14 + .../Http/Frame.cs | 192 +-- .../Http/FrameHeaders.Generated.cs | 1070 +++++++++++------ .../Http/FrameResponseHeaders.cs | 31 +- .../Http/MemoryPool.cs | 125 -- .../Http/MemoryPoolTextWriter.cs | 155 --- .../Http/ReasonPhrases.cs | 173 ++- .../Infrastructure/MemoryPool2.cs | 8 +- .../Infrastructure/MemoryPoolIterator2.cs | 104 ++ .../ServiceContext.cs | 4 - .../EngineTests.cs | 25 +- .../TestInput.cs | 2 - .../KnownHeaders.cs | 128 +- 13 files changed, 1231 insertions(+), 800 deletions(-) delete mode 100644 src/Microsoft.AspNet.Server.Kestrel/Http/MemoryPool.cs delete mode 100644 src/Microsoft.AspNet.Server.Kestrel/Http/MemoryPoolTextWriter.cs diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/DateHeaderValueManager.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/DateHeaderValueManager.cs index c471067eca..b85aaa2b1a 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/DateHeaderValueManager.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/DateHeaderValueManager.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Text; using System.Threading; using Microsoft.AspNet.Server.Kestrel.Infrastructure; @@ -17,6 +18,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http private readonly TimeSpan _timerInterval; private volatile string _dateValue; + private volatile bool _activeDateBytes; + private readonly byte[] _dateBytes0 = Encoding.ASCII.GetBytes("\r\nDate: DDD, dd mmm yyyy hh:mm:ss GMT"); + private readonly byte[] _dateBytes1 = Encoding.ASCII.GetBytes("\r\nDate: DDD, dd mmm yyyy hh:mm:ss GMT"); private object _timerLocker = new object(); private bool _isDisposed = false; private bool _hadRequestsSinceLastTimerTick = false; @@ -62,6 +66,12 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return _dateValue ?? _systemClock.UtcNow.ToString(Constants.RFC1123DateFormat); } + public byte[] GetDateHeaderValueBytes() + { + PumpTimer(); + return _activeDateBytes ? _dateBytes0 : _dateBytes1; + } + /// /// Releases all resources used by the current instance of . /// @@ -92,6 +102,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http // here as the timer won't fire until the timer interval has passed and we want a value assigned // inline now to serve requests that occur in the meantime. _dateValue = _systemClock.UtcNow.ToString(Constants.RFC1123DateFormat); + Encoding.ASCII.GetBytes(_dateValue, 0, _dateValue.Length, !_activeDateBytes ? _dateBytes0 : _dateBytes1, "\r\nDate: ".Length); + _activeDateBytes = !_activeDateBytes; _dateValueTimer = new Timer(UpdateDateValue, state: null, dueTime: _timerInterval, period: _timerInterval); } } @@ -105,6 +117,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http // See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.18 for required format of Date header _dateValue = now.ToString(Constants.RFC1123DateFormat); + Encoding.ASCII.GetBytes(_dateValue, 0, _dateValue.Length, !_activeDateBytes ? _dateBytes0 : _dateBytes1, "\r\nDate: ".Length); + _activeDateBytes = !_activeDateBytes; if (_hadRequestsSinceLastTimerTick) { diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs index 8dc2670a6a..98652050cd 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs @@ -29,6 +29,17 @@ namespace Microsoft.AspNet.Server.Kestrel.Http private static readonly ArraySegment _emptyData = new ArraySegment(new byte[0]); private static readonly byte[] _hex = Encoding.ASCII.GetBytes("0123456789abcdef"); + private static readonly byte[] _bytesConnectionClose = Encoding.ASCII.GetBytes("\r\nConnection: close"); + private static readonly byte[] _bytesConnectionKeepAlive = Encoding.ASCII.GetBytes("\r\nConnection: keep-alive"); + private static readonly byte[] _bytesTransferEncodingChunked = Encoding.ASCII.GetBytes("\r\nTransfer-Encoding: chunked"); + private static readonly byte[] _bytesHttpVersion1_0 = Encoding.ASCII.GetBytes("HTTP/1.0 "); + private static readonly byte[] _bytesHttpVersion1_1 = Encoding.ASCII.GetBytes("HTTP/1.1 "); + private static readonly byte[] _bytesContentLengthZero = Encoding.ASCII.GetBytes("\r\nContent-Length: 0"); + private static readonly byte[] _bytesSpace = Encoding.ASCII.GetBytes(" "); + private static readonly byte[] _bytesServer = Encoding.ASCII.GetBytes("\r\nServer: Kestrel"); + private static readonly byte[] _bytesDate = Encoding.ASCII.GetBytes("Date: "); + private static readonly byte[] _bytesEndHeaders = Encoding.ASCII.GetBytes("\r\n\r\n"); + private readonly object _onStartingSync = new Object(); private readonly object _onCompletedSync = new Object(); private readonly FrameRequestHeaders _requestHeaders = new FrameRequestHeaders(); @@ -53,6 +64,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http private bool _autoChunk; private Exception _applicationException; + private HttpVersionType _httpVersion; + private readonly IPEndPoint _localEndPoint; private readonly IPEndPoint _remoteEndPoint; private readonly Action _prepareRequest; @@ -81,7 +94,37 @@ namespace Microsoft.AspNet.Server.Kestrel.Http public string RequestUri { get; set; } public string Path { get; set; } public string QueryString { get; set; } - public string HttpVersion { get; set; } + public string HttpVersion + { + get + { + if (_httpVersion == HttpVersionType.Http1_1) + { + return "HTTP/1.1"; + } + if (_httpVersion == HttpVersionType.Http1_0) + { + return "HTTP/1.0"; + } + return ""; + } + set + { + if (value == "HTTP/1.1") + { + _httpVersion = HttpVersionType.Http1_1; + } + else if (value == "HTTP/1.0") + { + _httpVersion = HttpVersionType.Http1_0; + } + else + { + _httpVersion = HttpVersionType.Unknown; + } + } + } + public IHeaderDictionary RequestHeaders { get; set; } public Stream RequestBody { get; set; } @@ -118,7 +161,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http RequestUri = null; Path = null; QueryString = null; - HttpVersion = null; + _httpVersion = HttpVersionType.Unknown; RequestHeaders = _requestHeaders; RequestBody = null; StatusCode = 200; @@ -151,8 +194,12 @@ namespace Microsoft.AspNet.Server.Kestrel.Http public void ResetResponseHeaders() { _responseHeaders.Reset(); - _responseHeaders.HeaderServer = "Kestrel"; - _responseHeaders.HeaderDate = DateHeaderValueManager.GetDateHeaderValue(); + _responseHeaders.SetRawDate( + DateHeaderValueManager.GetDateHeaderValue(), + DateHeaderValueManager.GetDateHeaderValueBytes()); + _responseHeaders.SetRawServer( + "Kestrel", + _bytesServer); } /// @@ -502,7 +549,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http if (_responseStarted) return; StringValues expect; - if (HttpVersion.Equals("HTTP/1.1") && + if (_httpVersion == HttpVersionType.Http1_1 && RequestHeaders.TryGetValue("Expect", out expect) && (expect.FirstOrDefault() ?? "").Equals("100-continue", StringComparison.OrdinalIgnoreCase)) { @@ -526,19 +573,14 @@ namespace Microsoft.AspNet.Server.Kestrel.Http await ProduceStart(immediate, appCompleted: false); } - private async Task ProduceStart(bool immediate, bool appCompleted) + private Task ProduceStart(bool immediate, bool appCompleted) { - if (_responseStarted) return; + if (_responseStarted) return TaskUtilities.CompletedTask; _responseStarted = true; - var status = ReasonPhrases.ToStatus(StatusCode, ReasonPhrase); + var statusBytes = ReasonPhrases.ToStatusBytes(StatusCode, ReasonPhrase); - var responseHeader = CreateResponseHeader(status, appCompleted); - - using (responseHeader.Item2) - { - await SocketOutput.WriteAsync(responseHeader.Item1, immediate: immediate); - } + return CreateResponseHeader(statusBytes, appCompleted, immediate); } private async Task ProduceEnd() @@ -557,7 +599,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http ReasonPhrase = null; ResetResponseHeaders(); - _responseHeaders.HeaderContentLength = "0"; + _responseHeaders.SetRawContentLength("0", _bytesContentLengthZero); } } @@ -576,58 +618,26 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - private Tuple, IDisposable> CreateResponseHeader( - string status, - bool appCompleted) + private Task CreateResponseHeader( + byte[] statusBytes, + bool appCompleted, + bool immediate) { - var writer = new MemoryPoolTextWriter(Memory); - writer.Write(HttpVersion); - writer.Write(' '); - writer.Write(status); - writer.Write('\r'); - writer.Write('\n'); - - var hasConnection = false; - var hasTransferEncoding = false; - var hasContentLength = false; - - foreach (var header in _responseHeaders) + var memoryBlock = Memory2.Lease(); + var begin = memoryBlock.GetIterator(); + var end = begin; + if (_keepAlive) { - var isConnection = false; - if (!hasConnection && - string.Equals(header.Key, "Connection", StringComparison.OrdinalIgnoreCase)) + foreach (var connectionValue in _responseHeaders.HeaderConnection) { - hasConnection = isConnection = true; - } - else if (!hasTransferEncoding && - string.Equals(header.Key, "Transfer-Encoding", StringComparison.OrdinalIgnoreCase)) - { - hasTransferEncoding = true; - } - else if (!hasContentLength && - string.Equals(header.Key, "Content-Length", StringComparison.OrdinalIgnoreCase)) - { - hasContentLength = true; - } - - foreach (var value in header.Value) - { - writer.Write(header.Key); - writer.Write(':'); - writer.Write(' '); - writer.Write(value); - writer.Write('\r'); - writer.Write('\n'); - - if (isConnection && value.IndexOf("close", StringComparison.OrdinalIgnoreCase) != -1) + if (connectionValue.IndexOf("close", StringComparison.OrdinalIgnoreCase) != -1) { _keepAlive = false; } } - } - if (_keepAlive && !hasTransferEncoding && !hasContentLength) + if (_keepAlive && !_responseHeaders.HasTransferEncoding && !_responseHeaders.HasContentLength) { if (appCompleted) { @@ -637,15 +647,15 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { // Since the app has completed and we are only now generating // the headers we can safely set the Content-Length to 0. - writer.Write("Content-Length: 0\r\n"); + _responseHeaders.SetRawContentLength("0", _bytesContentLengthZero); } } else { - if (HttpVersion == "HTTP/1.1") + if (_httpVersion == HttpVersionType.Http1_1) { _autoChunk = true; - writer.Write("Transfer-Encoding: chunked\r\n"); + _responseHeaders.SetRawTransferEncoding("chunked", _bytesTransferEncodingChunked); } else { @@ -654,21 +664,58 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if (_keepAlive == false && hasConnection == false && HttpVersion == "HTTP/1.1") + if (_keepAlive == false && _responseHeaders.HasConnection == false && _httpVersion == HttpVersionType.Http1_1) { - writer.Write("Connection: close\r\n\r\n"); + _responseHeaders.SetRawConnection("close", _bytesConnectionClose); } - else if (_keepAlive && hasConnection == false && HttpVersion == "HTTP/1.0") + else if (_keepAlive && _responseHeaders.HasConnection == false && _httpVersion == HttpVersionType.Http1_0) { - writer.Write("Connection: keep-alive\r\n\r\n"); + _responseHeaders.SetRawConnection("keep-alive", _bytesConnectionKeepAlive); + } + + end.CopyFrom(_httpVersion == HttpVersionType.Http1_1 ? _bytesHttpVersion1_1 : _bytesHttpVersion1_0); + end.CopyFrom(statusBytes); + _responseHeaders.CopyTo(ref end); + end.CopyFrom(_bytesEndHeaders, 0, _bytesEndHeaders.Length); + + // TODO: change this to SocketOutput.ProduceStart/ProduceComplete once that change is made + var scan = begin.Block; + while (scan.Next != null) + { + if (scan.Start != scan.End) + { + SocketOutput.WriteAsync( + new ArraySegment(scan.Array, scan.Start, scan.End - scan.Start), + false); + } + var next = scan.Next; + Memory2.Return(scan); + scan = next; + } + var writeTask = SocketOutput.WriteAsync( + new ArraySegment(scan.Array, scan.Start, scan.End - scan.Start), + immediate); + + if (writeTask.IsCompleted) + { + Memory2.Return(scan); + return TaskUtilities.CompletedTask; } else { - writer.Write('\r'); - writer.Write('\n'); + return writeTask.ContinueWith( + (t, o) => + { + var mb = (MemoryPoolBlock2)o; + mb.Pool.Return(mb); + + if (t.IsFaulted) + { + throw t.Exception; + } + }, + scan); } - writer.Flush(); - return new Tuple, IDisposable>(writer.Buffer, writer); } private bool TakeStartLine(SocketInput input) @@ -877,5 +924,12 @@ namespace Microsoft.AspNet.Server.Kestrel.Http _applicationException = ex; Log.ApplicationError(ex); } + + private enum HttpVersionType + { + Unknown = -1, + Http1_0 = 0, + Http1_1 = 1 + } } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/FrameHeaders.Generated.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameHeaders.Generated.cs index 1b4415b5a9..5571e9c56b 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/FrameHeaders.Generated.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameHeaders.Generated.cs @@ -1,13 +1,16 @@ using System; using System.Collections.Generic; +using System.Text; +using Microsoft.AspNet.Server.Kestrel.Infrastructure; using Microsoft.Extensions.Primitives; namespace Microsoft.AspNet.Server.Kestrel.Http { - public partial class FrameRequestHeaders + public partial class FrameRequestHeaders { + private long _bits = 0; private StringValues _CacheControl; @@ -52,6 +55,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http private StringValues _Translate; private StringValues _UserAgent; + public StringValues HeaderCacheControl { get @@ -61,10 +65,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 1L; - _CacheControl = value; + _CacheControl = value; } } - public StringValues HeaderConnection { get @@ -74,10 +77,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 2L; - _Connection = value; + _Connection = value; } } - public StringValues HeaderDate { get @@ -87,10 +89,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 4L; - _Date = value; + _Date = value; } } - public StringValues HeaderKeepAlive { get @@ -100,10 +101,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 8L; - _KeepAlive = value; + _KeepAlive = value; } } - public StringValues HeaderPragma { get @@ -113,10 +113,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 16L; - _Pragma = value; + _Pragma = value; } } - public StringValues HeaderTrailer { get @@ -126,10 +125,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 32L; - _Trailer = value; + _Trailer = value; } } - public StringValues HeaderTransferEncoding { get @@ -139,10 +137,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 64L; - _TransferEncoding = value; + _TransferEncoding = value; } } - public StringValues HeaderUpgrade { get @@ -152,10 +149,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 128L; - _Upgrade = value; + _Upgrade = value; } } - public StringValues HeaderVia { get @@ -165,10 +161,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 256L; - _Via = value; + _Via = value; } } - public StringValues HeaderWarning { get @@ -178,10 +173,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 512L; - _Warning = value; + _Warning = value; } } - public StringValues HeaderAllow { get @@ -191,10 +185,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 1024L; - _Allow = value; + _Allow = value; } } - public StringValues HeaderContentLength { get @@ -204,10 +197,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 2048L; - _ContentLength = value; + _ContentLength = value; } } - public StringValues HeaderContentType { get @@ -217,10 +209,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 4096L; - _ContentType = value; + _ContentType = value; } } - public StringValues HeaderContentEncoding { get @@ -230,10 +221,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 8192L; - _ContentEncoding = value; + _ContentEncoding = value; } } - public StringValues HeaderContentLanguage { get @@ -243,10 +233,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 16384L; - _ContentLanguage = value; + _ContentLanguage = value; } } - public StringValues HeaderContentLocation { get @@ -256,10 +245,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 32768L; - _ContentLocation = value; + _ContentLocation = value; } } - public StringValues HeaderContentMD5 { get @@ -269,10 +257,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 65536L; - _ContentMD5 = value; + _ContentMD5 = value; } } - public StringValues HeaderContentRange { get @@ -282,10 +269,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 131072L; - _ContentRange = value; + _ContentRange = value; } } - public StringValues HeaderExpires { get @@ -295,10 +281,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 262144L; - _Expires = value; + _Expires = value; } } - public StringValues HeaderLastModified { get @@ -308,10 +293,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 524288L; - _LastModified = value; + _LastModified = value; } } - public StringValues HeaderAccept { get @@ -321,10 +305,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 1048576L; - _Accept = value; + _Accept = value; } } - public StringValues HeaderAcceptCharset { get @@ -334,10 +317,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 2097152L; - _AcceptCharset = value; + _AcceptCharset = value; } } - public StringValues HeaderAcceptEncoding { get @@ -347,10 +329,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 4194304L; - _AcceptEncoding = value; + _AcceptEncoding = value; } } - public StringValues HeaderAcceptLanguage { get @@ -360,10 +341,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 8388608L; - _AcceptLanguage = value; + _AcceptLanguage = value; } } - public StringValues HeaderAuthorization { get @@ -373,10 +353,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 16777216L; - _Authorization = value; + _Authorization = value; } } - public StringValues HeaderCookie { get @@ -386,10 +365,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 33554432L; - _Cookie = value; + _Cookie = value; } } - public StringValues HeaderExpect { get @@ -399,10 +377,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 67108864L; - _Expect = value; + _Expect = value; } } - public StringValues HeaderFrom { get @@ -412,10 +389,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 134217728L; - _From = value; + _From = value; } } - public StringValues HeaderHost { get @@ -425,10 +401,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 268435456L; - _Host = value; + _Host = value; } } - public StringValues HeaderIfMatch { get @@ -438,10 +413,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 536870912L; - _IfMatch = value; + _IfMatch = value; } } - public StringValues HeaderIfModifiedSince { get @@ -451,10 +425,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 1073741824L; - _IfModifiedSince = value; + _IfModifiedSince = value; } } - public StringValues HeaderIfNoneMatch { get @@ -464,10 +437,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 2147483648L; - _IfNoneMatch = value; + _IfNoneMatch = value; } } - public StringValues HeaderIfRange { get @@ -477,10 +449,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 4294967296L; - _IfRange = value; + _IfRange = value; } } - public StringValues HeaderIfUnmodifiedSince { get @@ -490,10 +461,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 8589934592L; - _IfUnmodifiedSince = value; + _IfUnmodifiedSince = value; } } - public StringValues HeaderMaxForwards { get @@ -503,10 +473,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 17179869184L; - _MaxForwards = value; + _MaxForwards = value; } } - public StringValues HeaderProxyAuthorization { get @@ -516,10 +485,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 34359738368L; - _ProxyAuthorization = value; + _ProxyAuthorization = value; } } - public StringValues HeaderReferer { get @@ -529,10 +497,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 68719476736L; - _Referer = value; + _Referer = value; } } - public StringValues HeaderRange { get @@ -542,10 +509,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 137438953472L; - _Range = value; + _Range = value; } } - public StringValues HeaderTE { get @@ -555,10 +521,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 274877906944L; - _TE = value; + _TE = value; } } - public StringValues HeaderTranslate { get @@ -568,10 +533,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 549755813888L; - _Translate = value; + _Translate = value; } } - public StringValues HeaderUserAgent { get @@ -581,7 +545,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 1099511627776L; - _UserAgent = value; + _UserAgent = value; } } @@ -589,14 +553,13 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { return BitCount(_bits) + (MaybeUnknown?.Count ?? 0); } - protected override StringValues GetValueFast(string key) { switch(key.Length) { case 13: { - if ("Cache-Control".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Cache-Control".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 1L) != 0)) { @@ -608,7 +571,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("Content-Range".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Range".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 131072L) != 0)) { @@ -620,7 +583,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("Last-Modified".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Last-Modified".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 524288L) != 0)) { @@ -632,7 +595,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("Authorization".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Authorization".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 16777216L) != 0)) { @@ -644,7 +607,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("If-None-Match".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("If-None-Match".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 2147483648L) != 0)) { @@ -657,10 +620,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 10: { - if ("Connection".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Connection".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 2L) != 0)) { @@ -672,7 +635,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("Keep-Alive".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Keep-Alive".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 8L) != 0)) { @@ -684,7 +647,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("User-Agent".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("User-Agent".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 1099511627776L) != 0)) { @@ -697,10 +660,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 4: { - if ("Date".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Date".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 4L) != 0)) { @@ -712,7 +675,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("From".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("From".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 134217728L) != 0)) { @@ -724,7 +687,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("Host".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Host".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 268435456L) != 0)) { @@ -737,10 +700,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 6: { - if ("Pragma".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Pragma".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 16L) != 0)) { @@ -752,7 +715,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("Accept".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Accept".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 1048576L) != 0)) { @@ -764,7 +727,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("Cookie".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Cookie".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 33554432L) != 0)) { @@ -776,7 +739,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("Expect".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Expect".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 67108864L) != 0)) { @@ -789,10 +752,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 7: { - if ("Trailer".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Trailer".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 32L) != 0)) { @@ -804,7 +767,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("Upgrade".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Upgrade".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 128L) != 0)) { @@ -816,7 +779,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("Warning".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Warning".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 512L) != 0)) { @@ -828,7 +791,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("Expires".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Expires".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 262144L) != 0)) { @@ -840,7 +803,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("Referer".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Referer".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 68719476736L) != 0)) { @@ -853,10 +816,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 17: { - if ("Transfer-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Transfer-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 64L) != 0)) { @@ -868,7 +831,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("If-Modified-Since".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("If-Modified-Since".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 1073741824L) != 0)) { @@ -881,10 +844,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 3: { - if ("Via".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Via".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 256L) != 0)) { @@ -897,10 +860,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 5: { - if ("Allow".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Allow".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 1024L) != 0)) { @@ -912,7 +875,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("Range".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Range".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 137438953472L) != 0)) { @@ -925,10 +888,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 14: { - if ("Content-Length".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Length".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 2048L) != 0)) { @@ -940,7 +903,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("Accept-Charset".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Accept-Charset".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 2097152L) != 0)) { @@ -953,10 +916,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 12: { - if ("Content-Type".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Type".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 4096L) != 0)) { @@ -968,7 +931,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("Max-Forwards".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Max-Forwards".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 17179869184L) != 0)) { @@ -981,10 +944,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 16: { - if ("Content-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 8192L) != 0)) { @@ -996,7 +959,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("Content-Language".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Language".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 16384L) != 0)) { @@ -1008,7 +971,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("Content-Location".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Location".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 32768L) != 0)) { @@ -1021,10 +984,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 11: { - if ("Content-MD5".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-MD5".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 65536L) != 0)) { @@ -1037,10 +1000,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 15: { - if ("Accept-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Accept-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 4194304L) != 0)) { @@ -1052,7 +1015,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("Accept-Language".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Accept-Language".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 8388608L) != 0)) { @@ -1065,10 +1028,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 8: { - if ("If-Match".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("If-Match".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 536870912L) != 0)) { @@ -1080,7 +1043,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("If-Range".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("If-Range".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 4294967296L) != 0)) { @@ -1093,10 +1056,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 19: { - if ("If-Unmodified-Since".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("If-Unmodified-Since".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 8589934592L) != 0)) { @@ -1108,7 +1071,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("Proxy-Authorization".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Proxy-Authorization".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 34359738368L) != 0)) { @@ -1121,10 +1084,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 2: { - if ("TE".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("TE".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 274877906944L) != 0)) { @@ -1137,10 +1100,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 9: { - if ("Translate".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Translate".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 549755813888L) != 0)) { @@ -1153,14 +1116,13 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - } +} if (MaybeUnknown == null) { throw new System.Collections.Generic.KeyNotFoundException(); } return MaybeUnknown[key]; } - protected override bool TryGetValueFast(string key, out StringValues value) { switch(key.Length) @@ -1238,7 +1200,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 10: { if ("Connection".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1284,7 +1246,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 4: { if ("Date".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1330,7 +1292,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 6: { if ("Pragma".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1390,7 +1352,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 7: { if ("Trailer".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1464,7 +1426,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 17: { if ("Transfer-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1496,7 +1458,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 3: { if ("Via".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1514,7 +1476,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 5: { if ("Allow".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1546,7 +1508,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 14: { if ("Content-Length".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1578,7 +1540,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 12: { if ("Content-Type".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1610,7 +1572,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 16: { if ("Content-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1656,7 +1618,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 11: { if ("Content-MD5".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1674,7 +1636,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 15: { if ("Accept-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1706,7 +1668,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 8: { if ("If-Match".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1738,7 +1700,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 19: { if ("If-Unmodified-Since".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1770,7 +1732,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 2: { if ("TE".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1788,7 +1750,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 9: { if ("Translate".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1806,11 +1768,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - } +} value = StringValues.Empty; return MaybeUnknown?.TryGetValue(key, out value) ?? false; } - protected override void SetValueFast(string key, StringValues value) { switch(key.Length) @@ -1853,7 +1814,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 10: { if ("Connection".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1878,7 +1839,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 4: { if ("Date".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1903,7 +1864,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 6: { if ("Pragma".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1935,7 +1896,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 7: { if ("Trailer".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1974,7 +1935,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 17: { if ("Transfer-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1992,7 +1953,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 3: { if ("Via".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -2003,7 +1964,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 5: { if ("Allow".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -2021,7 +1982,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 14: { if ("Content-Length".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -2039,7 +2000,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 12: { if ("Content-Type".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -2057,7 +2018,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 16: { if ("Content-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -2082,7 +2043,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 11: { if ("Content-MD5".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -2093,7 +2054,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 15: { if ("Accept-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -2111,7 +2072,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 8: { if ("If-Match".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -2129,7 +2090,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 19: { if ("If-Unmodified-Since".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -2147,7 +2108,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 2: { if ("TE".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -2158,7 +2119,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 9: { if ("Translate".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -2169,17 +2130,16 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - } +} Unknown[key] = value; } - protected override void AddValueFast(string key, StringValues value) { switch(key.Length) { case 13: { - if ("Cache-Control".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Cache-Control".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 1L) != 0)) { @@ -2190,7 +2150,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return; } - if ("Content-Range".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Range".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 131072L) != 0)) { @@ -2201,7 +2161,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return; } - if ("Last-Modified".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Last-Modified".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 524288L) != 0)) { @@ -2212,7 +2172,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return; } - if ("Authorization".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Authorization".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 16777216L) != 0)) { @@ -2223,7 +2183,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return; } - if ("If-None-Match".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("If-None-Match".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 2147483648L) != 0)) { @@ -2238,7 +2198,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http case 10: { - if ("Connection".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Connection".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 2L) != 0)) { @@ -2249,7 +2209,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return; } - if ("Keep-Alive".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Keep-Alive".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 8L) != 0)) { @@ -2260,7 +2220,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return; } - if ("User-Agent".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("User-Agent".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 1099511627776L) != 0)) { @@ -2275,7 +2235,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http case 4: { - if ("Date".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Date".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 4L) != 0)) { @@ -2286,7 +2246,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return; } - if ("From".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("From".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 134217728L) != 0)) { @@ -2297,7 +2257,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return; } - if ("Host".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Host".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 268435456L) != 0)) { @@ -2312,7 +2272,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http case 6: { - if ("Pragma".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Pragma".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 16L) != 0)) { @@ -2323,7 +2283,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return; } - if ("Accept".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Accept".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 1048576L) != 0)) { @@ -2334,7 +2294,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return; } - if ("Cookie".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Cookie".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 33554432L) != 0)) { @@ -2345,7 +2305,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return; } - if ("Expect".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Expect".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 67108864L) != 0)) { @@ -2360,7 +2320,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http case 7: { - if ("Trailer".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Trailer".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 32L) != 0)) { @@ -2371,7 +2331,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return; } - if ("Upgrade".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Upgrade".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 128L) != 0)) { @@ -2382,7 +2342,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return; } - if ("Warning".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Warning".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 512L) != 0)) { @@ -2393,7 +2353,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return; } - if ("Expires".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Expires".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 262144L) != 0)) { @@ -2404,7 +2364,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return; } - if ("Referer".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Referer".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 68719476736L) != 0)) { @@ -2419,7 +2379,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http case 17: { - if ("Transfer-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Transfer-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 64L) != 0)) { @@ -2430,7 +2390,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return; } - if ("If-Modified-Since".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("If-Modified-Since".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 1073741824L) != 0)) { @@ -2445,7 +2405,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http case 3: { - if ("Via".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Via".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 256L) != 0)) { @@ -2460,7 +2420,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http case 5: { - if ("Allow".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Allow".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 1024L) != 0)) { @@ -2471,7 +2431,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return; } - if ("Range".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Range".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 137438953472L) != 0)) { @@ -2486,7 +2446,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http case 14: { - if ("Content-Length".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Length".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 2048L) != 0)) { @@ -2497,7 +2457,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return; } - if ("Accept-Charset".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Accept-Charset".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 2097152L) != 0)) { @@ -2512,7 +2472,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http case 12: { - if ("Content-Type".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Type".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 4096L) != 0)) { @@ -2523,7 +2483,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return; } - if ("Max-Forwards".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Max-Forwards".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 17179869184L) != 0)) { @@ -2538,7 +2498,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http case 16: { - if ("Content-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 8192L) != 0)) { @@ -2549,7 +2509,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return; } - if ("Content-Language".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Language".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 16384L) != 0)) { @@ -2560,7 +2520,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return; } - if ("Content-Location".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Location".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 32768L) != 0)) { @@ -2575,7 +2535,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http case 11: { - if ("Content-MD5".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-MD5".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 65536L) != 0)) { @@ -2590,7 +2550,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http case 15: { - if ("Accept-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Accept-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 4194304L) != 0)) { @@ -2601,7 +2561,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return; } - if ("Accept-Language".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Accept-Language".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 8388608L) != 0)) { @@ -2616,7 +2576,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http case 8: { - if ("If-Match".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("If-Match".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 536870912L) != 0)) { @@ -2627,7 +2587,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return; } - if ("If-Range".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("If-Range".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 4294967296L) != 0)) { @@ -2642,7 +2602,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http case 19: { - if ("If-Unmodified-Since".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("If-Unmodified-Since".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 8589934592L) != 0)) { @@ -2653,7 +2613,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return; } - if ("Proxy-Authorization".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Proxy-Authorization".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 34359738368L) != 0)) { @@ -2668,7 +2628,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http case 2: { - if ("TE".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("TE".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 274877906944L) != 0)) { @@ -2683,7 +2643,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http case 9: { - if ("Translate".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Translate".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 549755813888L) != 0)) { @@ -2698,7 +2658,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } Unknown.Add(key, value); } - protected override bool RemoveFast(string key) { switch(key.Length) @@ -3347,7 +3306,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } return MaybeUnknown?.Remove(key) ?? false; } - protected override void ClearFast() { _bits = 0; @@ -3393,6 +3351,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http _TE = StringValues.Empty; _Translate = StringValues.Empty; _UserAgent = StringValues.Empty; + MaybeUnknown?.Clear(); } @@ -3402,7 +3361,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { throw new ArgumentException(); } - if (((_bits & 1L) != 0)) { @@ -3857,7 +3815,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http ((ICollection>)MaybeUnknown)?.CopyTo(array, arrayIndex); } - + public unsafe void Append(byte[] keyBytes, int keyOffset, int keyLength, string value) { fixed(byte* ptr = keyBytes) { var pUB = ptr + keyOffset; var pUL = (ulong*)pUB; var pUI = (uint*)pUB; var pUS = (ushort*)pUB; @@ -4510,7 +4468,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Http Unknown.TryGetValue(key, out existing); Unknown[key] = AppendValue(existing, value); } - public partial struct Enumerator { public bool MoveNext() @@ -4985,8 +4942,13 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - public partial class FrameResponseHeaders + public partial class FrameResponseHeaders { + private static byte[] _headerBytes = new byte[] + { + 13,10,67,97,99,104,101,45,67,111,110,116,114,111,108,58,32,13,10,67,111,110,110,101,99,116,105,111,110,58,32,13,10,68,97,116,101,58,32,13,10,75,101,101,112,45,65,108,105,118,101,58,32,13,10,80,114,97,103,109,97,58,32,13,10,84,114,97,105,108,101,114,58,32,13,10,84,114,97,110,115,102,101,114,45,69,110,99,111,100,105,110,103,58,32,13,10,85,112,103,114,97,100,101,58,32,13,10,86,105,97,58,32,13,10,87,97,114,110,105,110,103,58,32,13,10,65,108,108,111,119,58,32,13,10,67,111,110,116,101,110,116,45,76,101,110,103,116,104,58,32,13,10,67,111,110,116,101,110,116,45,84,121,112,101,58,32,13,10,67,111,110,116,101,110,116,45,69,110,99,111,100,105,110,103,58,32,13,10,67,111,110,116,101,110,116,45,76,97,110,103,117,97,103,101,58,32,13,10,67,111,110,116,101,110,116,45,76,111,99,97,116,105,111,110,58,32,13,10,67,111,110,116,101,110,116,45,77,68,53,58,32,13,10,67,111,110,116,101,110,116,45,82,97,110,103,101,58,32,13,10,69,120,112,105,114,101,115,58,32,13,10,76,97,115,116,45,77,111,100,105,102,105,101,100,58,32,13,10,65,99,99,101,112,116,45,82,97,110,103,101,115,58,32,13,10,65,103,101,58,32,13,10,69,84,97,103,58,32,13,10,76,111,99,97,116,105,111,110,58,32,13,10,80,114,111,120,121,45,65,117,116,104,101,116,105,99,97,116,101,58,32,13,10,82,101,116,114,121,45,65,102,116,101,114,58,32,13,10,83,101,114,118,101,114,58,32,13,10,83,101,116,45,67,111,111,107,105,101,58,32,13,10,86,97,114,121,58,32,13,10,87,87,87,45,65,117,116,104,101,110,116,105,99,97,116,101,58,32, + }; + private long _bits = 0; private StringValues _CacheControl; @@ -5020,6 +4982,12 @@ namespace Microsoft.AspNet.Server.Kestrel.Http private StringValues _Vary; private StringValues _WWWAuthenticate; + private byte[] _rawConnection; + private byte[] _rawDate; + private byte[] _rawTransferEncoding; + private byte[] _rawContentLength; + private byte[] _rawServer; + public StringValues HeaderCacheControl { get @@ -5029,10 +4997,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 1L; - _CacheControl = value; + _CacheControl = value; } } - public StringValues HeaderConnection { get @@ -5042,10 +5009,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 2L; - _Connection = value; + _Connection = value; + _rawConnection = null; } } - public StringValues HeaderDate { get @@ -5055,10 +5022,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 4L; - _Date = value; + _Date = value; + _rawDate = null; } } - public StringValues HeaderKeepAlive { get @@ -5068,10 +5035,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 8L; - _KeepAlive = value; + _KeepAlive = value; } } - public StringValues HeaderPragma { get @@ -5081,10 +5047,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 16L; - _Pragma = value; + _Pragma = value; } } - public StringValues HeaderTrailer { get @@ -5094,10 +5059,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 32L; - _Trailer = value; + _Trailer = value; } } - public StringValues HeaderTransferEncoding { get @@ -5107,10 +5071,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 64L; - _TransferEncoding = value; + _TransferEncoding = value; + _rawTransferEncoding = null; } } - public StringValues HeaderUpgrade { get @@ -5120,10 +5084,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 128L; - _Upgrade = value; + _Upgrade = value; } } - public StringValues HeaderVia { get @@ -5133,10 +5096,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 256L; - _Via = value; + _Via = value; } } - public StringValues HeaderWarning { get @@ -5146,10 +5108,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 512L; - _Warning = value; + _Warning = value; } } - public StringValues HeaderAllow { get @@ -5159,10 +5120,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 1024L; - _Allow = value; + _Allow = value; } } - public StringValues HeaderContentLength { get @@ -5172,10 +5132,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 2048L; - _ContentLength = value; + _ContentLength = value; + _rawContentLength = null; } } - public StringValues HeaderContentType { get @@ -5185,10 +5145,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 4096L; - _ContentType = value; + _ContentType = value; } } - public StringValues HeaderContentEncoding { get @@ -5198,10 +5157,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 8192L; - _ContentEncoding = value; + _ContentEncoding = value; } } - public StringValues HeaderContentLanguage { get @@ -5211,10 +5169,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 16384L; - _ContentLanguage = value; + _ContentLanguage = value; } } - public StringValues HeaderContentLocation { get @@ -5224,10 +5181,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 32768L; - _ContentLocation = value; + _ContentLocation = value; } } - public StringValues HeaderContentMD5 { get @@ -5237,10 +5193,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 65536L; - _ContentMD5 = value; + _ContentMD5 = value; } } - public StringValues HeaderContentRange { get @@ -5250,10 +5205,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 131072L; - _ContentRange = value; + _ContentRange = value; } } - public StringValues HeaderExpires { get @@ -5263,10 +5217,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 262144L; - _Expires = value; + _Expires = value; } } - public StringValues HeaderLastModified { get @@ -5276,10 +5229,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 524288L; - _LastModified = value; + _LastModified = value; } } - public StringValues HeaderAcceptRanges { get @@ -5289,10 +5241,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 1048576L; - _AcceptRanges = value; + _AcceptRanges = value; } } - public StringValues HeaderAge { get @@ -5302,10 +5253,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 2097152L; - _Age = value; + _Age = value; } } - public StringValues HeaderETag { get @@ -5315,10 +5265,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 4194304L; - _ETag = value; + _ETag = value; } } - public StringValues HeaderLocation { get @@ -5328,10 +5277,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 8388608L; - _Location = value; + _Location = value; } } - public StringValues HeaderProxyAutheticate { get @@ -5341,10 +5289,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 16777216L; - _ProxyAutheticate = value; + _ProxyAutheticate = value; } } - public StringValues HeaderRetryAfter { get @@ -5354,10 +5301,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 33554432L; - _RetryAfter = value; + _RetryAfter = value; } } - public StringValues HeaderServer { get @@ -5367,10 +5313,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 67108864L; - _Server = value; + _Server = value; + _rawServer = null; } } - public StringValues HeaderSetCookie { get @@ -5380,10 +5326,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 134217728L; - _SetCookie = value; + _SetCookie = value; } } - public StringValues HeaderVary { get @@ -5393,10 +5338,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 268435456L; - _Vary = value; + _Vary = value; } } - public StringValues HeaderWWWAuthenticate { get @@ -5406,22 +5350,51 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set { _bits |= 536870912L; - _WWWAuthenticate = value; + _WWWAuthenticate = value; } } + public void SetRawConnection(StringValues value, byte[] raw) + { + _bits |= 2L; + _Connection = value; + _rawConnection = raw; + } + public void SetRawDate(StringValues value, byte[] raw) + { + _bits |= 4L; + _Date = value; + _rawDate = raw; + } + public void SetRawTransferEncoding(StringValues value, byte[] raw) + { + _bits |= 64L; + _TransferEncoding = value; + _rawTransferEncoding = raw; + } + public void SetRawContentLength(StringValues value, byte[] raw) + { + _bits |= 2048L; + _ContentLength = value; + _rawContentLength = raw; + } + public void SetRawServer(StringValues value, byte[] raw) + { + _bits |= 67108864L; + _Server = value; + _rawServer = raw; + } protected override int GetCountFast() { return BitCount(_bits) + (MaybeUnknown?.Count ?? 0); } - protected override StringValues GetValueFast(string key) { switch(key.Length) { case 13: { - if ("Cache-Control".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Cache-Control".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 1L) != 0)) { @@ -5433,7 +5406,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("Content-Range".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Range".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 131072L) != 0)) { @@ -5445,7 +5418,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("Last-Modified".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Last-Modified".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 524288L) != 0)) { @@ -5457,7 +5430,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("Accept-Ranges".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Accept-Ranges".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 1048576L) != 0)) { @@ -5470,10 +5443,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 10: { - if ("Connection".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Connection".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 2L) != 0)) { @@ -5485,7 +5458,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("Keep-Alive".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Keep-Alive".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 8L) != 0)) { @@ -5497,7 +5470,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("Set-Cookie".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Set-Cookie".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 134217728L) != 0)) { @@ -5510,10 +5483,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 4: { - if ("Date".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Date".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 4L) != 0)) { @@ -5525,7 +5498,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("ETag".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("ETag".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 4194304L) != 0)) { @@ -5537,7 +5510,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("Vary".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Vary".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 268435456L) != 0)) { @@ -5550,10 +5523,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 6: { - if ("Pragma".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Pragma".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 16L) != 0)) { @@ -5565,7 +5538,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("Server".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Server".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 67108864L) != 0)) { @@ -5578,10 +5551,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 7: { - if ("Trailer".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Trailer".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 32L) != 0)) { @@ -5593,7 +5566,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("Upgrade".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Upgrade".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 128L) != 0)) { @@ -5605,7 +5578,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("Warning".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Warning".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 512L) != 0)) { @@ -5617,7 +5590,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("Expires".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Expires".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 262144L) != 0)) { @@ -5630,10 +5603,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 17: { - if ("Transfer-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Transfer-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 64L) != 0)) { @@ -5645,7 +5618,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("Proxy-Autheticate".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Proxy-Autheticate".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 16777216L) != 0)) { @@ -5658,10 +5631,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 3: { - if ("Via".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Via".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 256L) != 0)) { @@ -5673,7 +5646,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("Age".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Age".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 2097152L) != 0)) { @@ -5686,10 +5659,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 5: { - if ("Allow".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Allow".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 1024L) != 0)) { @@ -5702,10 +5675,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 14: { - if ("Content-Length".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Length".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 2048L) != 0)) { @@ -5718,10 +5691,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 12: { - if ("Content-Type".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Type".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 4096L) != 0)) { @@ -5734,10 +5707,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 16: { - if ("Content-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 8192L) != 0)) { @@ -5749,7 +5722,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("Content-Language".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Language".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 16384L) != 0)) { @@ -5761,7 +5734,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("Content-Location".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Location".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 32768L) != 0)) { @@ -5773,7 +5746,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("WWW-Authenticate".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("WWW-Authenticate".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 536870912L) != 0)) { @@ -5786,10 +5759,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 11: { - if ("Content-MD5".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-MD5".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 65536L) != 0)) { @@ -5801,7 +5774,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - if ("Retry-After".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Retry-After".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 33554432L) != 0)) { @@ -5814,10 +5787,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 8: { - if ("Location".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Location".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 8388608L) != 0)) { @@ -5830,14 +5803,13 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - } +} if (MaybeUnknown == null) { throw new System.Collections.Generic.KeyNotFoundException(); } return MaybeUnknown[key]; } - protected override bool TryGetValueFast(string key, out StringValues value) { switch(key.Length) @@ -5901,7 +5873,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 10: { if ("Connection".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -5947,7 +5919,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 4: { if ("Date".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -5993,7 +5965,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 6: { if ("Pragma".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -6025,7 +5997,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 7: { if ("Trailer".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -6085,7 +6057,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 17: { if ("Transfer-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -6117,7 +6089,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 3: { if ("Via".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -6149,7 +6121,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 5: { if ("Allow".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -6167,7 +6139,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 14: { if ("Content-Length".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -6185,7 +6157,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 12: { if ("Content-Type".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -6203,7 +6175,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 16: { if ("Content-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -6263,7 +6235,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 11: { if ("Content-MD5".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -6295,7 +6267,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 8: { if ("Location".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -6313,11 +6285,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - } +} value = StringValues.Empty; return MaybeUnknown?.TryGetValue(key, out value) ?? false; } - protected override void SetValueFast(string key, StringValues value) { switch(key.Length) @@ -6353,13 +6324,14 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 10: { if ("Connection".Equals(key, StringComparison.OrdinalIgnoreCase)) { _bits |= 2L; _Connection = value; + _rawConnection = null; return; } @@ -6378,13 +6350,14 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 4: { if ("Date".Equals(key, StringComparison.OrdinalIgnoreCase)) { _bits |= 4L; _Date = value; + _rawDate = null; return; } @@ -6403,7 +6376,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 6: { if ("Pragma".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -6417,11 +6390,12 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { _bits |= 67108864L; _Server = value; + _rawServer = null; return; } } break; - + case 7: { if ("Trailer".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -6453,13 +6427,14 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 17: { if ("Transfer-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) { _bits |= 64L; _TransferEncoding = value; + _rawTransferEncoding = null; return; } @@ -6471,7 +6446,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 3: { if ("Via".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -6489,7 +6464,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 5: { if ("Allow".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -6500,18 +6475,19 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 14: { if ("Content-Length".Equals(key, StringComparison.OrdinalIgnoreCase)) { _bits |= 2048L; _ContentLength = value; + _rawContentLength = null; return; } } break; - + case 12: { if ("Content-Type".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -6522,7 +6498,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 16: { if ("Content-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -6554,7 +6530,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 11: { if ("Content-MD5".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -6572,7 +6548,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - + case 8: { if ("Location".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -6583,17 +6559,16 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } break; - } +} Unknown[key] = value; } - protected override void AddValueFast(string key, StringValues value) { switch(key.Length) { case 13: { - if ("Cache-Control".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Cache-Control".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 1L) != 0)) { @@ -6604,7 +6579,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return; } - if ("Content-Range".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Range".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 131072L) != 0)) { @@ -6615,7 +6590,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return; } - if ("Last-Modified".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Last-Modified".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 524288L) != 0)) { @@ -6626,7 +6601,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return; } - if ("Accept-Ranges".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Accept-Ranges".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 1048576L) != 0)) { @@ -6641,7 +6616,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http case 10: { - if ("Connection".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Connection".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 2L) != 0)) { @@ -6649,10 +6624,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } _bits |= 2L; _Connection = value; + _rawConnection = null; return; } - if ("Keep-Alive".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Keep-Alive".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 8L) != 0)) { @@ -6663,7 +6639,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return; } - if ("Set-Cookie".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Set-Cookie".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 134217728L) != 0)) { @@ -6678,7 +6654,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http case 4: { - if ("Date".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Date".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 4L) != 0)) { @@ -6686,10 +6662,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } _bits |= 4L; _Date = value; + _rawDate = null; return; } - if ("ETag".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("ETag".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 4194304L) != 0)) { @@ -6700,7 +6677,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return; } - if ("Vary".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Vary".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 268435456L) != 0)) { @@ -6715,7 +6692,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http case 6: { - if ("Pragma".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Pragma".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 16L) != 0)) { @@ -6726,7 +6703,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return; } - if ("Server".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Server".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 67108864L) != 0)) { @@ -6734,6 +6711,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } _bits |= 67108864L; _Server = value; + _rawServer = null; return; } } @@ -6741,7 +6719,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http case 7: { - if ("Trailer".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Trailer".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 32L) != 0)) { @@ -6752,7 +6730,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return; } - if ("Upgrade".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Upgrade".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 128L) != 0)) { @@ -6763,7 +6741,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return; } - if ("Warning".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Warning".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 512L) != 0)) { @@ -6774,7 +6752,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return; } - if ("Expires".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Expires".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 262144L) != 0)) { @@ -6789,7 +6767,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http case 17: { - if ("Transfer-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Transfer-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 64L) != 0)) { @@ -6797,10 +6775,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } _bits |= 64L; _TransferEncoding = value; + _rawTransferEncoding = null; return; } - if ("Proxy-Autheticate".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Proxy-Autheticate".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 16777216L) != 0)) { @@ -6815,7 +6794,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http case 3: { - if ("Via".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Via".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 256L) != 0)) { @@ -6826,7 +6805,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return; } - if ("Age".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Age".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 2097152L) != 0)) { @@ -6841,7 +6820,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http case 5: { - if ("Allow".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Allow".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 1024L) != 0)) { @@ -6856,7 +6835,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http case 14: { - if ("Content-Length".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Length".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 2048L) != 0)) { @@ -6864,6 +6843,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } _bits |= 2048L; _ContentLength = value; + _rawContentLength = null; return; } } @@ -6871,7 +6851,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http case 12: { - if ("Content-Type".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Type".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 4096L) != 0)) { @@ -6886,7 +6866,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http case 16: { - if ("Content-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 8192L) != 0)) { @@ -6897,7 +6877,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return; } - if ("Content-Language".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Language".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 16384L) != 0)) { @@ -6908,7 +6888,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return; } - if ("Content-Location".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Location".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 32768L) != 0)) { @@ -6919,7 +6899,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return; } - if ("WWW-Authenticate".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("WWW-Authenticate".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 536870912L) != 0)) { @@ -6934,7 +6914,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http case 11: { - if ("Content-MD5".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-MD5".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 65536L) != 0)) { @@ -6945,7 +6925,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return; } - if ("Retry-After".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Retry-After".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 33554432L) != 0)) { @@ -6960,7 +6940,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http case 8: { - if ("Location".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Location".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 8388608L) != 0)) { @@ -6975,7 +6955,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } Unknown.Add(key, value); } - protected override bool RemoveFast(string key) { switch(key.Length) @@ -7048,6 +7027,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { _bits &= ~2L; _Connection = StringValues.Empty; + _rawConnection = null; return true; } else @@ -7094,6 +7074,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { _bits &= ~4L; _Date = StringValues.Empty; + _rawDate = null; return true; } else @@ -7154,6 +7135,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { _bits &= ~67108864L; _Server = StringValues.Empty; + _rawServer = null; return true; } else @@ -7232,6 +7214,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { _bits &= ~64L; _TransferEncoding = StringValues.Empty; + _rawTransferEncoding = null; return true; } else @@ -7314,6 +7297,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { _bits &= ~2048L; _ContentLength = StringValues.Empty; + _rawContentLength = null; return true; } else @@ -7454,7 +7438,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } return MaybeUnknown?.Remove(key) ?? false; } - protected override void ClearFast() { _bits = 0; @@ -7489,6 +7472,12 @@ namespace Microsoft.AspNet.Server.Kestrel.Http _SetCookie = StringValues.Empty; _Vary = StringValues.Empty; _WWWAuthenticate = StringValues.Empty; + + _rawConnection = null; + _rawDate = null; + _rawTransferEncoding = null; + _rawContentLength = null; + _rawServer = null; MaybeUnknown?.Clear(); } @@ -7498,7 +7487,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { throw new ArgumentException(); } - if (((_bits & 1L) != 0)) { @@ -7832,7 +7820,301 @@ namespace Microsoft.AspNet.Server.Kestrel.Http ((ICollection>)MaybeUnknown)?.CopyTo(array, arrayIndex); } - + + protected void CopyToFast(ref MemoryPoolIterator2 output) + { + + if (((_bits & 1L) != 0)) + { + foreach(var value in _CacheControl) + { + output.CopyFrom(_headerBytes, 0, 17); + output.CopyFromAscii(value); + } + } + + if (((_bits & 2L) != 0)) + { + if (_rawConnection != null) + { + output.CopyFrom(_rawConnection, 0, _rawConnection.Length); + } else + foreach(var value in _Connection) + { + output.CopyFrom(_headerBytes, 17, 14); + output.CopyFromAscii(value); + } + } + + if (((_bits & 4L) != 0)) + { + if (_rawDate != null) + { + output.CopyFrom(_rawDate, 0, _rawDate.Length); + } else + foreach(var value in _Date) + { + output.CopyFrom(_headerBytes, 31, 8); + output.CopyFromAscii(value); + } + } + + if (((_bits & 8L) != 0)) + { + foreach(var value in _KeepAlive) + { + output.CopyFrom(_headerBytes, 39, 14); + output.CopyFromAscii(value); + } + } + + if (((_bits & 16L) != 0)) + { + foreach(var value in _Pragma) + { + output.CopyFrom(_headerBytes, 53, 10); + output.CopyFromAscii(value); + } + } + + if (((_bits & 32L) != 0)) + { + foreach(var value in _Trailer) + { + output.CopyFrom(_headerBytes, 63, 11); + output.CopyFromAscii(value); + } + } + + if (((_bits & 64L) != 0)) + { + if (_rawTransferEncoding != null) + { + output.CopyFrom(_rawTransferEncoding, 0, _rawTransferEncoding.Length); + } else + foreach(var value in _TransferEncoding) + { + output.CopyFrom(_headerBytes, 74, 21); + output.CopyFromAscii(value); + } + } + + if (((_bits & 128L) != 0)) + { + foreach(var value in _Upgrade) + { + output.CopyFrom(_headerBytes, 95, 11); + output.CopyFromAscii(value); + } + } + + if (((_bits & 256L) != 0)) + { + foreach(var value in _Via) + { + output.CopyFrom(_headerBytes, 106, 7); + output.CopyFromAscii(value); + } + } + + if (((_bits & 512L) != 0)) + { + foreach(var value in _Warning) + { + output.CopyFrom(_headerBytes, 113, 11); + output.CopyFromAscii(value); + } + } + + if (((_bits & 1024L) != 0)) + { + foreach(var value in _Allow) + { + output.CopyFrom(_headerBytes, 124, 9); + output.CopyFromAscii(value); + } + } + + if (((_bits & 2048L) != 0)) + { + if (_rawContentLength != null) + { + output.CopyFrom(_rawContentLength, 0, _rawContentLength.Length); + } else + foreach(var value in _ContentLength) + { + output.CopyFrom(_headerBytes, 133, 18); + output.CopyFromAscii(value); + } + } + + if (((_bits & 4096L) != 0)) + { + foreach(var value in _ContentType) + { + output.CopyFrom(_headerBytes, 151, 16); + output.CopyFromAscii(value); + } + } + + if (((_bits & 8192L) != 0)) + { + foreach(var value in _ContentEncoding) + { + output.CopyFrom(_headerBytes, 167, 20); + output.CopyFromAscii(value); + } + } + + if (((_bits & 16384L) != 0)) + { + foreach(var value in _ContentLanguage) + { + output.CopyFrom(_headerBytes, 187, 20); + output.CopyFromAscii(value); + } + } + + if (((_bits & 32768L) != 0)) + { + foreach(var value in _ContentLocation) + { + output.CopyFrom(_headerBytes, 207, 20); + output.CopyFromAscii(value); + } + } + + if (((_bits & 65536L) != 0)) + { + foreach(var value in _ContentMD5) + { + output.CopyFrom(_headerBytes, 227, 15); + output.CopyFromAscii(value); + } + } + + if (((_bits & 131072L) != 0)) + { + foreach(var value in _ContentRange) + { + output.CopyFrom(_headerBytes, 242, 17); + output.CopyFromAscii(value); + } + } + + if (((_bits & 262144L) != 0)) + { + foreach(var value in _Expires) + { + output.CopyFrom(_headerBytes, 259, 11); + output.CopyFromAscii(value); + } + } + + if (((_bits & 524288L) != 0)) + { + foreach(var value in _LastModified) + { + output.CopyFrom(_headerBytes, 270, 17); + output.CopyFromAscii(value); + } + } + + if (((_bits & 1048576L) != 0)) + { + foreach(var value in _AcceptRanges) + { + output.CopyFrom(_headerBytes, 287, 17); + output.CopyFromAscii(value); + } + } + + if (((_bits & 2097152L) != 0)) + { + foreach(var value in _Age) + { + output.CopyFrom(_headerBytes, 304, 7); + output.CopyFromAscii(value); + } + } + + if (((_bits & 4194304L) != 0)) + { + foreach(var value in _ETag) + { + output.CopyFrom(_headerBytes, 311, 8); + output.CopyFromAscii(value); + } + } + + if (((_bits & 8388608L) != 0)) + { + foreach(var value in _Location) + { + output.CopyFrom(_headerBytes, 319, 12); + output.CopyFromAscii(value); + } + } + + if (((_bits & 16777216L) != 0)) + { + foreach(var value in _ProxyAutheticate) + { + output.CopyFrom(_headerBytes, 331, 21); + output.CopyFromAscii(value); + } + } + + if (((_bits & 33554432L) != 0)) + { + foreach(var value in _RetryAfter) + { + output.CopyFrom(_headerBytes, 352, 15); + output.CopyFromAscii(value); + } + } + + if (((_bits & 67108864L) != 0)) + { + if (_rawServer != null) + { + output.CopyFrom(_rawServer, 0, _rawServer.Length); + } else + foreach(var value in _Server) + { + output.CopyFrom(_headerBytes, 367, 10); + output.CopyFromAscii(value); + } + } + + if (((_bits & 134217728L) != 0)) + { + foreach(var value in _SetCookie) + { + output.CopyFrom(_headerBytes, 377, 14); + output.CopyFromAscii(value); + } + } + + if (((_bits & 268435456L) != 0)) + { + foreach(var value in _Vary) + { + output.CopyFrom(_headerBytes, 391, 8); + output.CopyFromAscii(value); + } + } + + if (((_bits & 536870912L) != 0)) + { + foreach(var value in _WWWAuthenticate) + { + output.CopyFrom(_headerBytes, 399, 20); + output.CopyFromAscii(value); + } + } + + } public unsafe void Append(byte[] keyBytes, int keyOffset, int keyLength, string value) { fixed(byte* ptr = keyBytes) { var pUB = ptr + keyOffset; var pUL = (ulong*)pUB; var pUI = (uint*)pUB; var pUS = (ushort*)pUB; @@ -7910,6 +8192,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { _bits |= 2L; _Connection = new StringValues(value); + _rawConnection = null; } return; } @@ -7956,6 +8239,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { _bits |= 4L; _Date = new StringValues(value); + _rawDate = null; } return; } @@ -8016,6 +8300,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { _bits |= 67108864L; _Server = new StringValues(value); + _rawServer = null; } return; } @@ -8094,6 +8379,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { _bits |= 64L; _TransferEncoding = new StringValues(value); + _rawTransferEncoding = null; } return; } @@ -8176,6 +8462,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { _bits |= 2048L; _ContentLength = new StringValues(value); + _rawContentLength = null; } return; } @@ -8315,7 +8602,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Http Unknown.TryGetValue(key, out existing); Unknown[key] = AppendValue(existing, value); } - public partial struct Enumerator { public bool MoveNext() diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/FrameResponseHeaders.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameResponseHeaders.cs index 725a647567..86c2a18554 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/FrameResponseHeaders.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameResponseHeaders.cs @@ -1,14 +1,25 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using Microsoft.Extensions.Primitives; using System.Collections; using System.Collections.Generic; +using Microsoft.AspNet.Server.Kestrel.Infrastructure; +using Microsoft.Extensions.Primitives; namespace Microsoft.AspNet.Server.Kestrel.Http { public partial class FrameResponseHeaders : FrameHeaders { + private static byte[] _CRLF = new[] { (byte)'\r', (byte)'\n' }; + private static byte[] _colonSpace = new[] { (byte)':', (byte)' ' }; + + public bool HasConnection => HeaderConnection.Count != 0; + + public bool HasTransferEncoding => HeaderTransferEncoding.Count != 0; + + public bool HasContentLength => HeaderContentLength.Count != 0; + + public Enumerator GetEnumerator() { return new Enumerator(this); @@ -19,6 +30,24 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return GetEnumerator(); } + public void CopyTo(ref MemoryPoolIterator2 output) + { + CopyToFast(ref output); + if (MaybeUnknown != null) + { + foreach (var kv in MaybeUnknown) + { + foreach (var value in kv.Value) + { + output.CopyFrom(_CRLF, 0, 2); + output.CopyFromAscii(kv.Key); + output.CopyFrom(_colonSpace, 0, 2); + output.CopyFromAscii(value); + } + } + } + } + public partial struct Enumerator : IEnumerator> { private FrameResponseHeaders _collection; diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/MemoryPool.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/MemoryPool.cs deleted file mode 100644 index 0fc5c390e1..0000000000 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/MemoryPool.cs +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.Collections.Generic; - -namespace Microsoft.AspNet.Server.Kestrel.Http -{ - public class MemoryPool : IMemoryPool - { - private static readonly byte[] EmptyArray = new byte[0]; - - private readonly Pool _pool1 = new Pool(); - private readonly Pool _pool2 = new Pool(); - private readonly Pool _pool3 = new Pool(); - - public byte[] Empty - { - get - { - return EmptyArray; - } - } - - public byte[] AllocByte(int minimumSize) - { - if (minimumSize == 0) - { - return EmptyArray; - } - if (minimumSize <= 1024) - { - return _pool1.Alloc(1024); - } - if (minimumSize <= 2048) - { - return _pool2.Alloc(2048); - } - return new byte[minimumSize]; - } - - public void FreeByte(byte[] memory) - { - if (memory == null) - { - return; - } - switch (memory.Length) - { - case 1024: - _pool1.Free(memory, 256); - break; - case 2048: - _pool2.Free(memory, 64); - break; - } - } - - public char[] AllocChar(int minimumSize) - { - if (minimumSize == 0) - { - return new char[0]; - } - if (minimumSize <= 128) - { - return _pool3.Alloc(128); - } - return new char[minimumSize]; - } - - public void FreeChar(char[] memory) - { - if (memory == null) - { - return; - } - switch (memory.Length) - { - case 128: - _pool3.Free(memory, 256); - break; - } - } - - public ArraySegment AllocSegment(int minimumSize) - { - return new ArraySegment(AllocByte(minimumSize)); - } - - public void FreeSegment(ArraySegment segment) - { - FreeByte(segment.Array); - } - - class Pool - { - private readonly Stack _stack = new Stack(); - private readonly object _sync = new object(); - - public T[] Alloc(int size) - { - lock (_sync) - { - if (_stack.Count != 0) - { - return _stack.Pop(); - } - } - return new T[size]; - } - - public void Free(T[] value, int limit) - { - lock (_sync) - { - if (_stack.Count < limit) - { - _stack.Push(value); - } - } - } - } - } -} diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/MemoryPoolTextWriter.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/MemoryPoolTextWriter.cs deleted file mode 100644 index 2f549dc157..0000000000 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/MemoryPoolTextWriter.cs +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System; -using System.IO; -using System.Text; - -namespace Microsoft.AspNet.Server.Kestrel.Http -{ - public class MemoryPoolTextWriter : TextWriter - { - private readonly IMemoryPool _memory; - - private char[] _textArray; - private int _textBegin; - private int _textEnd; - // ReSharper disable InconsistentNaming - private const int _textLength = 128; - // ReSharper restore InconsistentNaming - - private byte[] _dataArray; - private int _dataEnd; - - private readonly Encoder _encoder; - - public MemoryPoolTextWriter(IMemoryPool memory) - { - _memory = memory; - _textArray = _memory.AllocChar(_textLength); - _dataArray = _memory.Empty; - _encoder = Encoding.UTF8.GetEncoder(); - } - - public ArraySegment Buffer - { - get - { - return new ArraySegment(_dataArray, 0, _dataEnd); - } - } - - public override Encoding Encoding - { - get - { - return Encoding.UTF8; - } - } - - protected override void Dispose(bool disposing) - { - try - { - if (disposing) - { - if (_textArray != null) - { - _memory.FreeChar(_textArray); - _textArray = null; - } - if (_dataArray != null) - { - _memory.FreeByte(_dataArray); - _dataArray = null; - } - } - } - finally - { - base.Dispose(disposing); - } - } - - private void Encode(bool flush) - { - var bytesNeeded = _encoder.GetByteCount( - _textArray, - _textBegin, - _textEnd - _textBegin, - flush); - - Grow(bytesNeeded); - - var bytesUsed = _encoder.GetBytes( - _textArray, - _textBegin, - _textEnd - _textBegin, - _dataArray, - _dataEnd, - flush); - - _textBegin = _textEnd = 0; - _dataEnd += bytesUsed; - } - - private void Grow(int minimumAvailable) - { - if (_dataArray.Length - _dataEnd >= minimumAvailable) - { - return; - } - - var newLength = _dataArray.Length + Math.Max(_dataArray.Length, minimumAvailable); - var newArray = _memory.AllocByte(newLength); - Array.Copy(_dataArray, 0, newArray, 0, _dataEnd); - _memory.FreeByte(_dataArray); - _dataArray = newArray; - } - - public override void Write(char value) - { - if (_textLength == _textEnd) - { - Encode(false); - if (_textLength == _textEnd) - { - throw new InvalidOperationException("Unexplainable failure to encode text"); - } - } - - _textArray[_textEnd++] = value; - } - - public override void Write(string value) - { - var sourceIndex = 0; - var sourceLength = value.Length; - while (sourceIndex < sourceLength) - { - if (_textLength == _textEnd) - { - Encode(false); - } - - var count = sourceLength - sourceIndex; - if (count > _textLength - _textEnd) - { - count = _textLength - _textEnd; - } - - value.CopyTo(sourceIndex, _textArray, _textEnd, count); - sourceIndex += count; - _textEnd += count; - } - } - - public override void Flush() - { - while (_textBegin != _textEnd) - { - Encode(true); - } - } - } -} diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/ReasonPhrases.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/ReasonPhrases.cs index fe8fa6e19e..9afbe88d6c 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/ReasonPhrases.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/ReasonPhrases.cs @@ -2,11 +2,65 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Globalization; +using System.Text; namespace Microsoft.AspNet.Server.Kestrel.Http { public static class ReasonPhrases { + private static readonly byte[] _bytesStatus100 = Encoding.ASCII.GetBytes("100 Continue"); + private static readonly byte[] _bytesStatus101 = Encoding.ASCII.GetBytes("101 Switching Protocols"); + private static readonly byte[] _bytesStatus102 = Encoding.ASCII.GetBytes("102 Processing"); + private static readonly byte[] _bytesStatus200 = Encoding.ASCII.GetBytes("200 OK"); + private static readonly byte[] _bytesStatus201 = Encoding.ASCII.GetBytes("201 Created"); + private static readonly byte[] _bytesStatus202 = Encoding.ASCII.GetBytes("202 Accepted"); + private static readonly byte[] _bytesStatus203 = Encoding.ASCII.GetBytes("203 Non-Authoritative Information"); + private static readonly byte[] _bytesStatus204 = Encoding.ASCII.GetBytes("204 No Content"); + private static readonly byte[] _bytesStatus205 = Encoding.ASCII.GetBytes("205 Reset Content"); + private static readonly byte[] _bytesStatus206 = Encoding.ASCII.GetBytes("206 Partial Content"); + private static readonly byte[] _bytesStatus207 = Encoding.ASCII.GetBytes("207 Multi-Status"); + private static readonly byte[] _bytesStatus226 = Encoding.ASCII.GetBytes("226 IM Used"); + private static readonly byte[] _bytesStatus300 = Encoding.ASCII.GetBytes("300 Multiple Choices"); + private static readonly byte[] _bytesStatus301 = Encoding.ASCII.GetBytes("301 Moved Permanently"); + private static readonly byte[] _bytesStatus302 = Encoding.ASCII.GetBytes("302 Found"); + private static readonly byte[] _bytesStatus303 = Encoding.ASCII.GetBytes("303 See Other"); + private static readonly byte[] _bytesStatus304 = Encoding.ASCII.GetBytes("304 Not Modified"); + private static readonly byte[] _bytesStatus305 = Encoding.ASCII.GetBytes("305 Use Proxy"); + private static readonly byte[] _bytesStatus306 = Encoding.ASCII.GetBytes("306 Reserved"); + private static readonly byte[] _bytesStatus307 = Encoding.ASCII.GetBytes("307 Temporary Redirect"); + private static readonly byte[] _bytesStatus400 = Encoding.ASCII.GetBytes("400 Bad Request"); + private static readonly byte[] _bytesStatus401 = Encoding.ASCII.GetBytes("401 Unauthorized"); + private static readonly byte[] _bytesStatus402 = Encoding.ASCII.GetBytes("402 Payment Required"); + private static readonly byte[] _bytesStatus403 = Encoding.ASCII.GetBytes("403 Forbidden"); + private static readonly byte[] _bytesStatus404 = Encoding.ASCII.GetBytes("404 Not Found"); + private static readonly byte[] _bytesStatus405 = Encoding.ASCII.GetBytes("405 Method Not Allowed"); + private static readonly byte[] _bytesStatus406 = Encoding.ASCII.GetBytes("406 Not Acceptable"); + private static readonly byte[] _bytesStatus407 = Encoding.ASCII.GetBytes("407 Proxy Authentication Required"); + private static readonly byte[] _bytesStatus408 = Encoding.ASCII.GetBytes("408 Request Timeout"); + private static readonly byte[] _bytesStatus409 = Encoding.ASCII.GetBytes("409 Conflict"); + private static readonly byte[] _bytesStatus410 = Encoding.ASCII.GetBytes("410 Gone"); + private static readonly byte[] _bytesStatus411 = Encoding.ASCII.GetBytes("411 Length Required"); + private static readonly byte[] _bytesStatus412 = Encoding.ASCII.GetBytes("412 Precondition Failed"); + private static readonly byte[] _bytesStatus413 = Encoding.ASCII.GetBytes("413 Request Entity Too Large"); + private static readonly byte[] _bytesStatus414 = Encoding.ASCII.GetBytes("414 Request-URI Too Long"); + private static readonly byte[] _bytesStatus415 = Encoding.ASCII.GetBytes("415 Unsupported Media Type"); + private static readonly byte[] _bytesStatus416 = Encoding.ASCII.GetBytes("416 Requested Range Not Satisfiable"); + private static readonly byte[] _bytesStatus417 = Encoding.ASCII.GetBytes("417 Expectation Failed"); + private static readonly byte[] _bytesStatus418 = Encoding.ASCII.GetBytes("418 I'm a Teapot"); + private static readonly byte[] _bytesStatus422 = Encoding.ASCII.GetBytes("422 Unprocessable Entity"); + private static readonly byte[] _bytesStatus423 = Encoding.ASCII.GetBytes("423 Locked"); + private static readonly byte[] _bytesStatus424 = Encoding.ASCII.GetBytes("424 Failed Dependency"); + private static readonly byte[] _bytesStatus426 = Encoding.ASCII.GetBytes("426 Upgrade Required"); + private static readonly byte[] _bytesStatus500 = Encoding.ASCII.GetBytes("500 Internal Server Error"); + private static readonly byte[] _bytesStatus501 = Encoding.ASCII.GetBytes("501 Not Implemented"); + private static readonly byte[] _bytesStatus502 = Encoding.ASCII.GetBytes("502 Bad Gateway"); + private static readonly byte[] _bytesStatus503 = Encoding.ASCII.GetBytes("503 Service Unavailable"); + private static readonly byte[] _bytesStatus504 = Encoding.ASCII.GetBytes("504 Gateway Timeout"); + private static readonly byte[] _bytesStatus505 = Encoding.ASCII.GetBytes("505 HTTP Version Not Supported"); + private static readonly byte[] _bytesStatus506 = Encoding.ASCII.GetBytes("506 Variant Also Negotiates"); + private static readonly byte[] _bytesStatus507 = Encoding.ASCII.GetBytes("507 Insufficient Storage"); + private static readonly byte[] _bytesStatus510 = Encoding.ASCII.GetBytes("510 Not Extended"); + public static string ToStatus(int statusCode, string reasonPhrase = null) { if (string.IsNullOrEmpty(reasonPhrase)) @@ -16,6 +70,123 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return statusCode.ToString(CultureInfo.InvariantCulture) + " " + reasonPhrase; } + public static byte[] ToStatusBytes(int statusCode, string reasonPhrase = null) + { + if (string.IsNullOrEmpty(reasonPhrase)) + { + switch (statusCode) + { + case 100: + return _bytesStatus100; + case 101: + return _bytesStatus101; + case 102: + return _bytesStatus102; + case 200: + return _bytesStatus200; + case 201: + return _bytesStatus201; + case 202: + return _bytesStatus202; + case 203: + return _bytesStatus203; + case 204: + return _bytesStatus204; + case 205: + return _bytesStatus205; + case 206: + return _bytesStatus206; + case 207: + return _bytesStatus207; + case 226: + return _bytesStatus226; + case 300: + return _bytesStatus300; + case 301: + return _bytesStatus301; + case 302: + return _bytesStatus302; + case 303: + return _bytesStatus303; + case 304: + return _bytesStatus304; + case 305: + return _bytesStatus305; + case 306: + return _bytesStatus306; + case 307: + return _bytesStatus307; + case 400: + return _bytesStatus400; + case 401: + return _bytesStatus401; + case 402: + return _bytesStatus402; + case 403: + return _bytesStatus403; + case 404: + return _bytesStatus404; + case 405: + return _bytesStatus405; + case 406: + return _bytesStatus406; + case 407: + return _bytesStatus407; + case 408: + return _bytesStatus408; + case 409: + return _bytesStatus409; + case 410: + return _bytesStatus410; + case 411: + return _bytesStatus411; + case 412: + return _bytesStatus412; + case 413: + return _bytesStatus413; + case 414: + return _bytesStatus414; + case 415: + return _bytesStatus415; + case 416: + return _bytesStatus416; + case 417: + return _bytesStatus417; + case 418: + return _bytesStatus418; + case 422: + return _bytesStatus422; + case 423: + return _bytesStatus423; + case 424: + return _bytesStatus424; + case 426: + return _bytesStatus426; + case 500: + return _bytesStatus500; + case 501: + return _bytesStatus501; + case 502: + return _bytesStatus502; + case 503: + return _bytesStatus503; + case 504: + return _bytesStatus504; + case 505: + return _bytesStatus505; + case 506: + return _bytesStatus506; + case 507: + return _bytesStatus507; + case 510: + return _bytesStatus510; + default: + return Encoding.ASCII.GetBytes(statusCode.ToString(CultureInfo.InvariantCulture) + " Unknown"); + } + } + return Encoding.ASCII.GetBytes(statusCode.ToString(CultureInfo.InvariantCulture) + " " + reasonPhrase); + } + public static string ToReasonPhrase(int statusCode) { switch (statusCode) @@ -242,4 +413,4 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } } -} +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPool2.cs b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPool2.cs index 446f03c2b3..5c9ce81f69 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPool2.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPool2.cs @@ -30,6 +30,12 @@ namespace Microsoft.AspNet.Server.Kestrel.Infrastructure /// 4096 - 64 gives you a blockLength of 4032 usable bytes per block. /// private const int _blockLength = _blockStride - _blockUnused; + + /// + /// Max allocation block size for pooled blocks, + /// larger values can be leased but they will be disposed after use rather than returned to the pool. + /// + public const int MaxPooledBlockLength = _blockLength; /// /// 4096 * 32 gives you a slabLength of 128k contiguous bytes allocated per slab @@ -59,7 +65,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Infrastructure /// The block returned must be at least this size. It may be larger than this minimum size, and if so, /// the caller may write to the block's entire size rather than being limited to the minumumSize requested. /// The block that is reserved for the called. It must be passed to Return when it is no longer being used. - public MemoryPoolBlock2 Lease(int minimumSize = _blockLength) + public MemoryPoolBlock2 Lease(int minimumSize = MaxPooledBlockLength) { if (minimumSize > _blockLength) { diff --git a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolIterator2.cs b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolIterator2.cs index 6c5c1f205a..a3b5e678c5 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolIterator2.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolIterator2.cs @@ -572,5 +572,109 @@ namespace Microsoft.AspNet.Server.Kestrel.Infrastructure return new MemoryPoolIterator2(block, blockIndex); } + + public void CopyFrom(byte[] data) + { + CopyFrom(data, 0, data.Length); + } + + public void CopyFrom(byte[] data, int offset, int count) + { + Debug.Assert(_block.Next == null); + Debug.Assert(_block.End == _index); + + var block = _block; + + var sourceData = data; + var sourceStart = offset; + var sourceEnd = offset + count; + + var targetData = block.Array; + var targetStart = block.End; + var targetEnd = block.Data.Offset + block.Data.Count; + + while (true) + { + // actual count to copy is remaining data, or unused trailing space in the current block, whichever is smaller + var copyCount = Math.Min(sourceEnd - sourceStart, targetEnd - targetStart); + + Buffer.BlockCopy(sourceData, sourceStart, targetData, targetStart, copyCount); + sourceStart += copyCount; + targetStart += copyCount; + + // if this means all source data has been copied + if (sourceStart == sourceEnd) + { + // increase occupied space in the block, and adjust iterator at start of unused trailing space + block.End = targetStart; + _block = block; + _index = targetStart; + return; + } + + // otherwise another block needs to be allocated to follow this one + block.Next = block.Pool.Lease(); + block = block.Next; + + targetData = block.Array; + targetStart = block.End; + targetEnd = block.Data.Offset + block.Data.Count; + } + } + + public unsafe void CopyFromAscii(string data) + { + Debug.Assert(_block.Next == null); + Debug.Assert(_block.End == _index); + + var block = _block; + + var inputLength = data.Length; + var inputLengthMinusSpan = inputLength - 3; + + fixed (char* pData = data) + { + var input = pData; + var inputEnd = pData + data.Length; + var blockRemaining = block.Data.Offset + block.Data.Count - block.End; + var blockRemainingMinusSpan = blockRemaining - 3; + + while (input < inputEnd) + { + if (blockRemaining == 0) + { + block.Next = block.Pool.Lease(); + block = block.Next; + blockRemaining = block.Data.Count; + blockRemainingMinusSpan = blockRemaining - 3; + } + + fixed (byte* pOutput = block.Data.Array) + { + var output = pOutput + block.End; + + var copied = 0; + for (; copied < inputLengthMinusSpan && copied < blockRemainingMinusSpan; copied += 4) + { + *(output) = (byte)*(input); + *(output + 1) = (byte)*(input + 1); + *(output + 2) = (byte)*(input + 2); + *(output + 3) = (byte)*(input + 3); + output += 4; + input += 4; + blockRemainingMinusSpan -= 4; + } + for (; copied < inputLength && copied < blockRemaining; copied++) + { + *(output++) = (byte)*(input++); + blockRemaining--; + } + block.End += copied; + _block = block; + _index = block.End; + } + } + } + } } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/ServiceContext.cs b/src/Microsoft.AspNet.Server.Kestrel/ServiceContext.cs index d3d1539a59..095f37f856 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/ServiceContext.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/ServiceContext.cs @@ -13,13 +13,11 @@ namespace Microsoft.AspNet.Server.Kestrel { public ServiceContext() { - Memory = new MemoryPool(); } public ServiceContext(ServiceContext context) { AppLifetime = context.AppLifetime; - Memory = context.Memory; Log = context.Log; HttpContextFactory = context.HttpContextFactory; DateHeaderValueManager = context.DateHeaderValueManager; @@ -29,8 +27,6 @@ namespace Microsoft.AspNet.Server.Kestrel public IApplicationLifetime AppLifetime { get; set; } - public IMemoryPool Memory { get; set; } - public IKestrelTrace Log { get; set; } public IHttpContextFactory HttpContextFactory { get; set; } diff --git a/test/Microsoft.AspNet.Server.KestrelTests/EngineTests.cs b/test/Microsoft.AspNet.Server.KestrelTests/EngineTests.cs index f19b2b3077..afc30af8a9 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/EngineTests.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/EngineTests.cs @@ -171,8 +171,8 @@ namespace Microsoft.AspNet.Server.KestrelTests "Content-Length: 0", "", "HTTP/1.1 200 OK", - "Content-Length: 7", "Connection: close", + "Content-Length: 7", "", "Goodbye"); } @@ -241,8 +241,8 @@ namespace Microsoft.AspNet.Server.KestrelTests "Goodbye"); await connection.Receive( "HTTP/1.0 200 OK", - "Content-Length: 0", "Connection: keep-alive", + "Content-Length: 0", "\r\n"); await connection.ReceiveEnd( "HTTP/1.0 200 OK", @@ -267,14 +267,14 @@ namespace Microsoft.AspNet.Server.KestrelTests "Connection: keep-alive", "", "POST / HTTP/1.0", - "Connection: keep-alive", "Content-Length: 7", + "Connection: keep-alive", "", "Goodbye"); await connection.Receive( "HTTP/1.0 200 OK", - "Content-Length: 0", "Connection: keep-alive", + "Content-Length: 0", "\r\n"); await connection.ReceiveEnd( "HTTP/1.0 200 OK", @@ -295,16 +295,16 @@ namespace Microsoft.AspNet.Server.KestrelTests { await connection.SendEnd( "POST / HTTP/1.0", - "Connection: keep-alive", "Content-Length: 11", + "Connection: keep-alive", "", "Hello WorldPOST / HTTP/1.0", "", "Goodbye"); await connection.Receive( "HTTP/1.0 200 OK", - "Content-Length: 11", "Connection: keep-alive", + "Content-Length: 11", "", "Hello World"); await connection.ReceiveEnd( @@ -336,8 +336,8 @@ namespace Microsoft.AspNet.Server.KestrelTests "Goodbye"); await connection.Receive( "HTTP/1.0 200 OK", - "Content-Length: 11", "Connection: keep-alive", + "Content-Length: 11", "", "Hello World"); await connection.ReceiveEnd( @@ -361,15 +361,15 @@ namespace Microsoft.AspNet.Server.KestrelTests await connection.Send( "POST / HTTP/1.1", "Expect: 100-continue", - "Content-Length: 11", "Connection: close", + "Content-Length: 11", "\r\n"); await connection.Receive("HTTP/1.1 100 Continue", "\r\n"); await connection.SendEnd("Hello World"); await connection.Receive( "HTTP/1.1 200 OK", - "Content-Length: 11", "Connection: close", + "Content-Length: 11", "", "Hello World"); } @@ -422,8 +422,8 @@ namespace Microsoft.AspNet.Server.KestrelTests "Content-Length: 0", "", "HTTP/1.0 200 OK", - "Content-Length: 0", "Connection: keep-alive", + "Content-Length: 0", "", ""); } @@ -584,11 +584,12 @@ namespace Microsoft.AspNet.Server.KestrelTests "", "HTTP/1.1 500 Internal Server Error", ""); + await connection.Receive("Connection: close", + ""); await connection.ReceiveStartsWith("Date:"); await connection.ReceiveEnd( "Content-Length: 0", "Server: Kestrel", - "Connection: close", "", ""); @@ -812,12 +813,12 @@ namespace Microsoft.AspNet.Server.KestrelTests "Server: Kestrel", "", "HTTP/1.1 500 Internal Server Error", + "Connection: close", ""); await connection.ReceiveStartsWith("Date:"); await connection.ReceiveEnd( "Content-Length: 0", "Server: Kestrel", - "Connection: close", "", ""); diff --git a/test/Microsoft.AspNet.Server.KestrelTests/TestInput.cs b/test/Microsoft.AspNet.Server.KestrelTests/TestInput.cs index 24cf1cdb77..a440ea0edf 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/TestInput.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/TestInput.cs @@ -13,12 +13,10 @@ namespace Microsoft.AspNet.Server.KestrelTests { public TestInput() { - var memory = new MemoryPool(); var memory2 = new MemoryPool2(); FrameContext = new FrameContext { SocketInput = new SocketInput(memory2), - Memory = memory, ConnectionControl = this, FrameControl = this }; diff --git a/tools/Microsoft.AspNet.Server.Kestrel.GeneratedCode/KnownHeaders.cs b/tools/Microsoft.AspNet.Server.Kestrel.GeneratedCode/KnownHeaders.cs index b2def16b46..ab20b6c2d9 100644 --- a/tools/Microsoft.AspNet.Server.Kestrel.GeneratedCode/KnownHeaders.cs +++ b/tools/Microsoft.AspNet.Server.Kestrel.GeneratedCode/KnownHeaders.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; using Microsoft.Dnx.Compilation.CSharp; +using System.Text; namespace Microsoft.AspNet.Server.Kestrel.GeneratedCode { @@ -11,7 +12,7 @@ namespace Microsoft.AspNet.Server.Kestrel.GeneratedCode { static string Each(IEnumerable values, Func formatter) { - return values.Select(formatter).Aggregate((a, b) => a + b); + return values.Any() ? values.Select(formatter).Aggregate((a, b) => a + b) : ""; } class KnownHeader @@ -19,10 +20,14 @@ namespace Microsoft.AspNet.Server.Kestrel.GeneratedCode public string Name { get; set; } public int Index { get; set; } public string Identifier => Name.Replace("-", ""); + + public byte[] Bytes => Encoding.ASCII.GetBytes($"\r\n{Name}: "); + public int BytesOffset { get; set; } + public int BytesCount { get; set; } + public bool EnhancedSetter { get; set; } public string TestBit() => $"((_bits & {1L << Index}L) != 0)"; public string SetBit() => $"_bits |= {1L << Index}L"; public string ClearBit() => $"_bits &= ~{1L << Index}L"; - public string EqualIgnoreCaseBytes() { var result = ""; @@ -54,7 +59,6 @@ namespace Microsoft.AspNet.Server.Kestrel.GeneratedCode } return $"({result})"; } - protected string Term(string name, int offset, int count, string array, string suffix) { ulong mask = 0; @@ -69,13 +73,11 @@ namespace Microsoft.AspNet.Server.Kestrel.GeneratedCode return $"(({array}[{offset / count}] & {mask}{suffix}) == {comp}{suffix})"; } } - public virtual void BeforeCompile(BeforeCompileContext context) { var syntaxTree = Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.ParseText(GeneratedFile()); context.Compilation = context.Compilation.AddSyntaxTrees(syntaxTree); } - public static string GeneratedFile() { var commonHeaders = new[] @@ -128,8 +130,15 @@ namespace Microsoft.AspNet.Server.Kestrel.GeneratedCode { Name = header, Index = index - }); - + }).ToArray(); + var enhancedHeaders = new[] + { + "Connection", + "Server", + "Date", + "Transfer-Encoding", + "Content-Length", + }; var responseHeaders = commonHeaders.Concat(new[] { "Accept-Ranges", @@ -145,38 +154,60 @@ namespace Microsoft.AspNet.Server.Kestrel.GeneratedCode }).Select((header, index) => new KnownHeader { Name = header, - Index = index - }); - + Index = index, + EnhancedSetter = enhancedHeaders.Contains(header) + }).ToArray(); var loops = new[] { new { Headers = requestHeaders, HeadersByLength = requestHeaders.GroupBy(x => x.Name.Length), - ClassName = "FrameRequestHeaders" + ClassName = "FrameRequestHeaders", + Bytes = default(byte[]) }, new { Headers = responseHeaders, HeadersByLength = responseHeaders.GroupBy(x => x.Name.Length), - ClassName = "FrameResponseHeaders" + ClassName = "FrameResponseHeaders", + Bytes = responseHeaders.SelectMany(header => header.Bytes).ToArray() } }; - + foreach (var loop in loops.Where(l => l.Bytes != null)) + { + var offset = 0; + foreach (var header in loop.Headers) + { + header.BytesOffset = offset; + header.BytesCount += header.Bytes.Length; + offset += header.BytesCount; + } + } return $@" using System; using System.Collections.Generic; +using System.Text; +using Microsoft.AspNet.Server.Kestrel.Infrastructure; using Microsoft.Extensions.Primitives; namespace Microsoft.AspNet.Server.Kestrel.Http {{ {Each(loops, loop => $@" - public partial class {loop.ClassName} - {{ + public partial class {loop.ClassName} + {{{(loop.Bytes != null ? + $@" + private static byte[] _headerBytes = new byte[] + {{ + {Each(loop.Bytes, b => $"{b},")} + }};" + : "")} + private long _bits = 0; {Each(loop.Headers, header => @" private StringValues _" + header.Identifier + ";")} + {Each(loop.Headers.Where(header => header.EnhancedSetter), header => @" + private byte[] _raw" + header.Identifier + ";")} {Each(loop.Headers, header => $@" public StringValues Header{header.Identifier} {{ @@ -187,22 +218,28 @@ namespace Microsoft.AspNet.Server.Kestrel.Http set {{ {header.SetBit()}; - _{header.Identifier} = value; + _{header.Identifier} = value; {(header.EnhancedSetter == false ? "" : $@" + _raw{header.Identifier} = null;")} }} - }} - ")} + }}")} + {Each(loop.Headers.Where(header => header.EnhancedSetter), header => $@" + public void SetRaw{header.Identifier}(StringValues value, byte[] raw) + {{ + {header.SetBit()}; + _{header.Identifier} = value; + _raw{header.Identifier} = raw; + }}")} protected override int GetCountFast() {{ return BitCount(_bits) + (MaybeUnknown?.Count ?? 0); }} - protected override StringValues GetValueFast(string key) {{ switch(key.Length) {{{Each(loop.HeadersByLength, byLength => $@" case {byLength.Key}: {{{Each(byLength, header => $@" - if (""{header.Name}"".Equals(key, StringComparison.OrdinalIgnoreCase)) + if (""{header.Name}"".Equals(key, StringComparison.OrdinalIgnoreCase)) {{ if ({header.TestBit()}) {{ @@ -215,14 +252,13 @@ namespace Microsoft.AspNet.Server.Kestrel.Http }} ")}}} break; - ")}}} +")}}} if (MaybeUnknown == null) {{ throw new System.Collections.Generic.KeyNotFoundException(); }} return MaybeUnknown[key]; }} - protected override bool TryGetValueFast(string key, out StringValues value) {{ switch(key.Length) @@ -244,11 +280,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http }} ")}}} break; - ")}}} +")}}} value = StringValues.Empty; return MaybeUnknown?.TryGetValue(key, out value) ?? false; }} - protected override void SetValueFast(string key, StringValues value) {{ switch(key.Length) @@ -258,29 +293,30 @@ namespace Microsoft.AspNet.Server.Kestrel.Http if (""{header.Name}"".Equals(key, StringComparison.OrdinalIgnoreCase)) {{ {header.SetBit()}; - _{header.Identifier} = value; + _{header.Identifier} = value;{(header.EnhancedSetter == false ? "" : $@" + _raw{header.Identifier} = null;")} return; }} ")}}} break; - ")}}} +")}}} Unknown[key] = value; }} - protected override void AddValueFast(string key, StringValues value) {{ switch(key.Length) {{{Each(loop.HeadersByLength, byLength => $@" case {byLength.Key}: {{{Each(byLength, header => $@" - if (""{header.Name}"".Equals(key, StringComparison.OrdinalIgnoreCase)) + if (""{header.Name}"".Equals(key, StringComparison.OrdinalIgnoreCase)) {{ if ({header.TestBit()}) {{ throw new ArgumentException(""An item with the same key has already been added.""); }} {header.SetBit()}; - _{header.Identifier} = value; + _{header.Identifier} = value;{(header.EnhancedSetter == false ? "" : $@" + _raw{header.Identifier} = null;")} return; }} ")}}} @@ -288,7 +324,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Http ")}}} Unknown.Add(key, value); }} - protected override bool RemoveFast(string key) {{ switch(key.Length) @@ -300,7 +335,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http if ({header.TestBit()}) {{ {header.ClearBit()}; - _{header.Identifier} = StringValues.Empty; + _{header.Identifier} = StringValues.Empty;{(header.EnhancedSetter == false ? "" : $@" + _raw{header.Identifier} = null;")} return true; }} else @@ -313,12 +349,13 @@ namespace Microsoft.AspNet.Server.Kestrel.Http ")}}} return MaybeUnknown?.Remove(key) ?? false; }} - protected override void ClearFast() {{ _bits = 0; {Each(loop.Headers, header => $@" _{header.Identifier} = StringValues.Empty;")} + {Each(loop.Headers.Where(header => header.EnhancedSetter), header => $@" + _raw{header.Identifier} = null;")} MaybeUnknown?.Clear(); }} @@ -328,7 +365,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Http {{ throw new ArgumentException(); }} - {Each(loop.Headers, header => $@" if ({header.TestBit()}) {{ @@ -343,7 +379,24 @@ namespace Microsoft.AspNet.Server.Kestrel.Http ")} ((ICollection>)MaybeUnknown)?.CopyTo(array, arrayIndex); }} - + {(loop.ClassName == "FrameResponseHeaders" ? $@" + protected void CopyToFast(ref MemoryPoolIterator2 output) + {{ + {Each(loop.Headers, header => $@" + if ({header.TestBit()}) + {{ {(header.EnhancedSetter == false ? "" : $@" + if (_raw{header.Identifier} != null) + {{ + output.CopyFromAscii(_raw{header.Identifier}, 0, _raw{header.Identifier}.Length); + }} else ")} + foreach(var value in _{header.Identifier}) + {{ + output.CopyFromAscii(_headerBytes, {header.BytesOffset}, {header.BytesCount}); + output.CopyFromAscii(value); + }} + }} + ")} + }}" : "")} public unsafe void Append(byte[] keyBytes, int keyOffset, int keyLength, string value) {{ fixed(byte* ptr = keyBytes) {{ var pUB = ptr + keyOffset; var pUL = (ulong*)pUB; var pUI = (uint*)pUB; var pUS = (ushort*)pUB; @@ -360,7 +413,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http else {{ {header.SetBit()}; - _{header.Identifier} = new StringValues(value); + _{header.Identifier} = new StringValues(value);{(header.EnhancedSetter == false ? "" : $@" + _raw{header.Identifier} = null;")} }} return; }} @@ -372,7 +426,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Http Unknown.TryGetValue(key, out existing); Unknown[key] = AppendValue(existing, value); }} - public partial struct Enumerator {{ public bool MoveNext() @@ -409,9 +462,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http ")}}} "; } - public virtual void AfterCompile(AfterCompileContext context) { } } -} +} \ No newline at end of file From feb40402de27de1d6abe0c5ac3eb6e85dbf17d56 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Fri, 20 Nov 2015 21:27:59 +0000 Subject: [PATCH 4/5] merge preamble-output, socket-output --- .../Filter/LibuvStream.cs | 20 ++- .../Filter/StreamSocketOutput.cs | 2 +- .../Http/Frame.cs | 48 ++----- .../Http/FrameHeaders.Generated.cs | 134 +++++++++--------- .../Http/FrameResponseHeaders.cs | 15 +- .../Http/SocketOutput.cs | 12 +- .../Infrastructure/MemoryPoolIterator2.cs | 120 +++++++--------- .../MemoryPoolBlock2Tests.cs | 13 +- .../KnownHeaders.cs | 10 +- 9 files changed, 173 insertions(+), 201 deletions(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Filter/LibuvStream.cs b/src/Microsoft.AspNet.Server.Kestrel/Filter/LibuvStream.cs index d61c0eafbc..b4e92b2d76 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Filter/LibuvStream.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Filter/LibuvStream.cs @@ -68,13 +68,29 @@ namespace Microsoft.AspNet.Server.Kestrel.Filter public override void Write(byte[] buffer, int offset, int count) { - var segment = new ArraySegment(buffer, offset, count); + ArraySegment segment; + if (buffer != null) + { + segment = new ArraySegment(buffer, offset, count); + } + else + { + segment = default(ArraySegment); + } _output.Write(segment); } public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken token) { - var segment = new ArraySegment(buffer, offset, count); + ArraySegment segment; + if (buffer != null) + { + segment = new ArraySegment(buffer, offset, count); + } + else + { + segment = default(ArraySegment); + } return _output.WriteAsync(segment, cancellationToken: token); } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Filter/StreamSocketOutput.cs b/src/Microsoft.AspNet.Server.Kestrel/Filter/StreamSocketOutput.cs index ba7c57dcf4..e990e34ee8 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Filter/StreamSocketOutput.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Filter/StreamSocketOutput.cs @@ -52,7 +52,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Filter returnBlock.Pool?.Return(returnBlock); } - _outputStream.Write(end.Block.Array, end.Block.Data.Offset, end.Index); + _outputStream.Write(end.Block.Array, end.Block.Data.Offset, end.Index - end.Block.Data.Offset); end.Block.Pool?.Return(end.Block); } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs index 98652050cd..e86daad1ea 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs @@ -623,8 +623,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http bool appCompleted, bool immediate) { - var memoryBlock = Memory2.Lease(); - var begin = memoryBlock.GetIterator(); + var begin = SocketOutput.ProducingStart(); + var count = 0; var end = begin; if (_keepAlive) { @@ -673,48 +673,20 @@ namespace Microsoft.AspNet.Server.Kestrel.Http _responseHeaders.SetRawConnection("keep-alive", _bytesConnectionKeepAlive); } - end.CopyFrom(_httpVersion == HttpVersionType.Http1_1 ? _bytesHttpVersion1_1 : _bytesHttpVersion1_0); - end.CopyFrom(statusBytes); - _responseHeaders.CopyTo(ref end); - end.CopyFrom(_bytesEndHeaders, 0, _bytesEndHeaders.Length); + count += end.CopyFrom(_httpVersion == HttpVersionType.Http1_1 ? _bytesHttpVersion1_1 : _bytesHttpVersion1_0); + count += end.CopyFrom(statusBytes); + count += _responseHeaders.CopyTo(ref end); + count += end.CopyFrom(_bytesEndHeaders, 0, _bytesEndHeaders.Length); - // TODO: change this to SocketOutput.ProduceStart/ProduceComplete once that change is made - var scan = begin.Block; - while (scan.Next != null) - { - if (scan.Start != scan.End) - { - SocketOutput.WriteAsync( - new ArraySegment(scan.Array, scan.Start, scan.End - scan.Start), - false); - } - var next = scan.Next; - Memory2.Return(scan); - scan = next; - } - var writeTask = SocketOutput.WriteAsync( - new ArraySegment(scan.Array, scan.Start, scan.End - scan.Start), - immediate); + SocketOutput.ProducingComplete(end, count); - if (writeTask.IsCompleted) + if (immediate) { - Memory2.Return(scan); - return TaskUtilities.CompletedTask; + return SocketOutput.WriteAsync(default(ArraySegment), immediate: true); } else { - return writeTask.ContinueWith( - (t, o) => - { - var mb = (MemoryPoolBlock2)o; - mb.Pool.Return(mb); - - if (t.IsFaulted) - { - throw t.Exception; - } - }, - scan); + return TaskUtilities.CompletedTask; } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/FrameHeaders.Generated.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameHeaders.Generated.cs index 5571e9c56b..bc7f4d0caa 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/FrameHeaders.Generated.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameHeaders.Generated.cs @@ -7821,15 +7821,16 @@ namespace Microsoft.AspNet.Server.Kestrel.Http ((ICollection>)MaybeUnknown)?.CopyTo(array, arrayIndex); } - protected void CopyToFast(ref MemoryPoolIterator2 output) + protected int CopyToFast(ref MemoryPoolIterator2 output) { + var count = 0; if (((_bits & 1L) != 0)) { foreach(var value in _CacheControl) { - output.CopyFrom(_headerBytes, 0, 17); - output.CopyFromAscii(value); + count += output.CopyFrom(_headerBytes, 0, 17); + count += output.CopyFromAscii(value); } } @@ -7837,12 +7838,12 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { if (_rawConnection != null) { - output.CopyFrom(_rawConnection, 0, _rawConnection.Length); + count += output.CopyFrom(_rawConnection, 0, _rawConnection.Length); } else foreach(var value in _Connection) { - output.CopyFrom(_headerBytes, 17, 14); - output.CopyFromAscii(value); + count += output.CopyFrom(_headerBytes, 17, 14); + count += output.CopyFromAscii(value); } } @@ -7850,12 +7851,12 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { if (_rawDate != null) { - output.CopyFrom(_rawDate, 0, _rawDate.Length); + count += output.CopyFrom(_rawDate, 0, _rawDate.Length); } else foreach(var value in _Date) { - output.CopyFrom(_headerBytes, 31, 8); - output.CopyFromAscii(value); + count += output.CopyFrom(_headerBytes, 31, 8); + count += output.CopyFromAscii(value); } } @@ -7863,8 +7864,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _KeepAlive) { - output.CopyFrom(_headerBytes, 39, 14); - output.CopyFromAscii(value); + count += output.CopyFrom(_headerBytes, 39, 14); + count += output.CopyFromAscii(value); } } @@ -7872,8 +7873,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _Pragma) { - output.CopyFrom(_headerBytes, 53, 10); - output.CopyFromAscii(value); + count += output.CopyFrom(_headerBytes, 53, 10); + count += output.CopyFromAscii(value); } } @@ -7881,8 +7882,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _Trailer) { - output.CopyFrom(_headerBytes, 63, 11); - output.CopyFromAscii(value); + count += output.CopyFrom(_headerBytes, 63, 11); + count += output.CopyFromAscii(value); } } @@ -7890,12 +7891,12 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { if (_rawTransferEncoding != null) { - output.CopyFrom(_rawTransferEncoding, 0, _rawTransferEncoding.Length); + count += output.CopyFrom(_rawTransferEncoding, 0, _rawTransferEncoding.Length); } else foreach(var value in _TransferEncoding) { - output.CopyFrom(_headerBytes, 74, 21); - output.CopyFromAscii(value); + count += output.CopyFrom(_headerBytes, 74, 21); + count += output.CopyFromAscii(value); } } @@ -7903,8 +7904,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _Upgrade) { - output.CopyFrom(_headerBytes, 95, 11); - output.CopyFromAscii(value); + count += output.CopyFrom(_headerBytes, 95, 11); + count += output.CopyFromAscii(value); } } @@ -7912,8 +7913,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _Via) { - output.CopyFrom(_headerBytes, 106, 7); - output.CopyFromAscii(value); + count += output.CopyFrom(_headerBytes, 106, 7); + count += output.CopyFromAscii(value); } } @@ -7921,8 +7922,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _Warning) { - output.CopyFrom(_headerBytes, 113, 11); - output.CopyFromAscii(value); + count += output.CopyFrom(_headerBytes, 113, 11); + count += output.CopyFromAscii(value); } } @@ -7930,8 +7931,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _Allow) { - output.CopyFrom(_headerBytes, 124, 9); - output.CopyFromAscii(value); + count += output.CopyFrom(_headerBytes, 124, 9); + count += output.CopyFromAscii(value); } } @@ -7939,12 +7940,12 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { if (_rawContentLength != null) { - output.CopyFrom(_rawContentLength, 0, _rawContentLength.Length); + count += output.CopyFrom(_rawContentLength, 0, _rawContentLength.Length); } else foreach(var value in _ContentLength) { - output.CopyFrom(_headerBytes, 133, 18); - output.CopyFromAscii(value); + count += output.CopyFrom(_headerBytes, 133, 18); + count += output.CopyFromAscii(value); } } @@ -7952,8 +7953,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _ContentType) { - output.CopyFrom(_headerBytes, 151, 16); - output.CopyFromAscii(value); + count += output.CopyFrom(_headerBytes, 151, 16); + count += output.CopyFromAscii(value); } } @@ -7961,8 +7962,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _ContentEncoding) { - output.CopyFrom(_headerBytes, 167, 20); - output.CopyFromAscii(value); + count += output.CopyFrom(_headerBytes, 167, 20); + count += output.CopyFromAscii(value); } } @@ -7970,8 +7971,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _ContentLanguage) { - output.CopyFrom(_headerBytes, 187, 20); - output.CopyFromAscii(value); + count += output.CopyFrom(_headerBytes, 187, 20); + count += output.CopyFromAscii(value); } } @@ -7979,8 +7980,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _ContentLocation) { - output.CopyFrom(_headerBytes, 207, 20); - output.CopyFromAscii(value); + count += output.CopyFrom(_headerBytes, 207, 20); + count += output.CopyFromAscii(value); } } @@ -7988,8 +7989,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _ContentMD5) { - output.CopyFrom(_headerBytes, 227, 15); - output.CopyFromAscii(value); + count += output.CopyFrom(_headerBytes, 227, 15); + count += output.CopyFromAscii(value); } } @@ -7997,8 +7998,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _ContentRange) { - output.CopyFrom(_headerBytes, 242, 17); - output.CopyFromAscii(value); + count += output.CopyFrom(_headerBytes, 242, 17); + count += output.CopyFromAscii(value); } } @@ -8006,8 +8007,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _Expires) { - output.CopyFrom(_headerBytes, 259, 11); - output.CopyFromAscii(value); + count += output.CopyFrom(_headerBytes, 259, 11); + count += output.CopyFromAscii(value); } } @@ -8015,8 +8016,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _LastModified) { - output.CopyFrom(_headerBytes, 270, 17); - output.CopyFromAscii(value); + count += output.CopyFrom(_headerBytes, 270, 17); + count += output.CopyFromAscii(value); } } @@ -8024,8 +8025,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _AcceptRanges) { - output.CopyFrom(_headerBytes, 287, 17); - output.CopyFromAscii(value); + count += output.CopyFrom(_headerBytes, 287, 17); + count += output.CopyFromAscii(value); } } @@ -8033,8 +8034,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _Age) { - output.CopyFrom(_headerBytes, 304, 7); - output.CopyFromAscii(value); + count += output.CopyFrom(_headerBytes, 304, 7); + count += output.CopyFromAscii(value); } } @@ -8042,8 +8043,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _ETag) { - output.CopyFrom(_headerBytes, 311, 8); - output.CopyFromAscii(value); + count += output.CopyFrom(_headerBytes, 311, 8); + count += output.CopyFromAscii(value); } } @@ -8051,8 +8052,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _Location) { - output.CopyFrom(_headerBytes, 319, 12); - output.CopyFromAscii(value); + count += output.CopyFrom(_headerBytes, 319, 12); + count += output.CopyFromAscii(value); } } @@ -8060,8 +8061,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _ProxyAutheticate) { - output.CopyFrom(_headerBytes, 331, 21); - output.CopyFromAscii(value); + count += output.CopyFrom(_headerBytes, 331, 21); + count += output.CopyFromAscii(value); } } @@ -8069,8 +8070,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _RetryAfter) { - output.CopyFrom(_headerBytes, 352, 15); - output.CopyFromAscii(value); + count += output.CopyFrom(_headerBytes, 352, 15); + count += output.CopyFromAscii(value); } } @@ -8078,12 +8079,12 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { if (_rawServer != null) { - output.CopyFrom(_rawServer, 0, _rawServer.Length); + count += output.CopyFrom(_rawServer, 0, _rawServer.Length); } else foreach(var value in _Server) { - output.CopyFrom(_headerBytes, 367, 10); - output.CopyFromAscii(value); + count += output.CopyFrom(_headerBytes, 367, 10); + count += output.CopyFromAscii(value); } } @@ -8091,8 +8092,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _SetCookie) { - output.CopyFrom(_headerBytes, 377, 14); - output.CopyFromAscii(value); + count += output.CopyFrom(_headerBytes, 377, 14); + count += output.CopyFromAscii(value); } } @@ -8100,8 +8101,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _Vary) { - output.CopyFrom(_headerBytes, 391, 8); - output.CopyFromAscii(value); + count += output.CopyFrom(_headerBytes, 391, 8); + count += output.CopyFromAscii(value); } } @@ -8109,11 +8110,12 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { foreach(var value in _WWWAuthenticate) { - output.CopyFrom(_headerBytes, 399, 20); - output.CopyFromAscii(value); + count += output.CopyFrom(_headerBytes, 399, 20); + count += output.CopyFromAscii(value); } } + return count; } public unsafe void Append(byte[] keyBytes, int keyOffset, int keyLength, string value) { diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/FrameResponseHeaders.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameResponseHeaders.cs index 86c2a18554..30c75fe4e2 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/FrameResponseHeaders.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameResponseHeaders.cs @@ -10,7 +10,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { public partial class FrameResponseHeaders : FrameHeaders { - private static byte[] _CRLF = new[] { (byte)'\r', (byte)'\n' }; + private static byte[] _CrLf = new[] { (byte)'\r', (byte)'\n' }; private static byte[] _colonSpace = new[] { (byte)':', (byte)' ' }; public bool HasConnection => HeaderConnection.Count != 0; @@ -30,22 +30,23 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return GetEnumerator(); } - public void CopyTo(ref MemoryPoolIterator2 output) + public int CopyTo(ref MemoryPoolIterator2 output) { - CopyToFast(ref output); + var count = CopyToFast(ref output); if (MaybeUnknown != null) { foreach (var kv in MaybeUnknown) { foreach (var value in kv.Value) { - output.CopyFrom(_CRLF, 0, 2); - output.CopyFromAscii(kv.Key); - output.CopyFrom(_colonSpace, 0, 2); - output.CopyFromAscii(value); + count += output.CopyFrom(_CrLf, 0, 2); + count += output.CopyFromAscii(kv.Key); + count += output.CopyFrom(_colonSpace, 0, 2); + count += output.CopyFromAscii(value); } } } + return count; } public partial struct Enumerator : IEnumerator> diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs index 404cb601d5..5f58fac22d 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs @@ -70,11 +70,13 @@ namespace Microsoft.AspNet.Server.Kestrel.Http bool socketShutdownSend = false, bool socketDisconnect = false) { - var tail = ProducingStart(); - tail = tail.CopyFrom(buffer); - // We do our own accounting below - ProducingComplete(tail, count: 0); - + if (buffer.Count > 0) + { + var tail = ProducingStart(); + tail.CopyFrom(buffer); + // We do our own accounting below + ProducingComplete(tail, count: 0); + } TaskCompletionSource tcs = null; lock (_contextLock) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolIterator2.cs b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolIterator2.cs index a3b5e678c5..9af9d4428e 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolIterator2.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolIterator2.cs @@ -532,7 +532,17 @@ namespace Microsoft.AspNet.Server.Kestrel.Infrastructure } } - public MemoryPoolIterator2 CopyFrom(ArraySegment buffer) + public int CopyFrom(byte[] data) + { + return CopyFrom(new ArraySegment(data)); + } + + public int CopyFrom(byte[] data, int offset, int count) + { + return CopyFrom(new ArraySegment(data, offset, count)); + } + + public int CopyFrom(ArraySegment buffer) { Debug.Assert(_block != null); Debug.Assert(_block.Pool != null); @@ -545,11 +555,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Infrastructure var bufferIndex = buffer.Offset; var remaining = buffer.Count; + var bytesLeftInBlock = block.Data.Offset + block.Data.Count - blockIndex; while (remaining > 0) { - var bytesLeftInBlock = block.Data.Offset + block.Data.Count - blockIndex; - if (bytesLeftInBlock == 0) { var nextBlock = pool.Lease(); @@ -560,93 +569,55 @@ namespace Microsoft.AspNet.Server.Kestrel.Infrastructure bytesLeftInBlock = block.Data.Count; } - var bytesToCopy = Math.Min(remaining, bytesLeftInBlock); + var bytesToCopy = remaining < bytesLeftInBlock ? remaining : bytesLeftInBlock; Buffer.BlockCopy(buffer.Array, bufferIndex, block.Array, blockIndex, bytesToCopy); blockIndex += bytesToCopy; bufferIndex += bytesToCopy; remaining -= bytesToCopy; + bytesLeftInBlock -= bytesToCopy; block.End = blockIndex; } - return new MemoryPoolIterator2(block, blockIndex); + _block = block; + _index = blockIndex; + + return buffer.Count; } - public void CopyFrom(byte[] data) - { - CopyFrom(data, 0, data.Length); - } - - public void CopyFrom(byte[] data, int offset, int count) + public unsafe int CopyFromAscii(string data) { + Debug.Assert(_block != null); + Debug.Assert(_block.Pool != null); Debug.Assert(_block.Next == null); Debug.Assert(_block.End == _index); + var pool = _block.Pool; var block = _block; + var blockIndex = _index; + var length = data.Length; - var sourceData = data; - var sourceStart = offset; - var sourceEnd = offset + count; - - var targetData = block.Array; - var targetStart = block.End; - var targetEnd = block.Data.Offset + block.Data.Count; - - while (true) - { - // actual count to copy is remaining data, or unused trailing space in the current block, whichever is smaller - var copyCount = Math.Min(sourceEnd - sourceStart, targetEnd - targetStart); - - Buffer.BlockCopy(sourceData, sourceStart, targetData, targetStart, copyCount); - sourceStart += copyCount; - targetStart += copyCount; - - // if this means all source data has been copied - if (sourceStart == sourceEnd) - { - // increase occupied space in the block, and adjust iterator at start of unused trailing space - block.End = targetStart; - _block = block; - _index = targetStart; - return; - } - - // otherwise another block needs to be allocated to follow this one - block.Next = block.Pool.Lease(); - block = block.Next; - - targetData = block.Array; - targetStart = block.End; - targetEnd = block.Data.Offset + block.Data.Count; - } - } - - public unsafe void CopyFromAscii(string data) - { - Debug.Assert(_block.Next == null); - Debug.Assert(_block.End == _index); - - var block = _block; - - var inputLength = data.Length; - var inputLengthMinusSpan = inputLength - 3; + var bytesLeftInBlock = block.Data.Offset + block.Data.Count - blockIndex; + var bytesLeftInBlockMinusSpan = bytesLeftInBlock - 3; fixed (char* pData = data) { var input = pData; - var inputEnd = pData + data.Length; - var blockRemaining = block.Data.Offset + block.Data.Count - block.End; - var blockRemainingMinusSpan = blockRemaining - 3; + var inputEnd = pData + length; + var inputEndMinusSpan = inputEnd - 3; while (input < inputEnd) { - if (blockRemaining == 0) + if (bytesLeftInBlock == 0) { - block.Next = block.Pool.Lease(); - block = block.Next; - blockRemaining = block.Data.Count; - blockRemainingMinusSpan = blockRemaining - 3; + var nextBlock = pool.Lease(); + block.Next = nextBlock; + block = nextBlock; + + blockIndex = block.Data.Offset; + bytesLeftInBlock = block.Data.Count; + bytesLeftInBlockMinusSpan = bytesLeftInBlock - 3; } fixed (byte* pOutput = block.Data.Array) @@ -654,7 +625,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Infrastructure var output = pOutput + block.End; var copied = 0; - for (; copied < inputLengthMinusSpan && copied < blockRemainingMinusSpan; copied += 4) + for (; input < inputEndMinusSpan && copied < bytesLeftInBlockMinusSpan; copied += 4) { *(output) = (byte)*(input); *(output + 1) = (byte)*(input + 1); @@ -662,19 +633,24 @@ namespace Microsoft.AspNet.Server.Kestrel.Infrastructure *(output + 3) = (byte)*(input + 3); output += 4; input += 4; - blockRemainingMinusSpan -= 4; } - for (; copied < inputLength && copied < blockRemaining; copied++) + for (; input < inputEnd && copied < bytesLeftInBlock; copied++) { *(output++) = (byte)*(input++); - blockRemaining--; } - block.End += copied; - _block = block; - _index = block.End; + + blockIndex += copied; + bytesLeftInBlockMinusSpan -= copied; + bytesLeftInBlock -= copied; } + block.End = blockIndex; } } + + _block = block; + _index = blockIndex; + + return length; } } } diff --git a/test/Microsoft.AspNet.Server.KestrelTests/MemoryPoolBlock2Tests.cs b/test/Microsoft.AspNet.Server.KestrelTests/MemoryPoolBlock2Tests.cs index 5f445ef487..239175e198 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/MemoryPoolBlock2Tests.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/MemoryPoolBlock2Tests.cs @@ -159,7 +159,8 @@ namespace Microsoft.AspNet.Server.KestrelTests using (var pool = new MemoryPool2()) { var block1 = pool.Lease(128); - var iterator = block1.GetIterator(); + var start = block1.GetIterator(); + var end = start; var bufferSize = block1.Data.Count * 3; var buffer = new byte[bufferSize]; @@ -170,18 +171,18 @@ namespace Microsoft.AspNet.Server.KestrelTests Assert.Null(block1.Next); - var end = iterator.CopyFrom(new ArraySegment(buffer)); + end.CopyFrom(new ArraySegment(buffer)); Assert.NotNull(block1.Next); for (int i = 0; i < bufferSize; i++) { - Assert.Equal(i % 73, iterator.Take()); + Assert.Equal(i % 73, start.Take()); } - Assert.Equal(-1, iterator.Take()); - Assert.Equal(iterator.Block, end.Block); - Assert.Equal(iterator.Index, end.Index); + Assert.Equal(-1, start.Take()); + Assert.Equal(start.Block, end.Block); + Assert.Equal(start.Index, end.Index); } } diff --git a/tools/Microsoft.AspNet.Server.Kestrel.GeneratedCode/KnownHeaders.cs b/tools/Microsoft.AspNet.Server.Kestrel.GeneratedCode/KnownHeaders.cs index ab20b6c2d9..214cf91f6b 100644 --- a/tools/Microsoft.AspNet.Server.Kestrel.GeneratedCode/KnownHeaders.cs +++ b/tools/Microsoft.AspNet.Server.Kestrel.GeneratedCode/KnownHeaders.cs @@ -380,22 +380,24 @@ namespace Microsoft.AspNet.Server.Kestrel.Http ((ICollection>)MaybeUnknown)?.CopyTo(array, arrayIndex); }} {(loop.ClassName == "FrameResponseHeaders" ? $@" - protected void CopyToFast(ref MemoryPoolIterator2 output) + protected int CopyToFast(ref MemoryPoolIterator2 output) {{ + var count = 0; {Each(loop.Headers, header => $@" if ({header.TestBit()}) {{ {(header.EnhancedSetter == false ? "" : $@" if (_raw{header.Identifier} != null) {{ - output.CopyFromAscii(_raw{header.Identifier}, 0, _raw{header.Identifier}.Length); + count += output.CopyFrom(_raw{header.Identifier}, 0, _raw{header.Identifier}.Length); }} else ")} foreach(var value in _{header.Identifier}) {{ - output.CopyFromAscii(_headerBytes, {header.BytesOffset}, {header.BytesCount}); - output.CopyFromAscii(value); + count += output.CopyFrom(_headerBytes, {header.BytesOffset}, {header.BytesCount}); + count += output.CopyFromAscii(value); }} }} ")} + return count; }}" : "")} public unsafe void Append(byte[] keyBytes, int keyOffset, int keyLength, string value) {{ From 81dba3961d809d4624677b1d87f78968e521a7fa Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sat, 21 Nov 2015 14:13:23 +0000 Subject: [PATCH 5/5] less work in locks --- .../Http/SocketOutput.cs | 135 +++++++++++++----- 1 file changed, 100 insertions(+), 35 deletions(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs index 5f58fac22d..911a5a9675 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs @@ -16,6 +16,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { private const int _maxPendingWrites = 3; private const int _maxBytesPreCompleted = 65536; + private const int _initialTaskQueues = 64; + + private static WaitCallback _returnBlocks = (state) => ReturnBlocks((MemoryPoolBlock2)state); private readonly KestrelThread _thread; private readonly UvStreamHandle _socket; @@ -44,6 +47,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http private Exception _lastWriteError; private WriteContext _nextWriteContext; private readonly Queue> _tasksPending; + private readonly Queue> _tasksCompleted; public SocketOutput( KestrelThread thread, @@ -58,7 +62,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http _connection = connection; _connectionId = connectionId; _log = log; - _tasksPending = new Queue>(); + _tasksPending = new Queue>(_initialTaskQueues); + _tasksCompleted = new Queue>(_initialTaskQueues); _head = memory.Lease(); _tail = _head; @@ -79,6 +84,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } TaskCompletionSource tcs = null; + var scheduleWrite = false; + lock (_contextLock) { if (_nextWriteContext == null) @@ -118,11 +125,16 @@ namespace Microsoft.AspNet.Server.Kestrel.Http if (_writesPending < _maxPendingWrites && immediate) { - ScheduleWrite(); + scheduleWrite = true; _writesPending++; } } + if (scheduleWrite) + { + ScheduleWrite(); + } + // Return TaskCompletionSource's Task if set, otherwise completed Task return tcs?.Task ?? TaskUtilities.CompletedTask; } @@ -164,6 +176,9 @@ namespace Microsoft.AspNet.Server.Kestrel.Http public void ProducingComplete(MemoryPoolIterator2 end, int count) { + var decreasePreCompleted = false; + MemoryPoolBlock2 blockToReturn = null; + lock (_returnLock) { Debug.Assert(_isProducing); @@ -176,26 +191,40 @@ namespace Microsoft.AspNet.Server.Kestrel.Http if (count != 0) { - lock (_contextLock) - { - _numBytesPreCompleted += count; - } + decreasePreCompleted = true; } } else { - var block = _returnFromOnProducingComplete; - while (block != null) - { - var returnBlock = block; - block = block.Next; - - returnBlock.Pool?.Return(returnBlock); - } - + blockToReturn = _returnFromOnProducingComplete; _returnFromOnProducingComplete = null; } } + + if (decreasePreCompleted) + { + lock (_contextLock) + { + _numBytesPreCompleted += count; + } + } + + + if (blockToReturn != null) + { + ThreadPool.QueueUserWorkItem(_returnBlocks, blockToReturn); + } + } + + private static void ReturnBlocks(MemoryPoolBlock2 block) + { + while(block != null) + { + var returningBlock = block; + block = returningBlock.Next; + + returningBlock.Pool?.Return(returningBlock); + } } private void ScheduleWrite() @@ -252,11 +281,13 @@ namespace Microsoft.AspNet.Server.Kestrel.Http _connection.Abort(); } + bool scheduleWrite = false; + lock (_contextLock) { if (_nextWriteContext != null) { - ScheduleWrite(); + scheduleWrite = true; } else { @@ -279,21 +310,36 @@ namespace Microsoft.AspNet.Server.Kestrel.Http _numBytesPreCompleted += bytesToWrite; bytesLeftToBuffer -= bytesToWrite; - if (_lastWriteError == null) - { - ThreadPool.QueueUserWorkItem( - (o) => ((TaskCompletionSource)o).SetResult(null), - tcs); - } - else - { - // error is closure captured - ThreadPool.QueueUserWorkItem( - (o) => ((TaskCompletionSource)o).SetException(_lastWriteError), - tcs); - } + _tasksCompleted.Enqueue(tcs); } } + + while (_tasksCompleted.Count > 0) + { + var tcs = _tasksCompleted.Dequeue(); + if (_lastWriteError == null) + { + ThreadPool.QueueUserWorkItem( + (o) => ((TaskCompletionSource)o).SetResult(null), + tcs); + } + else + { + // error is closure captured + ThreadPool.QueueUserWorkItem( + (o) => ((TaskCompletionSource)o).SetException(_lastWriteError), + tcs); + } + } + + if (scheduleWrite) + { + // ScheduleWrite(); + // on right thread, fairness issues? + WriteAllPending(); + } + + _tasksCompleted.Clear(); } // This is called on the libuv event loop @@ -345,6 +391,8 @@ namespace Microsoft.AspNet.Server.Kestrel.Http private class WriteContext { + private static WaitCallback _returnWrittenBlocks = (state) => ReturnWrittenBlocks((MemoryPoolBlock2)state); + private MemoryPoolIterator2 _lockedStart; private MemoryPoolIterator2 _lockedEnd; private int _bufferCount; @@ -385,7 +433,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { _writeReq.Dispose(); var _this = (WriteContext)state; - _this.ReturnFullyWrittenBlocks(); + _this.ScheduleReturnFullyWrittenBlocks(); _this.WriteStatus = status; _this.WriteError = error; _this.DoShutdownIfNeeded(); @@ -441,11 +489,30 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { Self.OnWriteCompleted(_byteCount, WriteStatus, WriteError); } - - private void ReturnFullyWrittenBlocks() + + private void ScheduleReturnFullyWrittenBlocks() { var block = _lockedStart.Block; - while (block != _lockedEnd.Block) + var end = _lockedEnd.Block; + if (block == end) + { + end.Unpin(); + return; + } + + while (block.Next != end) + { + block = block.Next; + block.Unpin(); + } + block.Next = null; + + ThreadPool.QueueUserWorkItem(_returnWrittenBlocks, _lockedStart.Block); + } + + private static void ReturnWrittenBlocks(MemoryPoolBlock2 block) + { + while (block != null) { var returnBlock = block; block = block.Next; @@ -453,8 +520,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Http returnBlock.Unpin(); returnBlock.Pool?.Return(returnBlock); } - - _lockedEnd.Block.Unpin(); } private void LockWrite()