From 25f7b7b5fb0c515411fdc549595e17caeb025330 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 11 Nov 2015 12:24:39 +0000 Subject: [PATCH 01/24] Pre-allocate memory pool --- .../Infrastructure/MemoryPool2.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPool2.cs b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPool2.cs index 86561516c..fd2e2ba44 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPool2.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPool2.cs @@ -53,6 +53,13 @@ public class MemoryPool2 : IDisposable /// private bool _disposedValue = false; // To detect redundant calls + public MemoryPool2() + { + // Allocate on creation or multiple simultaneous connections + // will all allocate rather than reuse the pooled buffers + Return(AllocateSlab()); + } + /// /// Called to take a block from the pool. /// From 58d712f9d7fc6af11765169e8fac14d8e299e6e7 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 11 Nov 2015 17:20:31 +0000 Subject: [PATCH 02/24] Precomputed header bytes --- .../Http/Frame.cs | 183 ++++---- .../Http/FrameHeaders.Generated.cs | 423 ++++++++++++++++++ .../Http/FrameHeaders.cs | 6 + .../Http/FrameResponseHeaders.cs | 51 ++- .../Http/MemoryPool.cs | 125 ------ .../Http/MemoryPoolTextWriter.cs | 155 ------- .../Http/ReasonPhrases.cs | 171 +++++++ .../Infrastructure/MemoryPool2.cs | 2 +- .../ServiceContext.cs | 4 - .../TestInput.cs | 2 - .../KnownHeaders.cs | 70 ++- 11 files changed, 822 insertions(+), 370 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/Frame.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs index c08d0eb68..54c852e16 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs @@ -28,6 +28,13 @@ public partial class Frame : FrameContext, IFrameControl private static readonly ArraySegment _emptyData = new ArraySegment(new byte[0]); private static readonly byte[] _hex = Encoding.ASCII.GetBytes("0123456789abcdef"); + private static readonly byte[] _bytesEndLine = Encoding.ASCII.GetBytes("\r\n"); + private static readonly byte[] _bytesConnectionClose = Encoding.ASCII.GetBytes("Connection: close\r\n\r\n"); + private static readonly byte[] _bytesConnectionKeepAlive = Encoding.ASCII.GetBytes("Connection: keep-alive\r\n\r\n"); + private static readonly byte[] _bytesTransferEncodingChunked = Encoding.ASCII.GetBytes("Transfer-Encoding: chunked\r\n"); + private static readonly byte[] _bytesContentLengthZero = Encoding.ASCII.GetBytes("Content-Length: 0\r\n"); + private static readonly byte[] _bytesSpace = Encoding.ASCII.GetBytes(" "); + private readonly object _onStartingSync = new Object(); private readonly object _onCompletedSync = new Object(); private readonly FrameRequestHeaders _requestHeaders = new FrameRequestHeaders(); @@ -471,19 +478,14 @@ public async Task ProduceStartAndFireOnStarting(bool immediate = true) 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() @@ -521,99 +523,128 @@ private async Task ProduceEnd() } } - private Tuple, IDisposable> CreateResponseHeader( - string status, - bool appCompleted) + private static void OutputAsciiBlock(string data, MemoryPoolBlock2 memoryBlock, ISocketOutput output) { - 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; + var end = memoryBlock.Start + memoryBlock.Data.Count; - foreach (var header in _responseHeaders) + foreach (var chr in data) { - var isConnection = false; - if (!hasConnection && - string.Equals(header.Key, "Connection", StringComparison.OrdinalIgnoreCase)) - { - 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)) + memoryBlock.Array[memoryBlock.End] = (byte)chr; + + memoryBlock.End++; + + if (memoryBlock.End == end) { - hasContentLength = true; + output.Write(memoryBlock.Data, immediate: false); + memoryBlock.End = memoryBlock.Start; } + } + } - foreach (var value in header.Value) + private static void OutputAsciiBlock(byte[] data, MemoryPoolBlock2 memoryBlock, ISocketOutput output) + { + var offset = 0; + var remaining = data.Length; + var end = memoryBlock.Start + memoryBlock.Data.Count; + + while (remaining > 0) + { + var blockRemaining = end - memoryBlock.End; + var copyAmount = blockRemaining >= remaining ? remaining : blockRemaining; + Buffer.BlockCopy(data, offset, memoryBlock.Array, memoryBlock.End, copyAmount); + + memoryBlock.End += copyAmount; + remaining -= copyAmount; + offset += copyAmount; + + if (memoryBlock.End == end) { - 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) - { - _keepAlive = false; - } + output.Write(memoryBlock.Data, immediate: false); + memoryBlock.End = memoryBlock.Start; } - } + } - if (_keepAlive && !hasTransferEncoding && !hasContentLength) + private Task CreateResponseHeader( + byte[] statusBytes, + bool appCompleted, + bool immediate) + { + var memoryBlock = Memory2.Lease(); + try { - if (appCompleted) + var blockRemaining = memoryBlock.Data.Count; + + OutputAsciiBlock(HttpVersion, memoryBlock, SocketOutput); + OutputAsciiBlock(_bytesSpace, memoryBlock, SocketOutput); + OutputAsciiBlock(statusBytes, memoryBlock, SocketOutput); + + foreach (var header in _responseHeaders.AsOutputEnumerable()) { - // Don't set the Content-Length or Transfer-Encoding headers - // automatically for HEAD requests or 101, 204, 205, 304 responses. - if (Method != "HEAD" && StatusCanHaveBody(StatusCode)) + foreach (var value in header.Value) { - // 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"); + OutputAsciiBlock(header.Key, memoryBlock, SocketOutput); + OutputAsciiBlock(value, memoryBlock, SocketOutput); + OutputAsciiBlock(_bytesEndLine, memoryBlock, SocketOutput); + + if (_responseHeaders.HasConnection && value.IndexOf("close", StringComparison.OrdinalIgnoreCase) != -1) + { + _keepAlive = false; + } } + } - else + + if (_keepAlive && !_responseHeaders.HasTransferEncoding && !_responseHeaders.HasContentLength) { - if (HttpVersion == "HTTP/1.1") + if (appCompleted) { - _autoChunk = true; - writer.Write("Transfer-Encoding: chunked\r\n"); + // Don't set the Content-Length or Transfer-Encoding headers + // automatically for HEAD requests or 101, 204, 205, 304 responses. + if (Method != "HEAD" && StatusCanHaveBody(StatusCode)) + { + // Since the app has completed and we are only now generating + // the headers we can safely set the Content-Length to 0. + OutputAsciiBlock(_bytesContentLengthZero, memoryBlock, SocketOutput); + } } else { - _keepAlive = false; + if (HttpVersion == "HTTP/1.1") + { + _autoChunk = true; + OutputAsciiBlock(_bytesTransferEncodingChunked, memoryBlock, SocketOutput); + } + else + { + _keepAlive = false; + } } } - } - if (_keepAlive == false && hasConnection == false && HttpVersion == "HTTP/1.1") - { - writer.Write("Connection: close\r\n\r\n"); - } - else if (_keepAlive && hasConnection == false && HttpVersion == "HTTP/1.0") - { - writer.Write("Connection: keep-alive\r\n\r\n"); + if (_keepAlive == false && _responseHeaders.HasConnection == false && HttpVersion == "HTTP/1.1") + { + OutputAsciiBlock(_bytesConnectionClose, memoryBlock, SocketOutput); + } + else if (_keepAlive && _responseHeaders.HasConnection == false && HttpVersion == "HTTP/1.0") + { + OutputAsciiBlock(_bytesConnectionKeepAlive, memoryBlock, SocketOutput); + } + else + { + OutputAsciiBlock(_bytesEndLine, memoryBlock, SocketOutput); + } + + return SocketOutput.WriteAsync( + (memoryBlock.Start == memoryBlock.End) ? + default(ArraySegment) : + new ArraySegment(memoryBlock.Array, memoryBlock.Start, memoryBlock.End - memoryBlock.Start), + immediate); } - else + finally { - writer.Write('\r'); - writer.Write('\n'); + Memory2.Return(memoryBlock); } - writer.Flush(); - return new Tuple, IDisposable>(writer.Buffer, writer); } private bool TakeStartLine(SocketInput input) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/FrameHeaders.Generated.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameHeaders.Generated.cs index 1b4415b5a..2075aa30f 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/FrameHeaders.Generated.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameHeaders.Generated.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; +using System.Text; using Microsoft.Extensions.Primitives; namespace Microsoft.AspNet.Server.Kestrel.Http @@ -8,6 +9,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http public partial class FrameRequestHeaders { + private long _bits = 0; private StringValues _CacheControl; @@ -74,6 +76,7 @@ public StringValues HeaderConnection set { _bits |= 2L; + HasConnection = true; _Connection = value; } } @@ -139,6 +142,7 @@ public StringValues HeaderTransferEncoding set { _bits |= 64L; + HasTransferEncoding = true; _TransferEncoding = value; } } @@ -204,6 +208,7 @@ public StringValues HeaderContentLength set { _bits |= 2048L; + HasContentLength = true; _ContentLength = value; } } @@ -1860,6 +1865,7 @@ protected override void SetValueFast(string key, StringValues value) { _bits |= 2L; _Connection = value; + HasConnection = true; return; } @@ -1981,6 +1987,7 @@ protected override void SetValueFast(string key, StringValues value) { _bits |= 64L; _TransferEncoding = value; + HasTransferEncoding = true; return; } @@ -2028,6 +2035,7 @@ protected override void SetValueFast(string key, StringValues value) { _bits |= 2048L; _ContentLength = value; + HasContentLength = true; return; } @@ -2246,6 +2254,7 @@ protected override void AddValueFast(string key, StringValues value) } _bits |= 2L; _Connection = value; + HasConnection = true; return; } @@ -2427,6 +2436,7 @@ protected override void AddValueFast(string key, StringValues value) } _bits |= 64L; _TransferEncoding = value; + HasTransferEncoding = true; return; } @@ -2494,6 +2504,7 @@ protected override void AddValueFast(string key, StringValues value) } _bits |= 2048L; _ContentLength = value; + HasContentLength = true; return; } @@ -2785,6 +2796,7 @@ protected override bool RemoveFast(string key) { _bits &= ~2L; _Connection = StringValues.Empty; + HasConnection = false; return true; } else @@ -3011,6 +3023,7 @@ protected override bool RemoveFast(string key) { _bits &= ~64L; _TransferEncoding = StringValues.Empty; + HasTransferEncoding = false; return true; } else @@ -3093,6 +3106,7 @@ protected override bool RemoveFast(string key) { _bits &= ~2048L; _ContentLength = StringValues.Empty; + HasContentLength = false; return true; } else @@ -3394,6 +3408,9 @@ protected override void ClearFast() _Translate = StringValues.Empty; _UserAgent = StringValues.Empty; MaybeUnknown?.Clear(); + HasConnection = false; + HasTransferEncoding = false; + HasContentLength = false; } protected override void CopyToFast(KeyValuePair[] array, int arrayIndex) @@ -3948,6 +3965,7 @@ public unsafe void Append(byte[] keyBytes, int keyOffset, int keyLength, string else { _bits |= 2L; + HasConnection = true; _Connection = new StringValues(value); } return; @@ -4174,6 +4192,7 @@ public unsafe void Append(byte[] keyBytes, int keyOffset, int keyLength, string else { _bits |= 64L; + HasTransferEncoding = true; _TransferEncoding = new StringValues(value); } return; @@ -4256,6 +4275,7 @@ public unsafe void Append(byte[] keyBytes, int keyOffset, int keyLength, string else { _bits |= 2048L; + HasContentLength = true; _ContentLength = new StringValues(value); } return; @@ -4983,10 +5003,42 @@ public bool MoveNext() return true; } } + } public partial class FrameResponseHeaders { + private static byte[] _bytesCacheControl = Encoding.ASCII.GetBytes("Cache-Control: "); + private static byte[] _bytesConnection = Encoding.ASCII.GetBytes("Connection: "); + private static byte[] _bytesDate = Encoding.ASCII.GetBytes("Date: "); + private static byte[] _bytesKeepAlive = Encoding.ASCII.GetBytes("Keep-Alive: "); + private static byte[] _bytesPragma = Encoding.ASCII.GetBytes("Pragma: "); + private static byte[] _bytesTrailer = Encoding.ASCII.GetBytes("Trailer: "); + private static byte[] _bytesTransferEncoding = Encoding.ASCII.GetBytes("Transfer-Encoding: "); + private static byte[] _bytesUpgrade = Encoding.ASCII.GetBytes("Upgrade: "); + private static byte[] _bytesVia = Encoding.ASCII.GetBytes("Via: "); + private static byte[] _bytesWarning = Encoding.ASCII.GetBytes("Warning: "); + private static byte[] _bytesAllow = Encoding.ASCII.GetBytes("Allow: "); + private static byte[] _bytesContentLength = Encoding.ASCII.GetBytes("Content-Length: "); + private static byte[] _bytesContentType = Encoding.ASCII.GetBytes("Content-Type: "); + private static byte[] _bytesContentEncoding = Encoding.ASCII.GetBytes("Content-Encoding: "); + private static byte[] _bytesContentLanguage = Encoding.ASCII.GetBytes("Content-Language: "); + private static byte[] _bytesContentLocation = Encoding.ASCII.GetBytes("Content-Location: "); + private static byte[] _bytesContentMD5 = Encoding.ASCII.GetBytes("Content-MD5: "); + private static byte[] _bytesContentRange = Encoding.ASCII.GetBytes("Content-Range: "); + private static byte[] _bytesExpires = Encoding.ASCII.GetBytes("Expires: "); + private static byte[] _bytesLastModified = Encoding.ASCII.GetBytes("Last-Modified: "); + private static byte[] _bytesAcceptRanges = Encoding.ASCII.GetBytes("Accept-Ranges: "); + private static byte[] _bytesAge = Encoding.ASCII.GetBytes("Age: "); + private static byte[] _bytesETag = Encoding.ASCII.GetBytes("ETag: "); + private static byte[] _bytesLocation = Encoding.ASCII.GetBytes("Location: "); + private static byte[] _bytesProxyAutheticate = Encoding.ASCII.GetBytes("Proxy-Autheticate: "); + private static byte[] _bytesRetryAfter = Encoding.ASCII.GetBytes("Retry-After: "); + private static byte[] _bytesServer = Encoding.ASCII.GetBytes("Server: "); + private static byte[] _bytesSetCookie = Encoding.ASCII.GetBytes("Set-Cookie: "); + private static byte[] _bytesVary = Encoding.ASCII.GetBytes("Vary: "); + private static byte[] _bytesWWWAuthenticate = Encoding.ASCII.GetBytes("WWW-Authenticate: "); + private long _bits = 0; private StringValues _CacheControl; @@ -5042,6 +5094,7 @@ public StringValues HeaderConnection set { _bits |= 2L; + HasConnection = true; _Connection = value; } } @@ -5107,6 +5160,7 @@ public StringValues HeaderTransferEncoding set { _bits |= 64L; + HasTransferEncoding = true; _TransferEncoding = value; } } @@ -5172,6 +5226,7 @@ public StringValues HeaderContentLength set { _bits |= 2048L; + HasContentLength = true; _ContentLength = value; } } @@ -6360,6 +6415,7 @@ protected override void SetValueFast(string key, StringValues value) { _bits |= 2L; _Connection = value; + HasConnection = true; return; } @@ -6460,6 +6516,7 @@ protected override void SetValueFast(string key, StringValues value) { _bits |= 64L; _TransferEncoding = value; + HasTransferEncoding = true; return; } @@ -6507,6 +6564,7 @@ protected override void SetValueFast(string key, StringValues value) { _bits |= 2048L; _ContentLength = value; + HasContentLength = true; return; } } @@ -6649,6 +6707,7 @@ protected override void AddValueFast(string key, StringValues value) } _bits |= 2L; _Connection = value; + HasConnection = true; return; } @@ -6797,6 +6856,7 @@ protected override void AddValueFast(string key, StringValues value) } _bits |= 64L; _TransferEncoding = value; + HasTransferEncoding = true; return; } @@ -6864,6 +6924,7 @@ protected override void AddValueFast(string key, StringValues value) } _bits |= 2048L; _ContentLength = value; + HasContentLength = true; return; } } @@ -7048,6 +7109,7 @@ protected override bool RemoveFast(string key) { _bits &= ~2L; _Connection = StringValues.Empty; + HasConnection = false; return true; } else @@ -7232,6 +7294,7 @@ protected override bool RemoveFast(string key) { _bits &= ~64L; _TransferEncoding = StringValues.Empty; + HasTransferEncoding = false; return true; } else @@ -7314,6 +7377,7 @@ protected override bool RemoveFast(string key) { _bits &= ~2048L; _ContentLength = StringValues.Empty; + HasContentLength = false; return true; } else @@ -7490,6 +7554,9 @@ protected override void ClearFast() _Vary = StringValues.Empty; _WWWAuthenticate = StringValues.Empty; MaybeUnknown?.Clear(); + HasConnection = false; + HasTransferEncoding = false; + HasContentLength = false; } protected override void CopyToFast(KeyValuePair[] array, int arrayIndex) @@ -7909,6 +7976,7 @@ public unsafe void Append(byte[] keyBytes, int keyOffset, int keyLength, string else { _bits |= 2L; + HasConnection = true; _Connection = new StringValues(value); } return; @@ -8093,6 +8161,7 @@ public unsafe void Append(byte[] keyBytes, int keyOffset, int keyLength, string else { _bits |= 64L; + HasTransferEncoding = true; _TransferEncoding = new StringValues(value); } return; @@ -8175,6 +8244,7 @@ public unsafe void Append(byte[] keyBytes, int keyOffset, int keyLength, string else { _bits |= 2048L; + HasContentLength = true; _ContentLength = new StringValues(value); } return; @@ -8667,5 +8737,358 @@ public bool MoveNext() return true; } } + + public partial struct ByteEnumerator + { + public bool MoveNext() + { + switch (_state) + { + + case 0: + goto state0; + + case 1: + goto state1; + + case 2: + goto state2; + + case 3: + goto state3; + + case 4: + goto state4; + + case 5: + goto state5; + + case 6: + goto state6; + + case 7: + goto state7; + + case 8: + goto state8; + + case 9: + goto state9; + + case 10: + goto state10; + + case 11: + goto state11; + + case 12: + goto state12; + + case 13: + goto state13; + + case 14: + goto state14; + + case 15: + goto state15; + + case 16: + goto state16; + + case 17: + goto state17; + + case 18: + goto state18; + + case 19: + goto state19; + + case 20: + goto state20; + + case 21: + goto state21; + + case 22: + goto state22; + + case 23: + goto state23; + + case 24: + goto state24; + + case 25: + goto state25; + + case 26: + goto state26; + + case 27: + goto state27; + + case 28: + goto state28; + + case 29: + goto state29; + + default: + goto state_default; + } + + state0: + if (((_bits & 1L) != 0)) + { + _current = new KeyValuePair(_bytesCacheControl, _collection._CacheControl); + _state = 1; + return true; + } + + state1: + if (((_bits & 2L) != 0)) + { + _current = new KeyValuePair(_bytesConnection, _collection._Connection); + _state = 2; + return true; + } + + state2: + if (((_bits & 4L) != 0)) + { + _current = new KeyValuePair(_bytesDate, _collection._Date); + _state = 3; + return true; + } + + state3: + if (((_bits & 8L) != 0)) + { + _current = new KeyValuePair(_bytesKeepAlive, _collection._KeepAlive); + _state = 4; + return true; + } + + state4: + if (((_bits & 16L) != 0)) + { + _current = new KeyValuePair(_bytesPragma, _collection._Pragma); + _state = 5; + return true; + } + + state5: + if (((_bits & 32L) != 0)) + { + _current = new KeyValuePair(_bytesTrailer, _collection._Trailer); + _state = 6; + return true; + } + + state6: + if (((_bits & 64L) != 0)) + { + _current = new KeyValuePair(_bytesTransferEncoding, _collection._TransferEncoding); + _state = 7; + return true; + } + + state7: + if (((_bits & 128L) != 0)) + { + _current = new KeyValuePair(_bytesUpgrade, _collection._Upgrade); + _state = 8; + return true; + } + + state8: + if (((_bits & 256L) != 0)) + { + _current = new KeyValuePair(_bytesVia, _collection._Via); + _state = 9; + return true; + } + + state9: + if (((_bits & 512L) != 0)) + { + _current = new KeyValuePair(_bytesWarning, _collection._Warning); + _state = 10; + return true; + } + + state10: + if (((_bits & 1024L) != 0)) + { + _current = new KeyValuePair(_bytesAllow, _collection._Allow); + _state = 11; + return true; + } + + state11: + if (((_bits & 2048L) != 0)) + { + _current = new KeyValuePair(_bytesContentLength, _collection._ContentLength); + _state = 12; + return true; + } + + state12: + if (((_bits & 4096L) != 0)) + { + _current = new KeyValuePair(_bytesContentType, _collection._ContentType); + _state = 13; + return true; + } + + state13: + if (((_bits & 8192L) != 0)) + { + _current = new KeyValuePair(_bytesContentEncoding, _collection._ContentEncoding); + _state = 14; + return true; + } + + state14: + if (((_bits & 16384L) != 0)) + { + _current = new KeyValuePair(_bytesContentLanguage, _collection._ContentLanguage); + _state = 15; + return true; + } + + state15: + if (((_bits & 32768L) != 0)) + { + _current = new KeyValuePair(_bytesContentLocation, _collection._ContentLocation); + _state = 16; + return true; + } + + state16: + if (((_bits & 65536L) != 0)) + { + _current = new KeyValuePair(_bytesContentMD5, _collection._ContentMD5); + _state = 17; + return true; + } + + state17: + if (((_bits & 131072L) != 0)) + { + _current = new KeyValuePair(_bytesContentRange, _collection._ContentRange); + _state = 18; + return true; + } + + state18: + if (((_bits & 262144L) != 0)) + { + _current = new KeyValuePair(_bytesExpires, _collection._Expires); + _state = 19; + return true; + } + + state19: + if (((_bits & 524288L) != 0)) + { + _current = new KeyValuePair(_bytesLastModified, _collection._LastModified); + _state = 20; + return true; + } + + state20: + if (((_bits & 1048576L) != 0)) + { + _current = new KeyValuePair(_bytesAcceptRanges, _collection._AcceptRanges); + _state = 21; + return true; + } + + state21: + if (((_bits & 2097152L) != 0)) + { + _current = new KeyValuePair(_bytesAge, _collection._Age); + _state = 22; + return true; + } + + state22: + if (((_bits & 4194304L) != 0)) + { + _current = new KeyValuePair(_bytesETag, _collection._ETag); + _state = 23; + return true; + } + + state23: + if (((_bits & 8388608L) != 0)) + { + _current = new KeyValuePair(_bytesLocation, _collection._Location); + _state = 24; + return true; + } + + state24: + if (((_bits & 16777216L) != 0)) + { + _current = new KeyValuePair(_bytesProxyAutheticate, _collection._ProxyAutheticate); + _state = 25; + return true; + } + + state25: + if (((_bits & 33554432L) != 0)) + { + _current = new KeyValuePair(_bytesRetryAfter, _collection._RetryAfter); + _state = 26; + return true; + } + + state26: + if (((_bits & 67108864L) != 0)) + { + _current = new KeyValuePair(_bytesServer, _collection._Server); + _state = 27; + return true; + } + + state27: + if (((_bits & 134217728L) != 0)) + { + _current = new KeyValuePair(_bytesSetCookie, _collection._SetCookie); + _state = 28; + return true; + } + + state28: + if (((_bits & 268435456L) != 0)) + { + _current = new KeyValuePair(_bytesVary, _collection._Vary); + _state = 29; + return true; + } + + state29: + if (((_bits & 536870912L) != 0)) + { + _current = new KeyValuePair(_bytesWWWAuthenticate, _collection._WWWAuthenticate); + _state = 30; + return true; + } + + state_default: + if (!_hasUnknown || !_unknownEnumerator.MoveNext()) + { + _current = default(KeyValuePair); + return false; + } + var kv = _unknownEnumerator.Current; + _current = new KeyValuePair(Encoding.ASCII.GetBytes(kv.Key + ": "), kv.Value); + return true; + } + } } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/FrameHeaders.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameHeaders.cs index a97a09209..8d8ed2a12 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/FrameHeaders.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameHeaders.cs @@ -12,6 +12,12 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { public abstract class FrameHeaders : IHeaderDictionary { + public bool HasConnection { get; protected set; } + + public bool HasTransferEncoding { get; protected set; } + + public bool HasContentLength { get; protected set; } + protected Dictionary MaybeUnknown; protected Dictionary Unknown => MaybeUnknown ?? (MaybeUnknown = new Dictionary(StringComparer.OrdinalIgnoreCase)); diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/FrameResponseHeaders.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameResponseHeaders.cs index 725a64756..1639e6746 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/FrameResponseHeaders.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameResponseHeaders.cs @@ -1,9 +1,9 @@ // 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.Extensions.Primitives; namespace Microsoft.AspNet.Server.Kestrel.Http { @@ -14,11 +14,26 @@ public Enumerator GetEnumerator() return new Enumerator(this); } + public OutputEnumerable AsOutputEnumerable() + { + return new OutputEnumerable() { Headers = this }; + } + protected override IEnumerator> GetEnumeratorFast() { return GetEnumerator(); } + public struct OutputEnumerable + { + public FrameResponseHeaders Headers { get; set; } + + public ByteEnumerator GetEnumerator() + { + return new ByteEnumerator(Headers); + } + } + public partial struct Enumerator : IEnumerator> { private FrameResponseHeaders _collection; @@ -48,6 +63,40 @@ public void Dispose() { } + public void Reset() + { + _state = 0; + } + } + public partial struct ByteEnumerator : IEnumerator> + { + private FrameResponseHeaders _collection; + private long _bits; + private int _state; + private KeyValuePair _current; + private bool _hasUnknown; + private Dictionary.Enumerator _unknownEnumerator; + + internal ByteEnumerator(FrameResponseHeaders collection) + { + _collection = collection; + _bits = collection._bits; + _state = 0; + _current = default(KeyValuePair); + _hasUnknown = collection.MaybeUnknown != null; + _unknownEnumerator = _hasUnknown + ? collection.MaybeUnknown.GetEnumerator() + : default(Dictionary.Enumerator); + } + + public KeyValuePair Current => _current; + + object IEnumerator.Current => _current; + + public void Dispose() + { + } + public void Reset() { _state = 0; 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 0fc5c390e..000000000 --- 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 2f549dc15..000000000 --- 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 fe8fa6e19..679b041a1 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\r\n"); + private static readonly byte[] _bytesStatus101 = Encoding.ASCII.GetBytes("101 Switching Protocols\r\n"); + private static readonly byte[] _bytesStatus102 = Encoding.ASCII.GetBytes("102 Processing\r\n"); + private static readonly byte[] _bytesStatus200 = Encoding.ASCII.GetBytes("200 OK\r\n"); + private static readonly byte[] _bytesStatus201 = Encoding.ASCII.GetBytes("201 Created\r\n"); + private static readonly byte[] _bytesStatus202 = Encoding.ASCII.GetBytes("202 Accepted\r\n"); + private static readonly byte[] _bytesStatus203 = Encoding.ASCII.GetBytes("203 Non-Authoritative Information\r\n"); + private static readonly byte[] _bytesStatus204 = Encoding.ASCII.GetBytes("204 No Content\r\n"); + private static readonly byte[] _bytesStatus205 = Encoding.ASCII.GetBytes("205 Reset Content\r\n"); + private static readonly byte[] _bytesStatus206 = Encoding.ASCII.GetBytes("206 Partial Content\r\n"); + private static readonly byte[] _bytesStatus207 = Encoding.ASCII.GetBytes("207 Multi-Status\r\n"); + private static readonly byte[] _bytesStatus226 = Encoding.ASCII.GetBytes("226 IM Used\r\n"); + private static readonly byte[] _bytesStatus300 = Encoding.ASCII.GetBytes("300 Multiple Choices\r\n"); + private static readonly byte[] _bytesStatus301 = Encoding.ASCII.GetBytes("301 Moved Permanently\r\n"); + private static readonly byte[] _bytesStatus302 = Encoding.ASCII.GetBytes("302 Found\r\n"); + private static readonly byte[] _bytesStatus303 = Encoding.ASCII.GetBytes("303 See Other\r\n"); + private static readonly byte[] _bytesStatus304 = Encoding.ASCII.GetBytes("304 Not Modified\r\n"); + private static readonly byte[] _bytesStatus305 = Encoding.ASCII.GetBytes("305 Use Proxy\r\n"); + private static readonly byte[] _bytesStatus306 = Encoding.ASCII.GetBytes("306 Reserved\r\n"); + private static readonly byte[] _bytesStatus307 = Encoding.ASCII.GetBytes("307 Temporary Redirect\r\n"); + private static readonly byte[] _bytesStatus400 = Encoding.ASCII.GetBytes("400 Bad Request\r\n"); + private static readonly byte[] _bytesStatus401 = Encoding.ASCII.GetBytes("401 Unauthorized\r\n"); + private static readonly byte[] _bytesStatus402 = Encoding.ASCII.GetBytes("402 Payment Required\r\n"); + private static readonly byte[] _bytesStatus403 = Encoding.ASCII.GetBytes("403 Forbidden\r\n"); + private static readonly byte[] _bytesStatus404 = Encoding.ASCII.GetBytes("404 Not Found\r\n"); + private static readonly byte[] _bytesStatus405 = Encoding.ASCII.GetBytes("405 Method Not Allowed\r\n"); + private static readonly byte[] _bytesStatus406 = Encoding.ASCII.GetBytes("406 Not Acceptable\r\n"); + private static readonly byte[] _bytesStatus407 = Encoding.ASCII.GetBytes("407 Proxy Authentication Required\r\n"); + private static readonly byte[] _bytesStatus408 = Encoding.ASCII.GetBytes("408 Request Timeout\r\n"); + private static readonly byte[] _bytesStatus409 = Encoding.ASCII.GetBytes("409 Conflict\r\n"); + private static readonly byte[] _bytesStatus410 = Encoding.ASCII.GetBytes("410 Gone\r\n"); + private static readonly byte[] _bytesStatus411 = Encoding.ASCII.GetBytes("411 Length Required\r\n"); + private static readonly byte[] _bytesStatus412 = Encoding.ASCII.GetBytes("412 Precondition Failed\r\n"); + private static readonly byte[] _bytesStatus413 = Encoding.ASCII.GetBytes("413 Request Entity Too Large\r\n"); + private static readonly byte[] _bytesStatus414 = Encoding.ASCII.GetBytes("414 Request-URI Too Long\r\n"); + private static readonly byte[] _bytesStatus415 = Encoding.ASCII.GetBytes("415 Unsupported Media Type\r\n"); + private static readonly byte[] _bytesStatus416 = Encoding.ASCII.GetBytes("416 Requested Range Not Satisfiable\r\n"); + private static readonly byte[] _bytesStatus417 = Encoding.ASCII.GetBytes("417 Expectation Failed\r\n"); + private static readonly byte[] _bytesStatus418 = Encoding.ASCII.GetBytes("418 I'm a Teapot\r\n"); + private static readonly byte[] _bytesStatus422 = Encoding.ASCII.GetBytes("422 Unprocessable Entity\r\n"); + private static readonly byte[] _bytesStatus423 = Encoding.ASCII.GetBytes("423 Locked\r\n"); + private static readonly byte[] _bytesStatus424 = Encoding.ASCII.GetBytes("424 Failed Dependency\r\n"); + private static readonly byte[] _bytesStatus426 = Encoding.ASCII.GetBytes("426 Upgrade Required\r\n"); + private static readonly byte[] _bytesStatus500 = Encoding.ASCII.GetBytes("500 Internal Server Error\r\n"); + private static readonly byte[] _bytesStatus501 = Encoding.ASCII.GetBytes("501 Not Implemented\r\n"); + private static readonly byte[] _bytesStatus502 = Encoding.ASCII.GetBytes("502 Bad Gateway\r\n"); + private static readonly byte[] _bytesStatus503 = Encoding.ASCII.GetBytes("503 Service Unavailable\r\n"); + private static readonly byte[] _bytesStatus504 = Encoding.ASCII.GetBytes("504 Gateway Timeout\r\n"); + private static readonly byte[] _bytesStatus505 = Encoding.ASCII.GetBytes("505 HTTP Version Not Supported\r\n"); + private static readonly byte[] _bytesStatus506 = Encoding.ASCII.GetBytes("506 Variant Also Negotiates\r\n"); + private static readonly byte[] _bytesStatus507 = Encoding.ASCII.GetBytes("507 Insufficient Storage\r\n"); + private static readonly byte[] _bytesStatus510 = Encoding.ASCII.GetBytes("510 Not Extended\r\n"); + public static string ToStatus(int statusCode, string reasonPhrase = null) { if (string.IsNullOrEmpty(reasonPhrase)) @@ -16,6 +70,123 @@ public static string ToStatus(int statusCode, string reasonPhrase = null) 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) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPool2.cs b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPool2.cs index 86561516c..5dea314cd 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPool2.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPool2.cs @@ -59,7 +59,7 @@ public class MemoryPool2 : IDisposable /// 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/ServiceContext.cs b/src/Microsoft.AspNet.Server.Kestrel/ServiceContext.cs index d3d1539a5..095f37f85 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/ServiceContext.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/ServiceContext.cs @@ -13,13 +13,11 @@ public class ServiceContext { 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 @@ public ServiceContext(ServiceContext context) 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/TestInput.cs b/test/Microsoft.AspNet.Server.KestrelTests/TestInput.cs index 3db112560..11d18f7c4 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/TestInput.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/TestInput.cs @@ -13,12 +13,10 @@ class TestInput : IConnectionControl, IFrameControl { 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 b2def16b4..b0ca2788c 100644 --- a/tools/Microsoft.AspNet.Server.Kestrel.GeneratedCode/KnownHeaders.cs +++ b/tools/Microsoft.AspNet.Server.Kestrel.GeneratedCode/KnownHeaders.cs @@ -167,13 +167,18 @@ public static string GeneratedFile() return $@" using System; using System.Collections.Generic; +using System.Text; using Microsoft.Extensions.Primitives; namespace Microsoft.AspNet.Server.Kestrel.Http {{ {Each(loops, loop => $@" public partial class {loop.ClassName} - {{ + {{{(loop.ClassName == "FrameResponseHeaders" ? + $@"{Each(loop.Headers, header => @" + private static byte[] _bytes" + header.Identifier + " = Encoding.ASCII.GetBytes(\"" + header.Name + ": \");")}" + :"")} + private long _bits = 0; {Each(loop.Headers, header => @" private StringValues _" + header.Identifier + ";")} @@ -186,7 +191,10 @@ public StringValues Header{header.Identifier} }} set {{ - {header.SetBit()}; + {header.SetBit()};{(header.Name == "Connection" ? $@" + HasConnection = true;" : "")}{(header.Name == "Transfer-Encoding" ? $@" + HasTransferEncoding = true;" : "")}{(header.Name == "Content-Length" ? $@" + HasContentLength = true;" : "")} _{header.Identifier} = value; }} }} @@ -258,7 +266,10 @@ protected override void SetValueFast(string key, StringValues value) if (""{header.Name}"".Equals(key, StringComparison.OrdinalIgnoreCase)) {{ {header.SetBit()}; - _{header.Identifier} = value; + _{header.Identifier} = value;{(header.Name == "Connection" ? $@" + HasConnection = true;" : "")}{(header.Name == "Transfer-Encoding" ? $@" + HasTransferEncoding = true;" : "")}{(header.Name == "Content-Length" ? $@" + HasContentLength = true;" : "")} return; }} ")}}} @@ -280,7 +291,10 @@ protected override void AddValueFast(string key, StringValues value) throw new ArgumentException(""An item with the same key has already been added.""); }} {header.SetBit()}; - _{header.Identifier} = value; + _{header.Identifier} = value;{(header.Name == "Connection" ? $@" + HasConnection = true;" : "")}{(header.Name == "Transfer-Encoding" ? $@" + HasTransferEncoding = true;" : "")}{(header.Name == "Content-Length" ? $@" + HasContentLength = true;" : "")} return; }} ")}}} @@ -300,7 +314,10 @@ protected override bool RemoveFast(string key) if ({header.TestBit()}) {{ {header.ClearBit()}; - _{header.Identifier} = StringValues.Empty; + _{header.Identifier} = StringValues.Empty;{(header.Name == "Connection" ? $@" + HasConnection = false;" : "")}{(header.Name == "Transfer-Encoding" ? $@" + HasTransferEncoding = false;" : "")}{(header.Name == "Content-Length" ? $@" + HasContentLength = false;" : "")} return true; }} else @@ -320,6 +337,9 @@ protected override void ClearFast() {Each(loop.Headers, header => $@" _{header.Identifier} = StringValues.Empty;")} MaybeUnknown?.Clear(); + HasConnection = false; + HasTransferEncoding = false; + HasContentLength = false; }} protected override void CopyToFast(KeyValuePair[] array, int arrayIndex) @@ -359,7 +379,10 @@ public unsafe void Append(byte[] keyBytes, int keyOffset, int keyLength, string }} else {{ - {header.SetBit()}; + {header.SetBit()};{(header.Name == "Connection" ? $@" + HasConnection = true;" : "")}{(header.Name == "Transfer-Encoding" ? $@" + HasTransferEncoding = true;" : "")}{(header.Name == "Content-Length" ? $@" + HasContentLength = true;" : "")} _{header.Identifier} = new StringValues(value); }} return; @@ -405,6 +428,41 @@ public bool MoveNext() return true; }} }} +{(loop.ClassName == "FrameResponseHeaders" ?$@" + public partial struct ByteEnumerator + {{ + public bool MoveNext() + {{ + switch (_state) + {{ + {Each(loop.Headers, header => $@" + case {header.Index}: + goto state{header.Index}; + ")} + default: + goto state_default; + }} + {Each(loop.Headers, header => $@" + state{header.Index}: + if ({header.TestBit()}) + {{ + _current = new KeyValuePair(_bytes{header.Identifier}, _collection._{header.Identifier}); + _state = {header.Index + 1}; + return true; + }} + ")} + state_default: + if (!_hasUnknown || !_unknownEnumerator.MoveNext()) + {{ + _current = default(KeyValuePair); + return false; + }} + var kv = _unknownEnumerator.Current; + _current = new KeyValuePair(Encoding.ASCII.GetBytes(kv.Key + "": ""), kv.Value); + return true; + }} + }}" + : "")} }} ")}}} "; From 72cfbab612b3627272b1d8137bc119ff78310a92 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 11 Nov 2015 21:31:24 +0000 Subject: [PATCH 03/24] Precompute Http Version bytes --- .../Http/Frame.cs | 56 ++++++++++++++++--- .../Http/FrameHeaders.cs | 8 +-- 2 files changed, 52 insertions(+), 12 deletions(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs index 54c852e16..0b6e45c3e 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs @@ -32,6 +32,8 @@ public partial class Frame : FrameContext, IFrameControl private static readonly byte[] _bytesConnectionClose = Encoding.ASCII.GetBytes("Connection: close\r\n\r\n"); private static readonly byte[] _bytesConnectionKeepAlive = Encoding.ASCII.GetBytes("Connection: keep-alive\r\n\r\n"); private static readonly byte[] _bytesTransferEncodingChunked = Encoding.ASCII.GetBytes("Transfer-Encoding: chunked\r\n"); + 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("Content-Length: 0\r\n"); private static readonly byte[] _bytesSpace = Encoding.ASCII.GetBytes(" "); @@ -53,6 +55,8 @@ public partial class Frame : FrameContext, IFrameControl private bool _keepAlive; private bool _autoChunk; private Exception _applicationException; + + private HttpVersionType _httpVersion; private readonly IPEndPoint _localEndPoint; private readonly IPEndPoint _remoteEndPoint; @@ -78,7 +82,36 @@ public Frame(ConnectionContext context, 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 MessageBody MessageBody { get; set; } public Stream RequestBody { get; set; } @@ -113,7 +146,7 @@ public void Reset() RequestUri = null; Path = null; QueryString = null; - HttpVersion = null; + _httpVersion = HttpVersionType.Unknown; RequestHeaders = _requestHeaders; MessageBody = null; RequestBody = null; @@ -454,7 +487,7 @@ public void ProduceContinue() 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)) { @@ -575,8 +608,8 @@ private Task CreateResponseHeader( { var blockRemaining = memoryBlock.Data.Count; - OutputAsciiBlock(HttpVersion, memoryBlock, SocketOutput); - OutputAsciiBlock(_bytesSpace, memoryBlock, SocketOutput); + OutputAsciiBlock(_httpVersion == HttpVersionType.Http1_1 ? _bytesHttpVersion1_1 : _bytesHttpVersion1_0, memoryBlock, SocketOutput); + OutputAsciiBlock(statusBytes, memoryBlock, SocketOutput); foreach (var header in _responseHeaders.AsOutputEnumerable()) @@ -610,7 +643,7 @@ private Task CreateResponseHeader( } else { - if (HttpVersion == "HTTP/1.1") + if (_httpVersion == HttpVersionType.Http1_1) { _autoChunk = true; OutputAsciiBlock(_bytesTransferEncodingChunked, memoryBlock, SocketOutput); @@ -622,11 +655,11 @@ private Task CreateResponseHeader( } } - if (_keepAlive == false && _responseHeaders.HasConnection == false && HttpVersion == "HTTP/1.1") + if (_keepAlive == false && _responseHeaders.HasConnection == false && _httpVersion == HttpVersionType.Http1_1) { OutputAsciiBlock(_bytesConnectionClose, memoryBlock, SocketOutput); } - else if (_keepAlive && _responseHeaders.HasConnection == false && HttpVersion == "HTTP/1.0") + else if (_keepAlive && _responseHeaders.HasConnection == false && _httpVersion == HttpVersionType.Http1_0) { OutputAsciiBlock(_bytesConnectionKeepAlive, memoryBlock, SocketOutput); } @@ -849,5 +882,12 @@ private void ReportApplicationError(Exception ex) _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.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameHeaders.cs index 8d8ed2a12..552a146c8 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/FrameHeaders.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameHeaders.cs @@ -12,16 +12,16 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { public abstract class FrameHeaders : IHeaderDictionary { + protected Dictionary MaybeUnknown; + + protected Dictionary Unknown => MaybeUnknown ?? (MaybeUnknown = new Dictionary(StringComparer.OrdinalIgnoreCase)); + public bool HasConnection { get; protected set; } public bool HasTransferEncoding { get; protected set; } public bool HasContentLength { get; protected set; } - protected Dictionary MaybeUnknown; - - protected Dictionary Unknown => MaybeUnknown ?? (MaybeUnknown = new Dictionary(StringComparer.OrdinalIgnoreCase)); - StringValues IHeaderDictionary.this[string key] { get From 9e5729814370b62e50ec9c2d6d94cac3b2f7a96f Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 12 Nov 2015 00:48:44 +0000 Subject: [PATCH 04/24] Use memmove rather than Array.Copy [Buffer.BlockCopy](https://github.com/dotnet/coreclr/blob/bc146608854d1db9cdbcc0b08029a87754e12b49/src/vm/comutilnative.cpp#L1451) is memmove; and a larger throughput intrinsic [Array.Copy](https://github.com/dotnet/coreclr/blob/bc146608854d1db9cdbcc0b08029a87754e12b49/src/classlibnative/bcltype/arraynative.cpp#L895) is 16 byte loop --- src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs index d2534206e..757613741 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs @@ -52,7 +52,7 @@ public Task WriteAsync( if (buffer.Array != null) { var copy = new byte[buffer.Count]; - Array.Copy(buffer.Array, buffer.Offset, copy, 0, buffer.Count); + Buffer.BlockCopy(buffer.Array, buffer.Offset, copy, 0, buffer.Count); buffer = new ArraySegment(copy); _log.ConnectionWrite(_connectionId, buffer.Count); } From fcb26f656a77968f25568a94c0d070745bcc69ce Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 12 Nov 2015 07:11:44 +0000 Subject: [PATCH 05/24] Precompute Server and Date bytes --- .../Http/DateHeaderValueManager.cs | 14 + .../Http/Frame.cs | 18 +- .../Http/FrameHeaders.Generated.cs | 674 ++++++++++-------- .../KnownHeaders.cs | 114 ++- 4 files changed, 489 insertions(+), 331 deletions(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/DateHeaderValueManager.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/DateHeaderValueManager.cs index c471067ec..e9695ce26 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 @@ public class DateHeaderValueManager : IDisposable private readonly TimeSpan _timerInterval; private volatile string _dateValue; + private volatile int _activeDateBytes; + private byte[] _dateBytes0 = Encoding.ASCII.GetBytes("DDD, dd mmm yyyy hh:mm:ss GMT"); + private byte[] _dateBytes1 = Encoding.ASCII.GetBytes("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 @@ public virtual string GetDateHeaderValue() return _dateValue ?? _systemClock.UtcNow.ToString(Constants.RFC1123DateFormat); } + public byte[] GetDateHeaderValueBytes() + { + PumpTimer(); + return _activeDateBytes == 0 ? _dateBytes0 : _dateBytes1; + } + /// /// Releases all resources used by the current instance of . /// @@ -92,6 +102,8 @@ private void PumpTimer() // 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 ^ 1) == 0 ? _dateBytes0 : _dateBytes1, 0); + _activeDateBytes ^= 1; _dateValueTimer = new Timer(UpdateDateValue, state: null, dueTime: _timerInterval, period: _timerInterval); } } @@ -105,6 +117,8 @@ private void UpdateDateValue(object state) // 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 ^ 1) == 0 ? _dateBytes0 : _dateBytes1, 0); + _activeDateBytes ^= 1; if (_hadRequestsSinceLastTimerTick) { diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs index 0b6e45c3e..84aa103ed 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs @@ -36,6 +36,8 @@ public partial class Frame : FrameContext, IFrameControl private static readonly byte[] _bytesHttpVersion1_1 = Encoding.ASCII.GetBytes("HTTP/1.1 "); private static readonly byte[] _bytesContentLengthZero = Encoding.ASCII.GetBytes("Content-Length: 0\r\n"); private static readonly byte[] _bytesSpace = Encoding.ASCII.GetBytes(" "); + private static readonly byte[] _bytesServer = Encoding.ASCII.GetBytes("Server: Kestrel\r\n"); + private static readonly byte[] _bytesDate = Encoding.ASCII.GetBytes("Date: "); private readonly object _onStartingSync = new Object(); private readonly object _onCompletedSync = new Object(); @@ -112,6 +114,7 @@ public string HttpVersion } } } + public IHeaderDictionary RequestHeaders { get; set; } public MessageBody MessageBody { get; set; } public Stream RequestBody { get; set; } @@ -176,8 +179,8 @@ public void Reset() public void ResetResponseHeaders() { _responseHeaders.Reset(); - _responseHeaders.HeaderServer = "Kestrel"; - _responseHeaders.HeaderDate = DateHeaderValueManager.GetDateHeaderValue(); + _responseHeaders.HasDefaultDate = true; + _responseHeaders.HasDefaultServer = true; } /// @@ -612,6 +615,13 @@ private Task CreateResponseHeader( OutputAsciiBlock(statusBytes, memoryBlock, SocketOutput); + if (_responseHeaders.HasDefaultDate) + { + OutputAsciiBlock(_bytesDate, memoryBlock, SocketOutput); + OutputAsciiBlock(DateHeaderValueManager.GetDateHeaderValueBytes(), memoryBlock, SocketOutput); + OutputAsciiBlock(_bytesEndLine, memoryBlock, SocketOutput); + } + foreach (var header in _responseHeaders.AsOutputEnumerable()) { foreach (var value in header.Value) @@ -625,7 +635,11 @@ private Task CreateResponseHeader( _keepAlive = false; } } + } + if (_responseHeaders.HasDefaultServer) + { + OutputAsciiBlock(_bytesServer, memoryBlock, SocketOutput); } if (_keepAlive && !_responseHeaders.HasTransferEncoding && !_responseHeaders.HasContentLength) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/FrameHeaders.Generated.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameHeaders.Generated.cs index 2075aa30f..d67f3f025 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/FrameHeaders.Generated.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameHeaders.Generated.cs @@ -2,14 +2,14 @@ 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; @@ -66,7 +66,7 @@ public StringValues HeaderCacheControl _CacheControl = value; } } - + public StringValues HeaderConnection { get @@ -80,7 +80,7 @@ public StringValues HeaderConnection _Connection = value; } } - + public StringValues HeaderDate { get @@ -93,7 +93,7 @@ public StringValues HeaderDate _Date = value; } } - + public StringValues HeaderKeepAlive { get @@ -106,7 +106,7 @@ public StringValues HeaderKeepAlive _KeepAlive = value; } } - + public StringValues HeaderPragma { get @@ -119,7 +119,7 @@ public StringValues HeaderPragma _Pragma = value; } } - + public StringValues HeaderTrailer { get @@ -132,7 +132,7 @@ public StringValues HeaderTrailer _Trailer = value; } } - + public StringValues HeaderTransferEncoding { get @@ -146,7 +146,7 @@ public StringValues HeaderTransferEncoding _TransferEncoding = value; } } - + public StringValues HeaderUpgrade { get @@ -159,7 +159,7 @@ public StringValues HeaderUpgrade _Upgrade = value; } } - + public StringValues HeaderVia { get @@ -172,7 +172,7 @@ public StringValues HeaderVia _Via = value; } } - + public StringValues HeaderWarning { get @@ -185,7 +185,7 @@ public StringValues HeaderWarning _Warning = value; } } - + public StringValues HeaderAllow { get @@ -198,7 +198,7 @@ public StringValues HeaderAllow _Allow = value; } } - + public StringValues HeaderContentLength { get @@ -212,7 +212,7 @@ public StringValues HeaderContentLength _ContentLength = value; } } - + public StringValues HeaderContentType { get @@ -225,7 +225,7 @@ public StringValues HeaderContentType _ContentType = value; } } - + public StringValues HeaderContentEncoding { get @@ -238,7 +238,7 @@ public StringValues HeaderContentEncoding _ContentEncoding = value; } } - + public StringValues HeaderContentLanguage { get @@ -251,7 +251,7 @@ public StringValues HeaderContentLanguage _ContentLanguage = value; } } - + public StringValues HeaderContentLocation { get @@ -264,7 +264,7 @@ public StringValues HeaderContentLocation _ContentLocation = value; } } - + public StringValues HeaderContentMD5 { get @@ -277,7 +277,7 @@ public StringValues HeaderContentMD5 _ContentMD5 = value; } } - + public StringValues HeaderContentRange { get @@ -290,7 +290,7 @@ public StringValues HeaderContentRange _ContentRange = value; } } - + public StringValues HeaderExpires { get @@ -303,7 +303,7 @@ public StringValues HeaderExpires _Expires = value; } } - + public StringValues HeaderLastModified { get @@ -316,7 +316,7 @@ public StringValues HeaderLastModified _LastModified = value; } } - + public StringValues HeaderAccept { get @@ -329,7 +329,7 @@ public StringValues HeaderAccept _Accept = value; } } - + public StringValues HeaderAcceptCharset { get @@ -342,7 +342,7 @@ public StringValues HeaderAcceptCharset _AcceptCharset = value; } } - + public StringValues HeaderAcceptEncoding { get @@ -355,7 +355,7 @@ public StringValues HeaderAcceptEncoding _AcceptEncoding = value; } } - + public StringValues HeaderAcceptLanguage { get @@ -368,7 +368,7 @@ public StringValues HeaderAcceptLanguage _AcceptLanguage = value; } } - + public StringValues HeaderAuthorization { get @@ -381,7 +381,7 @@ public StringValues HeaderAuthorization _Authorization = value; } } - + public StringValues HeaderCookie { get @@ -394,7 +394,7 @@ public StringValues HeaderCookie _Cookie = value; } } - + public StringValues HeaderExpect { get @@ -407,7 +407,7 @@ public StringValues HeaderExpect _Expect = value; } } - + public StringValues HeaderFrom { get @@ -420,7 +420,7 @@ public StringValues HeaderFrom _From = value; } } - + public StringValues HeaderHost { get @@ -433,7 +433,7 @@ public StringValues HeaderHost _Host = value; } } - + public StringValues HeaderIfMatch { get @@ -446,7 +446,7 @@ public StringValues HeaderIfMatch _IfMatch = value; } } - + public StringValues HeaderIfModifiedSince { get @@ -459,7 +459,7 @@ public StringValues HeaderIfModifiedSince _IfModifiedSince = value; } } - + public StringValues HeaderIfNoneMatch { get @@ -472,7 +472,7 @@ public StringValues HeaderIfNoneMatch _IfNoneMatch = value; } } - + public StringValues HeaderIfRange { get @@ -485,7 +485,7 @@ public StringValues HeaderIfRange _IfRange = value; } } - + public StringValues HeaderIfUnmodifiedSince { get @@ -498,7 +498,7 @@ public StringValues HeaderIfUnmodifiedSince _IfUnmodifiedSince = value; } } - + public StringValues HeaderMaxForwards { get @@ -511,7 +511,7 @@ public StringValues HeaderMaxForwards _MaxForwards = value; } } - + public StringValues HeaderProxyAuthorization { get @@ -524,7 +524,7 @@ public StringValues HeaderProxyAuthorization _ProxyAuthorization = value; } } - + public StringValues HeaderReferer { get @@ -537,7 +537,7 @@ public StringValues HeaderReferer _Referer = value; } } - + public StringValues HeaderRange { get @@ -550,7 +550,7 @@ public StringValues HeaderRange _Range = value; } } - + public StringValues HeaderTE { get @@ -563,7 +563,7 @@ public StringValues HeaderTE _TE = value; } } - + public StringValues HeaderTranslate { get @@ -576,7 +576,7 @@ public StringValues HeaderTranslate _Translate = value; } } - + public StringValues HeaderUserAgent { get @@ -589,10 +589,11 @@ public StringValues HeaderUserAgent _UserAgent = value; } } - + protected override int GetCountFast() { - return BitCount(_bits) + (MaybeUnknown?.Count ?? 0); + return BitCount(_bits) + + (MaybeUnknown?.Count ?? 0); } protected override StringValues GetValueFast(string key) @@ -601,7 +602,7 @@ protected override StringValues GetValueFast(string key) { case 13: { - if ("Cache-Control".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Cache-Control".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 1L) != 0)) { @@ -613,7 +614,7 @@ protected override StringValues GetValueFast(string key) } } - if ("Content-Range".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Range".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 131072L) != 0)) { @@ -625,7 +626,7 @@ protected override StringValues GetValueFast(string key) } } - if ("Last-Modified".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Last-Modified".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 524288L) != 0)) { @@ -637,7 +638,7 @@ protected override StringValues GetValueFast(string key) } } - if ("Authorization".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Authorization".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 16777216L) != 0)) { @@ -649,7 +650,7 @@ protected override StringValues GetValueFast(string key) } } - if ("If-None-Match".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("If-None-Match".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 2147483648L) != 0)) { @@ -662,10 +663,10 @@ protected override StringValues GetValueFast(string key) } } break; - + case 10: { - if ("Connection".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Connection".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 2L) != 0)) { @@ -677,7 +678,7 @@ protected override StringValues GetValueFast(string key) } } - if ("Keep-Alive".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Keep-Alive".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 8L) != 0)) { @@ -689,7 +690,7 @@ protected override StringValues GetValueFast(string key) } } - if ("User-Agent".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("User-Agent".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 1099511627776L) != 0)) { @@ -702,10 +703,10 @@ protected override StringValues GetValueFast(string key) } } break; - + case 4: { - if ("Date".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Date".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 4L) != 0)) { @@ -717,7 +718,7 @@ protected override StringValues GetValueFast(string key) } } - if ("From".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("From".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 134217728L) != 0)) { @@ -729,7 +730,7 @@ protected override StringValues GetValueFast(string key) } } - if ("Host".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Host".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 268435456L) != 0)) { @@ -742,10 +743,10 @@ protected override StringValues GetValueFast(string key) } } break; - + case 6: { - if ("Pragma".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Pragma".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 16L) != 0)) { @@ -757,7 +758,7 @@ protected override StringValues GetValueFast(string key) } } - if ("Accept".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Accept".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 1048576L) != 0)) { @@ -769,7 +770,7 @@ protected override StringValues GetValueFast(string key) } } - if ("Cookie".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Cookie".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 33554432L) != 0)) { @@ -781,7 +782,7 @@ protected override StringValues GetValueFast(string key) } } - if ("Expect".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Expect".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 67108864L) != 0)) { @@ -794,10 +795,10 @@ protected override StringValues GetValueFast(string key) } } break; - + case 7: { - if ("Trailer".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Trailer".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 32L) != 0)) { @@ -809,7 +810,7 @@ protected override StringValues GetValueFast(string key) } } - if ("Upgrade".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Upgrade".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 128L) != 0)) { @@ -821,7 +822,7 @@ protected override StringValues GetValueFast(string key) } } - if ("Warning".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Warning".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 512L) != 0)) { @@ -833,7 +834,7 @@ protected override StringValues GetValueFast(string key) } } - if ("Expires".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Expires".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 262144L) != 0)) { @@ -845,7 +846,7 @@ protected override StringValues GetValueFast(string key) } } - if ("Referer".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Referer".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 68719476736L) != 0)) { @@ -858,10 +859,10 @@ protected override StringValues GetValueFast(string key) } } break; - + case 17: { - if ("Transfer-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Transfer-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 64L) != 0)) { @@ -873,7 +874,7 @@ protected override StringValues GetValueFast(string key) } } - if ("If-Modified-Since".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("If-Modified-Since".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 1073741824L) != 0)) { @@ -886,10 +887,10 @@ protected override StringValues GetValueFast(string key) } } break; - + case 3: { - if ("Via".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Via".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 256L) != 0)) { @@ -902,10 +903,10 @@ protected override StringValues GetValueFast(string key) } } break; - + case 5: { - if ("Allow".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Allow".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 1024L) != 0)) { @@ -917,7 +918,7 @@ protected override StringValues GetValueFast(string key) } } - if ("Range".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Range".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 137438953472L) != 0)) { @@ -930,10 +931,10 @@ protected override StringValues GetValueFast(string key) } } break; - + case 14: { - if ("Content-Length".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Length".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 2048L) != 0)) { @@ -945,7 +946,7 @@ protected override StringValues GetValueFast(string key) } } - if ("Accept-Charset".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Accept-Charset".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 2097152L) != 0)) { @@ -958,10 +959,10 @@ protected override StringValues GetValueFast(string key) } } break; - + case 12: { - if ("Content-Type".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Type".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 4096L) != 0)) { @@ -973,7 +974,7 @@ protected override StringValues GetValueFast(string key) } } - if ("Max-Forwards".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Max-Forwards".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 17179869184L) != 0)) { @@ -986,10 +987,10 @@ protected override StringValues GetValueFast(string key) } } break; - + case 16: { - if ("Content-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 8192L) != 0)) { @@ -1001,7 +1002,7 @@ protected override StringValues GetValueFast(string key) } } - if ("Content-Language".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Language".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 16384L) != 0)) { @@ -1013,7 +1014,7 @@ protected override StringValues GetValueFast(string key) } } - if ("Content-Location".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Location".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 32768L) != 0)) { @@ -1026,10 +1027,10 @@ protected override StringValues GetValueFast(string key) } } break; - + case 11: { - if ("Content-MD5".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-MD5".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 65536L) != 0)) { @@ -1042,10 +1043,10 @@ protected override StringValues GetValueFast(string key) } } break; - + case 15: { - if ("Accept-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Accept-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 4194304L) != 0)) { @@ -1057,7 +1058,7 @@ protected override StringValues GetValueFast(string key) } } - if ("Accept-Language".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Accept-Language".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 8388608L) != 0)) { @@ -1070,10 +1071,10 @@ protected override StringValues GetValueFast(string key) } } break; - + case 8: { - if ("If-Match".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("If-Match".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 536870912L) != 0)) { @@ -1085,7 +1086,7 @@ protected override StringValues GetValueFast(string key) } } - if ("If-Range".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("If-Range".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 4294967296L) != 0)) { @@ -1098,10 +1099,10 @@ protected override StringValues GetValueFast(string key) } } break; - + case 19: { - if ("If-Unmodified-Since".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("If-Unmodified-Since".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 8589934592L) != 0)) { @@ -1113,7 +1114,7 @@ protected override StringValues GetValueFast(string key) } } - if ("Proxy-Authorization".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Proxy-Authorization".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 34359738368L) != 0)) { @@ -1126,10 +1127,10 @@ protected override StringValues GetValueFast(string key) } } break; - + case 2: { - if ("TE".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("TE".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 274877906944L) != 0)) { @@ -1142,10 +1143,10 @@ protected override StringValues GetValueFast(string key) } } break; - + case 9: { - if ("Translate".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Translate".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 549755813888L) != 0)) { @@ -1158,7 +1159,7 @@ protected override StringValues GetValueFast(string key) } } break; - } +} if (MaybeUnknown == null) { throw new System.Collections.Generic.KeyNotFoundException(); @@ -1243,7 +1244,7 @@ protected override bool TryGetValueFast(string key, out StringValues value) } } break; - + case 10: { if ("Connection".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1289,7 +1290,7 @@ protected override bool TryGetValueFast(string key, out StringValues value) } } break; - + case 4: { if ("Date".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1335,7 +1336,7 @@ protected override bool TryGetValueFast(string key, out StringValues value) } } break; - + case 6: { if ("Pragma".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1395,7 +1396,7 @@ protected override bool TryGetValueFast(string key, out StringValues value) } } break; - + case 7: { if ("Trailer".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1469,7 +1470,7 @@ protected override bool TryGetValueFast(string key, out StringValues value) } } break; - + case 17: { if ("Transfer-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1501,7 +1502,7 @@ protected override bool TryGetValueFast(string key, out StringValues value) } } break; - + case 3: { if ("Via".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1519,7 +1520,7 @@ protected override bool TryGetValueFast(string key, out StringValues value) } } break; - + case 5: { if ("Allow".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1551,7 +1552,7 @@ protected override bool TryGetValueFast(string key, out StringValues value) } } break; - + case 14: { if ("Content-Length".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1583,7 +1584,7 @@ protected override bool TryGetValueFast(string key, out StringValues value) } } break; - + case 12: { if ("Content-Type".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1615,7 +1616,7 @@ protected override bool TryGetValueFast(string key, out StringValues value) } } break; - + case 16: { if ("Content-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1661,7 +1662,7 @@ protected override bool TryGetValueFast(string key, out StringValues value) } } break; - + case 11: { if ("Content-MD5".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1679,7 +1680,7 @@ protected override bool TryGetValueFast(string key, out StringValues value) } } break; - + case 15: { if ("Accept-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1711,7 +1712,7 @@ protected override bool TryGetValueFast(string key, out StringValues value) } } break; - + case 8: { if ("If-Match".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1743,7 +1744,7 @@ protected override bool TryGetValueFast(string key, out StringValues value) } } break; - + case 19: { if ("If-Unmodified-Since".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1775,7 +1776,7 @@ protected override bool TryGetValueFast(string key, out StringValues value) } } break; - + case 2: { if ("TE".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1793,7 +1794,7 @@ protected override bool TryGetValueFast(string key, out StringValues value) } } break; - + case 9: { if ("Translate".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1811,7 +1812,7 @@ protected override bool TryGetValueFast(string key, out StringValues value) } } break; - } +} value = StringValues.Empty; return MaybeUnknown?.TryGetValue(key, out value) ?? false; } @@ -1858,7 +1859,7 @@ protected override void SetValueFast(string key, StringValues value) } } break; - + case 10: { if ("Connection".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1884,7 +1885,7 @@ protected override void SetValueFast(string key, StringValues value) } } break; - + case 4: { if ("Date".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1909,7 +1910,7 @@ protected override void SetValueFast(string key, StringValues value) } } break; - + case 6: { if ("Pragma".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1941,7 +1942,7 @@ protected override void SetValueFast(string key, StringValues value) } } break; - + case 7: { if ("Trailer".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1980,7 +1981,7 @@ protected override void SetValueFast(string key, StringValues value) } } break; - + case 17: { if ("Transfer-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -1999,7 +2000,7 @@ protected override void SetValueFast(string key, StringValues value) } } break; - + case 3: { if ("Via".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -2010,7 +2011,7 @@ protected override void SetValueFast(string key, StringValues value) } } break; - + case 5: { if ("Allow".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -2028,7 +2029,7 @@ protected override void SetValueFast(string key, StringValues value) } } break; - + case 14: { if ("Content-Length".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -2047,7 +2048,7 @@ protected override void SetValueFast(string key, StringValues value) } } break; - + case 12: { if ("Content-Type".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -2065,7 +2066,7 @@ protected override void SetValueFast(string key, StringValues value) } } break; - + case 16: { if ("Content-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -2090,7 +2091,7 @@ protected override void SetValueFast(string key, StringValues value) } } break; - + case 11: { if ("Content-MD5".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -2101,7 +2102,7 @@ protected override void SetValueFast(string key, StringValues value) } } break; - + case 15: { if ("Accept-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -2119,7 +2120,7 @@ protected override void SetValueFast(string key, StringValues value) } } break; - + case 8: { if ("If-Match".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -2137,7 +2138,7 @@ protected override void SetValueFast(string key, StringValues value) } } break; - + case 19: { if ("If-Unmodified-Since".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -2155,7 +2156,7 @@ protected override void SetValueFast(string key, StringValues value) } } break; - + case 2: { if ("TE".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -2166,7 +2167,7 @@ protected override void SetValueFast(string key, StringValues value) } } break; - + case 9: { if ("Translate".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -2177,7 +2178,7 @@ protected override void SetValueFast(string key, StringValues value) } } break; - } +} Unknown[key] = value; } @@ -2187,7 +2188,7 @@ protected override void AddValueFast(string key, StringValues value) { case 13: { - if ("Cache-Control".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Cache-Control".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 1L) != 0)) { @@ -2198,7 +2199,7 @@ protected override void AddValueFast(string key, StringValues value) return; } - if ("Content-Range".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Range".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 131072L) != 0)) { @@ -2209,7 +2210,7 @@ protected override void AddValueFast(string key, StringValues value) return; } - if ("Last-Modified".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Last-Modified".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 524288L) != 0)) { @@ -2220,7 +2221,7 @@ protected override void AddValueFast(string key, StringValues value) return; } - if ("Authorization".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Authorization".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 16777216L) != 0)) { @@ -2231,7 +2232,7 @@ protected override void AddValueFast(string key, StringValues value) return; } - if ("If-None-Match".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("If-None-Match".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 2147483648L) != 0)) { @@ -2246,7 +2247,7 @@ protected override void AddValueFast(string key, StringValues value) case 10: { - if ("Connection".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Connection".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 2L) != 0)) { @@ -2258,7 +2259,7 @@ protected override void AddValueFast(string key, StringValues value) return; } - if ("Keep-Alive".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Keep-Alive".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 8L) != 0)) { @@ -2269,7 +2270,7 @@ protected override void AddValueFast(string key, StringValues value) return; } - if ("User-Agent".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("User-Agent".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 1099511627776L) != 0)) { @@ -2284,7 +2285,7 @@ protected override void AddValueFast(string key, StringValues value) case 4: { - if ("Date".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Date".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 4L) != 0)) { @@ -2295,7 +2296,7 @@ protected override void AddValueFast(string key, StringValues value) return; } - if ("From".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("From".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 134217728L) != 0)) { @@ -2306,7 +2307,7 @@ protected override void AddValueFast(string key, StringValues value) return; } - if ("Host".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Host".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 268435456L) != 0)) { @@ -2321,7 +2322,7 @@ protected override void AddValueFast(string key, StringValues value) case 6: { - if ("Pragma".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Pragma".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 16L) != 0)) { @@ -2332,7 +2333,7 @@ protected override void AddValueFast(string key, StringValues value) return; } - if ("Accept".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Accept".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 1048576L) != 0)) { @@ -2343,7 +2344,7 @@ protected override void AddValueFast(string key, StringValues value) return; } - if ("Cookie".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Cookie".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 33554432L) != 0)) { @@ -2354,7 +2355,7 @@ protected override void AddValueFast(string key, StringValues value) return; } - if ("Expect".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Expect".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 67108864L) != 0)) { @@ -2369,7 +2370,7 @@ protected override void AddValueFast(string key, StringValues value) case 7: { - if ("Trailer".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Trailer".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 32L) != 0)) { @@ -2380,7 +2381,7 @@ protected override void AddValueFast(string key, StringValues value) return; } - if ("Upgrade".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Upgrade".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 128L) != 0)) { @@ -2391,7 +2392,7 @@ protected override void AddValueFast(string key, StringValues value) return; } - if ("Warning".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Warning".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 512L) != 0)) { @@ -2402,7 +2403,7 @@ protected override void AddValueFast(string key, StringValues value) return; } - if ("Expires".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Expires".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 262144L) != 0)) { @@ -2413,7 +2414,7 @@ protected override void AddValueFast(string key, StringValues value) return; } - if ("Referer".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Referer".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 68719476736L) != 0)) { @@ -2428,7 +2429,7 @@ protected override void AddValueFast(string key, StringValues value) case 17: { - if ("Transfer-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Transfer-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 64L) != 0)) { @@ -2440,7 +2441,7 @@ protected override void AddValueFast(string key, StringValues value) return; } - if ("If-Modified-Since".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("If-Modified-Since".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 1073741824L) != 0)) { @@ -2455,7 +2456,7 @@ protected override void AddValueFast(string key, StringValues value) case 3: { - if ("Via".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Via".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 256L) != 0)) { @@ -2470,7 +2471,7 @@ protected override void AddValueFast(string key, StringValues value) case 5: { - if ("Allow".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Allow".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 1024L) != 0)) { @@ -2481,7 +2482,7 @@ protected override void AddValueFast(string key, StringValues value) return; } - if ("Range".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Range".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 137438953472L) != 0)) { @@ -2496,7 +2497,7 @@ protected override void AddValueFast(string key, StringValues value) case 14: { - if ("Content-Length".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Length".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 2048L) != 0)) { @@ -2508,7 +2509,7 @@ protected override void AddValueFast(string key, StringValues value) return; } - if ("Accept-Charset".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Accept-Charset".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 2097152L) != 0)) { @@ -2523,7 +2524,7 @@ protected override void AddValueFast(string key, StringValues value) case 12: { - if ("Content-Type".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Type".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 4096L) != 0)) { @@ -2534,7 +2535,7 @@ protected override void AddValueFast(string key, StringValues value) return; } - if ("Max-Forwards".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Max-Forwards".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 17179869184L) != 0)) { @@ -2549,7 +2550,7 @@ protected override void AddValueFast(string key, StringValues value) case 16: { - if ("Content-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 8192L) != 0)) { @@ -2560,7 +2561,7 @@ protected override void AddValueFast(string key, StringValues value) return; } - if ("Content-Language".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Language".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 16384L) != 0)) { @@ -2571,7 +2572,7 @@ protected override void AddValueFast(string key, StringValues value) return; } - if ("Content-Location".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Location".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 32768L) != 0)) { @@ -2586,7 +2587,7 @@ protected override void AddValueFast(string key, StringValues value) case 11: { - if ("Content-MD5".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-MD5".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 65536L) != 0)) { @@ -2601,7 +2602,7 @@ protected override void AddValueFast(string key, StringValues value) case 15: { - if ("Accept-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Accept-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 4194304L) != 0)) { @@ -2612,7 +2613,7 @@ protected override void AddValueFast(string key, StringValues value) return; } - if ("Accept-Language".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Accept-Language".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 8388608L) != 0)) { @@ -2627,7 +2628,7 @@ protected override void AddValueFast(string key, StringValues value) case 8: { - if ("If-Match".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("If-Match".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 536870912L) != 0)) { @@ -2638,7 +2639,7 @@ protected override void AddValueFast(string key, StringValues value) return; } - if ("If-Range".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("If-Range".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 4294967296L) != 0)) { @@ -2653,7 +2654,7 @@ protected override void AddValueFast(string key, StringValues value) case 19: { - if ("If-Unmodified-Since".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("If-Unmodified-Since".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 8589934592L) != 0)) { @@ -2664,7 +2665,7 @@ protected override void AddValueFast(string key, StringValues value) return; } - if ("Proxy-Authorization".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Proxy-Authorization".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 34359738368L) != 0)) { @@ -2679,7 +2680,7 @@ protected override void AddValueFast(string key, StringValues value) case 2: { - if ("TE".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("TE".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 274877906944L) != 0)) { @@ -2694,7 +2695,7 @@ protected override void AddValueFast(string key, StringValues value) case 9: { - if ("Translate".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Translate".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 549755813888L) != 0)) { @@ -5006,7 +5007,7 @@ public bool MoveNext() } - public partial class FrameResponseHeaders + public partial class FrameResponseHeaders { private static byte[] _bytesCacheControl = Encoding.ASCII.GetBytes("Cache-Control: "); private static byte[] _bytesConnection = Encoding.ASCII.GetBytes("Connection: "); @@ -5038,7 +5039,6 @@ public partial class FrameResponseHeaders private static byte[] _bytesSetCookie = Encoding.ASCII.GetBytes("Set-Cookie: "); private static byte[] _bytesVary = Encoding.ASCII.GetBytes("Vary: "); private static byte[] _bytesWWWAuthenticate = Encoding.ASCII.GetBytes("WWW-Authenticate: "); - private long _bits = 0; private StringValues _CacheControl; @@ -5071,6 +5071,26 @@ public partial class FrameResponseHeaders private StringValues _SetCookie; private StringValues _Vary; private StringValues _WWWAuthenticate; + private bool _hasDefaultServer; + private bool _hasDefaultDate; + + public bool HasDefaultServer { + get { return _hasDefaultServer; } + set + { + _hasDefaultServer = value; + _bits &= ~67108864L; + } + } + + public bool HasDefaultDate { + get { return _hasDefaultDate; } + set + { + _hasDefaultDate = value; + _bits &= ~4L; + } + } public StringValues HeaderCacheControl { @@ -5084,7 +5104,7 @@ public StringValues HeaderCacheControl _CacheControl = value; } } - + public StringValues HeaderConnection { get @@ -5098,7 +5118,7 @@ public StringValues HeaderConnection _Connection = value; } } - + public StringValues HeaderDate { get @@ -5108,10 +5128,11 @@ public StringValues HeaderDate set { _bits |= 4L; + HasDefaultDate = false; _Date = value; } } - + public StringValues HeaderKeepAlive { get @@ -5124,7 +5145,7 @@ public StringValues HeaderKeepAlive _KeepAlive = value; } } - + public StringValues HeaderPragma { get @@ -5137,7 +5158,7 @@ public StringValues HeaderPragma _Pragma = value; } } - + public StringValues HeaderTrailer { get @@ -5150,7 +5171,7 @@ public StringValues HeaderTrailer _Trailer = value; } } - + public StringValues HeaderTransferEncoding { get @@ -5164,7 +5185,7 @@ public StringValues HeaderTransferEncoding _TransferEncoding = value; } } - + public StringValues HeaderUpgrade { get @@ -5177,7 +5198,7 @@ public StringValues HeaderUpgrade _Upgrade = value; } } - + public StringValues HeaderVia { get @@ -5190,7 +5211,7 @@ public StringValues HeaderVia _Via = value; } } - + public StringValues HeaderWarning { get @@ -5203,7 +5224,7 @@ public StringValues HeaderWarning _Warning = value; } } - + public StringValues HeaderAllow { get @@ -5216,7 +5237,7 @@ public StringValues HeaderAllow _Allow = value; } } - + public StringValues HeaderContentLength { get @@ -5230,7 +5251,7 @@ public StringValues HeaderContentLength _ContentLength = value; } } - + public StringValues HeaderContentType { get @@ -5243,7 +5264,7 @@ public StringValues HeaderContentType _ContentType = value; } } - + public StringValues HeaderContentEncoding { get @@ -5256,7 +5277,7 @@ public StringValues HeaderContentEncoding _ContentEncoding = value; } } - + public StringValues HeaderContentLanguage { get @@ -5269,7 +5290,7 @@ public StringValues HeaderContentLanguage _ContentLanguage = value; } } - + public StringValues HeaderContentLocation { get @@ -5282,7 +5303,7 @@ public StringValues HeaderContentLocation _ContentLocation = value; } } - + public StringValues HeaderContentMD5 { get @@ -5295,7 +5316,7 @@ public StringValues HeaderContentMD5 _ContentMD5 = value; } } - + public StringValues HeaderContentRange { get @@ -5308,7 +5329,7 @@ public StringValues HeaderContentRange _ContentRange = value; } } - + public StringValues HeaderExpires { get @@ -5321,7 +5342,7 @@ public StringValues HeaderExpires _Expires = value; } } - + public StringValues HeaderLastModified { get @@ -5334,7 +5355,7 @@ public StringValues HeaderLastModified _LastModified = value; } } - + public StringValues HeaderAcceptRanges { get @@ -5347,7 +5368,7 @@ public StringValues HeaderAcceptRanges _AcceptRanges = value; } } - + public StringValues HeaderAge { get @@ -5360,7 +5381,7 @@ public StringValues HeaderAge _Age = value; } } - + public StringValues HeaderETag { get @@ -5373,7 +5394,7 @@ public StringValues HeaderETag _ETag = value; } } - + public StringValues HeaderLocation { get @@ -5386,7 +5407,7 @@ public StringValues HeaderLocation _Location = value; } } - + public StringValues HeaderProxyAutheticate { get @@ -5399,7 +5420,7 @@ public StringValues HeaderProxyAutheticate _ProxyAutheticate = value; } } - + public StringValues HeaderRetryAfter { get @@ -5412,7 +5433,7 @@ public StringValues HeaderRetryAfter _RetryAfter = value; } } - + public StringValues HeaderServer { get @@ -5422,10 +5443,11 @@ public StringValues HeaderServer set { _bits |= 67108864L; + HasDefaultServer = false; _Server = value; } } - + public StringValues HeaderSetCookie { get @@ -5438,7 +5460,7 @@ public StringValues HeaderSetCookie _SetCookie = value; } } - + public StringValues HeaderVary { get @@ -5451,7 +5473,7 @@ public StringValues HeaderVary _Vary = value; } } - + public StringValues HeaderWWWAuthenticate { get @@ -5464,10 +5486,12 @@ public StringValues HeaderWWWAuthenticate _WWWAuthenticate = value; } } - + protected override int GetCountFast() { - return BitCount(_bits) + (MaybeUnknown?.Count ?? 0); + return BitCount(_bits) + (HasDefaultDate ? 1 : 0) + + (HasDefaultServer ? 1 : 0) + + (MaybeUnknown?.Count ?? 0); } protected override StringValues GetValueFast(string key) @@ -5476,7 +5500,7 @@ protected override StringValues GetValueFast(string key) { case 13: { - if ("Cache-Control".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Cache-Control".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 1L) != 0)) { @@ -5488,7 +5512,7 @@ protected override StringValues GetValueFast(string key) } } - if ("Content-Range".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Range".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 131072L) != 0)) { @@ -5500,7 +5524,7 @@ protected override StringValues GetValueFast(string key) } } - if ("Last-Modified".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Last-Modified".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 524288L) != 0)) { @@ -5512,7 +5536,7 @@ protected override StringValues GetValueFast(string key) } } - if ("Accept-Ranges".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Accept-Ranges".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 1048576L) != 0)) { @@ -5525,10 +5549,10 @@ protected override StringValues GetValueFast(string key) } } break; - + case 10: { - if ("Connection".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Connection".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 2L) != 0)) { @@ -5540,7 +5564,7 @@ protected override StringValues GetValueFast(string key) } } - if ("Keep-Alive".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Keep-Alive".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 8L) != 0)) { @@ -5552,7 +5576,7 @@ protected override StringValues GetValueFast(string key) } } - if ("Set-Cookie".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Set-Cookie".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 134217728L) != 0)) { @@ -5565,10 +5589,10 @@ protected override StringValues GetValueFast(string key) } } break; - + case 4: { - if ("Date".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Date".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 4L) != 0)) { @@ -5580,7 +5604,7 @@ protected override StringValues GetValueFast(string key) } } - if ("ETag".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("ETag".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 4194304L) != 0)) { @@ -5592,7 +5616,7 @@ protected override StringValues GetValueFast(string key) } } - if ("Vary".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Vary".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 268435456L) != 0)) { @@ -5605,10 +5629,10 @@ protected override StringValues GetValueFast(string key) } } break; - + case 6: { - if ("Pragma".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Pragma".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 16L) != 0)) { @@ -5620,7 +5644,7 @@ protected override StringValues GetValueFast(string key) } } - if ("Server".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Server".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 67108864L) != 0)) { @@ -5633,10 +5657,10 @@ protected override StringValues GetValueFast(string key) } } break; - + case 7: { - if ("Trailer".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Trailer".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 32L) != 0)) { @@ -5648,7 +5672,7 @@ protected override StringValues GetValueFast(string key) } } - if ("Upgrade".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Upgrade".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 128L) != 0)) { @@ -5660,7 +5684,7 @@ protected override StringValues GetValueFast(string key) } } - if ("Warning".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Warning".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 512L) != 0)) { @@ -5672,7 +5696,7 @@ protected override StringValues GetValueFast(string key) } } - if ("Expires".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Expires".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 262144L) != 0)) { @@ -5685,10 +5709,10 @@ protected override StringValues GetValueFast(string key) } } break; - + case 17: { - if ("Transfer-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Transfer-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 64L) != 0)) { @@ -5700,7 +5724,7 @@ protected override StringValues GetValueFast(string key) } } - if ("Proxy-Autheticate".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Proxy-Autheticate".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 16777216L) != 0)) { @@ -5713,10 +5737,10 @@ protected override StringValues GetValueFast(string key) } } break; - + case 3: { - if ("Via".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Via".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 256L) != 0)) { @@ -5728,7 +5752,7 @@ protected override StringValues GetValueFast(string key) } } - if ("Age".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Age".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 2097152L) != 0)) { @@ -5741,10 +5765,10 @@ protected override StringValues GetValueFast(string key) } } break; - + case 5: { - if ("Allow".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Allow".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 1024L) != 0)) { @@ -5757,10 +5781,10 @@ protected override StringValues GetValueFast(string key) } } break; - + case 14: { - if ("Content-Length".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Length".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 2048L) != 0)) { @@ -5773,10 +5797,10 @@ protected override StringValues GetValueFast(string key) } } break; - + case 12: { - if ("Content-Type".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Type".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 4096L) != 0)) { @@ -5789,10 +5813,10 @@ protected override StringValues GetValueFast(string key) } } break; - + case 16: { - if ("Content-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 8192L) != 0)) { @@ -5804,7 +5828,7 @@ protected override StringValues GetValueFast(string key) } } - if ("Content-Language".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Language".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 16384L) != 0)) { @@ -5816,7 +5840,7 @@ protected override StringValues GetValueFast(string key) } } - if ("Content-Location".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Location".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 32768L) != 0)) { @@ -5828,7 +5852,7 @@ protected override StringValues GetValueFast(string key) } } - if ("WWW-Authenticate".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("WWW-Authenticate".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 536870912L) != 0)) { @@ -5841,10 +5865,10 @@ protected override StringValues GetValueFast(string key) } } break; - + case 11: { - if ("Content-MD5".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-MD5".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 65536L) != 0)) { @@ -5856,7 +5880,7 @@ protected override StringValues GetValueFast(string key) } } - if ("Retry-After".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Retry-After".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 33554432L) != 0)) { @@ -5869,10 +5893,10 @@ protected override StringValues GetValueFast(string key) } } break; - + case 8: { - if ("Location".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Location".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 8388608L) != 0)) { @@ -5885,7 +5909,7 @@ protected override StringValues GetValueFast(string key) } } break; - } +} if (MaybeUnknown == null) { throw new System.Collections.Generic.KeyNotFoundException(); @@ -5956,7 +5980,7 @@ protected override bool TryGetValueFast(string key, out StringValues value) } } break; - + case 10: { if ("Connection".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -6002,11 +6026,16 @@ protected override bool TryGetValueFast(string key, out StringValues value) } } break; - + case 4: { if ("Date".Equals(key, StringComparison.OrdinalIgnoreCase)) { + if (HasDefaultDate) + { + value = DateTime.UtcNow.ToString(Constants.RFC1123DateFormat); + return true; + } if (((_bits & 4L) != 0)) { value = _Date; @@ -6048,7 +6077,7 @@ protected override bool TryGetValueFast(string key, out StringValues value) } } break; - + case 6: { if ("Pragma".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -6067,6 +6096,11 @@ protected override bool TryGetValueFast(string key, out StringValues value) if ("Server".Equals(key, StringComparison.OrdinalIgnoreCase)) { + if (HasDefaultServer) + { + value = "Kestrel"; + return true; + } if (((_bits & 67108864L) != 0)) { value = _Server; @@ -6080,7 +6114,7 @@ protected override bool TryGetValueFast(string key, out StringValues value) } } break; - + case 7: { if ("Trailer".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -6140,7 +6174,7 @@ protected override bool TryGetValueFast(string key, out StringValues value) } } break; - + case 17: { if ("Transfer-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -6172,7 +6206,7 @@ protected override bool TryGetValueFast(string key, out StringValues value) } } break; - + case 3: { if ("Via".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -6204,7 +6238,7 @@ protected override bool TryGetValueFast(string key, out StringValues value) } } break; - + case 5: { if ("Allow".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -6222,7 +6256,7 @@ protected override bool TryGetValueFast(string key, out StringValues value) } } break; - + case 14: { if ("Content-Length".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -6240,7 +6274,7 @@ protected override bool TryGetValueFast(string key, out StringValues value) } } break; - + case 12: { if ("Content-Type".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -6258,7 +6292,7 @@ protected override bool TryGetValueFast(string key, out StringValues value) } } break; - + case 16: { if ("Content-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -6318,7 +6352,7 @@ protected override bool TryGetValueFast(string key, out StringValues value) } } break; - + case 11: { if ("Content-MD5".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -6350,7 +6384,7 @@ protected override bool TryGetValueFast(string key, out StringValues value) } } break; - + case 8: { if ("Location".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -6368,7 +6402,7 @@ protected override bool TryGetValueFast(string key, out StringValues value) } } break; - } +} value = StringValues.Empty; return MaybeUnknown?.TryGetValue(key, out value) ?? false; } @@ -6408,7 +6442,7 @@ protected override void SetValueFast(string key, StringValues value) } } break; - + case 10: { if ("Connection".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -6434,13 +6468,14 @@ protected override void SetValueFast(string key, StringValues value) } } break; - + case 4: { if ("Date".Equals(key, StringComparison.OrdinalIgnoreCase)) { _bits |= 4L; _Date = value; + HasDefaultDate = false; return; } @@ -6459,7 +6494,7 @@ protected override void SetValueFast(string key, StringValues value) } } break; - + case 6: { if ("Pragma".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -6473,11 +6508,12 @@ protected override void SetValueFast(string key, StringValues value) { _bits |= 67108864L; _Server = value; + HasDefaultServer = false; return; } } break; - + case 7: { if ("Trailer".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -6509,7 +6545,7 @@ protected override void SetValueFast(string key, StringValues value) } } break; - + case 17: { if ("Transfer-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -6528,7 +6564,7 @@ protected override void SetValueFast(string key, StringValues value) } } break; - + case 3: { if ("Via".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -6546,7 +6582,7 @@ protected override void SetValueFast(string key, StringValues value) } } break; - + case 5: { if ("Allow".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -6557,7 +6593,7 @@ protected override void SetValueFast(string key, StringValues value) } } break; - + case 14: { if ("Content-Length".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -6569,7 +6605,7 @@ protected override void SetValueFast(string key, StringValues value) } } break; - + case 12: { if ("Content-Type".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -6580,7 +6616,7 @@ protected override void SetValueFast(string key, StringValues value) } } break; - + case 16: { if ("Content-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -6612,7 +6648,7 @@ protected override void SetValueFast(string key, StringValues value) } } break; - + case 11: { if ("Content-MD5".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -6630,7 +6666,7 @@ protected override void SetValueFast(string key, StringValues value) } } break; - + case 8: { if ("Location".Equals(key, StringComparison.OrdinalIgnoreCase)) @@ -6641,7 +6677,7 @@ protected override void SetValueFast(string key, StringValues value) } } break; - } +} Unknown[key] = value; } @@ -6651,7 +6687,7 @@ protected override void AddValueFast(string key, StringValues value) { case 13: { - if ("Cache-Control".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Cache-Control".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 1L) != 0)) { @@ -6662,7 +6698,7 @@ protected override void AddValueFast(string key, StringValues value) return; } - if ("Content-Range".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Range".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 131072L) != 0)) { @@ -6673,7 +6709,7 @@ protected override void AddValueFast(string key, StringValues value) return; } - if ("Last-Modified".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Last-Modified".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 524288L) != 0)) { @@ -6684,7 +6720,7 @@ protected override void AddValueFast(string key, StringValues value) return; } - if ("Accept-Ranges".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Accept-Ranges".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 1048576L) != 0)) { @@ -6699,7 +6735,7 @@ protected override void AddValueFast(string key, StringValues value) case 10: { - if ("Connection".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Connection".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 2L) != 0)) { @@ -6711,7 +6747,7 @@ protected override void AddValueFast(string key, StringValues value) return; } - if ("Keep-Alive".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Keep-Alive".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 8L) != 0)) { @@ -6722,7 +6758,7 @@ protected override void AddValueFast(string key, StringValues value) return; } - if ("Set-Cookie".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Set-Cookie".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 134217728L) != 0)) { @@ -6737,7 +6773,7 @@ protected override void AddValueFast(string key, StringValues value) case 4: { - if ("Date".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Date".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 4L) != 0)) { @@ -6745,10 +6781,11 @@ protected override void AddValueFast(string key, StringValues value) } _bits |= 4L; _Date = value; + HasDefaultDate = false; return; } - if ("ETag".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("ETag".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 4194304L) != 0)) { @@ -6759,7 +6796,7 @@ protected override void AddValueFast(string key, StringValues value) return; } - if ("Vary".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Vary".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 268435456L) != 0)) { @@ -6774,7 +6811,7 @@ protected override void AddValueFast(string key, StringValues value) case 6: { - if ("Pragma".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Pragma".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 16L) != 0)) { @@ -6785,7 +6822,7 @@ protected override void AddValueFast(string key, StringValues value) return; } - if ("Server".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Server".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 67108864L) != 0)) { @@ -6793,6 +6830,7 @@ protected override void AddValueFast(string key, StringValues value) } _bits |= 67108864L; _Server = value; + HasDefaultServer = false; return; } } @@ -6800,7 +6838,7 @@ protected override void AddValueFast(string key, StringValues value) case 7: { - if ("Trailer".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Trailer".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 32L) != 0)) { @@ -6811,7 +6849,7 @@ protected override void AddValueFast(string key, StringValues value) return; } - if ("Upgrade".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Upgrade".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 128L) != 0)) { @@ -6822,7 +6860,7 @@ protected override void AddValueFast(string key, StringValues value) return; } - if ("Warning".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Warning".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 512L) != 0)) { @@ -6833,7 +6871,7 @@ protected override void AddValueFast(string key, StringValues value) return; } - if ("Expires".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Expires".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 262144L) != 0)) { @@ -6848,7 +6886,7 @@ protected override void AddValueFast(string key, StringValues value) case 17: { - if ("Transfer-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Transfer-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 64L) != 0)) { @@ -6860,7 +6898,7 @@ protected override void AddValueFast(string key, StringValues value) return; } - if ("Proxy-Autheticate".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Proxy-Autheticate".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 16777216L) != 0)) { @@ -6875,7 +6913,7 @@ protected override void AddValueFast(string key, StringValues value) case 3: { - if ("Via".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Via".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 256L) != 0)) { @@ -6886,7 +6924,7 @@ protected override void AddValueFast(string key, StringValues value) return; } - if ("Age".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Age".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 2097152L) != 0)) { @@ -6901,7 +6939,7 @@ protected override void AddValueFast(string key, StringValues value) case 5: { - if ("Allow".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Allow".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 1024L) != 0)) { @@ -6916,7 +6954,7 @@ protected override void AddValueFast(string key, StringValues value) case 14: { - if ("Content-Length".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Length".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 2048L) != 0)) { @@ -6932,7 +6970,7 @@ protected override void AddValueFast(string key, StringValues value) case 12: { - if ("Content-Type".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Type".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 4096L) != 0)) { @@ -6947,7 +6985,7 @@ protected override void AddValueFast(string key, StringValues value) case 16: { - if ("Content-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Encoding".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 8192L) != 0)) { @@ -6958,7 +6996,7 @@ protected override void AddValueFast(string key, StringValues value) return; } - if ("Content-Language".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Language".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 16384L) != 0)) { @@ -6969,7 +7007,7 @@ protected override void AddValueFast(string key, StringValues value) return; } - if ("Content-Location".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-Location".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 32768L) != 0)) { @@ -6980,7 +7018,7 @@ protected override void AddValueFast(string key, StringValues value) return; } - if ("WWW-Authenticate".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("WWW-Authenticate".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 536870912L) != 0)) { @@ -6995,7 +7033,7 @@ protected override void AddValueFast(string key, StringValues value) case 11: { - if ("Content-MD5".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Content-MD5".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 65536L) != 0)) { @@ -7006,7 +7044,7 @@ protected override void AddValueFast(string key, StringValues value) return; } - if ("Retry-After".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Retry-After".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 33554432L) != 0)) { @@ -7021,7 +7059,7 @@ protected override void AddValueFast(string key, StringValues value) case 8: { - if ("Location".Equals(key, StringComparison.OrdinalIgnoreCase)) + if ("Location".Equals(key, StringComparison.OrdinalIgnoreCase)) { if (((_bits & 8388608L) != 0)) { @@ -7156,6 +7194,7 @@ protected override bool RemoveFast(string key) { _bits &= ~4L; _Date = StringValues.Empty; + HasDefaultDate = false; return true; } else @@ -7216,6 +7255,7 @@ protected override bool RemoveFast(string key) { _bits &= ~67108864L; _Server = StringValues.Empty; + HasDefaultServer = false; return true; } else @@ -7557,6 +7597,8 @@ protected override void ClearFast() HasConnection = false; HasTransferEncoding = false; HasContentLength = false; + HasDefaultServer = false; + HasDefaultDate = false; } protected override void CopyToFast(KeyValuePair[] array, int arrayIndex) @@ -8023,6 +8065,7 @@ public unsafe void Append(byte[] keyBytes, int keyOffset, int keyLength, string else { _bits |= 4L; + HasDefaultDate = false; _Date = new StringValues(value); } return; @@ -8083,6 +8126,7 @@ public unsafe void Append(byte[] keyBytes, int keyOffset, int keyLength, string else { _bits |= 67108864L; + HasDefaultServer = false; _Server = new StringValues(value); } return; @@ -8504,6 +8548,12 @@ public bool MoveNext() } state2: + if (_collection.HasDefaultDate) + { + _current = new KeyValuePair("Date", DateTime.UtcNow.ToString(Constants.RFC1123DateFormat)); + _state = 3; + return true; + } if (((_bits & 4L) != 0)) { _current = new KeyValuePair("Date", _collection._Date); @@ -8696,6 +8746,12 @@ public bool MoveNext() } state26: + if (_collection.HasDefaultServer) + { + _current = new KeyValuePair("Server", "Kestrel"); + _state = 27; + return true; + } if (((_bits & 67108864L) != 0)) { _current = new KeyValuePair("Server", _collection._Server); diff --git a/tools/Microsoft.AspNet.Server.Kestrel.GeneratedCode/KnownHeaders.cs b/tools/Microsoft.AspNet.Server.Kestrel.GeneratedCode/KnownHeaders.cs index b0ca2788c..f62bd1b38 100644 --- a/tools/Microsoft.AspNet.Server.Kestrel.GeneratedCode/KnownHeaders.cs +++ b/tools/Microsoft.AspNet.Server.Kestrel.GeneratedCode/KnownHeaders.cs @@ -168,20 +168,42 @@ public static string GeneratedFile() 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} - {{{(loop.ClassName == "FrameResponseHeaders" ? + public partial class {loop.ClassName} + {{{(loop.ClassName == "FrameResponseHeaders" ? $@"{Each(loop.Headers, header => @" private static byte[] _bytes" + header.Identifier + " = Encoding.ASCII.GetBytes(\"" + header.Name + ": \");")}" :"")} - private long _bits = 0; {Each(loop.Headers, header => @" - private StringValues _" + header.Identifier + ";")} + private StringValues _" + header.Identifier + ";")}{ +(loop.ClassName == "FrameResponseHeaders" ?$@" + private bool _hasDefaultServer; + private bool _hasDefaultDate; + + public bool HasDefaultServer {{ + get {{ return _hasDefaultServer; }} + set + {{ + _hasDefaultServer = value; + {responseHeaders.First((h) => h.Name == "Server").ClearBit()}; + }} + }} + + public bool HasDefaultDate {{ + get {{ return _hasDefaultDate; }} + set + {{ + _hasDefaultDate = value; + {responseHeaders.First((h) => h.Name == "Date").ClearBit()}; + }} + }}" +: "")} {Each(loop.Headers, header => $@" public StringValues Header{header.Identifier} {{ @@ -194,14 +216,21 @@ public StringValues Header{header.Identifier} {header.SetBit()};{(header.Name == "Connection" ? $@" HasConnection = true;" : "")}{(header.Name == "Transfer-Encoding" ? $@" HasTransferEncoding = true;" : "")}{(header.Name == "Content-Length" ? $@" - HasContentLength = true;" : "")} + HasContentLength = true;" : "")}{ + (loop.ClassName == "FrameResponseHeaders" && header.Name == "Date" ? $@" + HasDefaultDate = false;" : "")}{ + (loop.ClassName == "FrameResponseHeaders" && header.Name == "Server" ? $@" + HasDefaultServer = false;" : "")} _{header.Identifier} = value; }} }} - ")} +")} protected override int GetCountFast() {{ - return BitCount(_bits) + (MaybeUnknown?.Count ?? 0); + return BitCount(_bits) {(loop.ClassName == "FrameResponseHeaders" ? + $@"+ (HasDefaultDate ? 1 : 0) + + (HasDefaultServer ? 1 : 0)" :"")} + + (MaybeUnknown?.Count ?? 0); }} protected override StringValues GetValueFast(string key) @@ -210,7 +239,7 @@ protected override StringValues GetValueFast(string key) {{{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()}) {{ @@ -223,7 +252,7 @@ protected override StringValues GetValueFast(string key) }} ")}}} break; - ")}}} +")}}} if (MaybeUnknown == null) {{ throw new System.Collections.Generic.KeyNotFoundException(); @@ -238,7 +267,19 @@ protected override bool TryGetValueFast(string key, out StringValues value) case {byLength.Key}: {{{Each(byLength, header => $@" if (""{header.Name}"".Equals(key, StringComparison.OrdinalIgnoreCase)) - {{ + {{{ + (loop.ClassName == "FrameResponseHeaders" && header.Name == "Date" ? $@" + if (HasDefaultDate) + {{ + value = DateTime.UtcNow.ToString(Constants.RFC1123DateFormat); + return true; + }}" : "")}{ + (loop.ClassName == "FrameResponseHeaders" && header.Name == "Server" ? $@" + if (HasDefaultServer) + {{ + value = ""Kestrel""; + return true; + }}" : "")} if ({header.TestBit()}) {{ value = _{header.Identifier}; @@ -252,7 +293,7 @@ protected override bool TryGetValueFast(string key, out StringValues value) }} ")}}} break; - ")}}} +")}}} value = StringValues.Empty; return MaybeUnknown?.TryGetValue(key, out value) ?? false; }} @@ -269,12 +310,16 @@ protected override void SetValueFast(string key, StringValues value) _{header.Identifier} = value;{(header.Name == "Connection" ? $@" HasConnection = true;" : "")}{(header.Name == "Transfer-Encoding" ? $@" HasTransferEncoding = true;" : "")}{(header.Name == "Content-Length" ? $@" - HasContentLength = true;" : "")} + HasContentLength = true;" : "")}{ + (loop.ClassName == "FrameResponseHeaders" && header.Name == "Date" ? $@" + HasDefaultDate = false;" : "")}{ + (loop.ClassName == "FrameResponseHeaders" && header.Name == "Server" ? $@" + HasDefaultServer = false;" : "")} return; }} ")}}} break; - ")}}} +")}}} Unknown[key] = value; }} @@ -284,7 +329,7 @@ protected override void AddValueFast(string key, StringValues value) {{{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()}) {{ @@ -294,7 +339,11 @@ protected override void AddValueFast(string key, StringValues value) _{header.Identifier} = value;{(header.Name == "Connection" ? $@" HasConnection = true;" : "")}{(header.Name == "Transfer-Encoding" ? $@" HasTransferEncoding = true;" : "")}{(header.Name == "Content-Length" ? $@" - HasContentLength = true;" : "")} + HasContentLength = true;" : "")}{ + (loop.ClassName == "FrameResponseHeaders" && header.Name == "Date" ? $@" + HasDefaultDate = false;" : "")}{ + (loop.ClassName == "FrameResponseHeaders" && header.Name == "Server" ? $@" + HasDefaultServer = false;" : "")} return; }} ")}}} @@ -317,7 +366,11 @@ protected override bool RemoveFast(string key) _{header.Identifier} = StringValues.Empty;{(header.Name == "Connection" ? $@" HasConnection = false;" : "")}{(header.Name == "Transfer-Encoding" ? $@" HasTransferEncoding = false;" : "")}{(header.Name == "Content-Length" ? $@" - HasContentLength = false;" : "")} + HasContentLength = false;" : "")}{ + (loop.ClassName == "FrameResponseHeaders" && header.Name == "Date" ? $@" + HasDefaultDate = false;" : "")}{ + (loop.ClassName == "FrameResponseHeaders" && header.Name == "Server" ? $@" + HasDefaultServer = false;" : "")} return true; }} else @@ -339,7 +392,10 @@ protected override void ClearFast() MaybeUnknown?.Clear(); HasConnection = false; HasTransferEncoding = false; - HasContentLength = false; + HasContentLength = false;{(loop.ClassName == "FrameResponseHeaders" ? @" + HasDefaultServer = false; + HasDefaultDate = false;": + "")} }} protected override void CopyToFast(KeyValuePair[] array, int arrayIndex) @@ -382,7 +438,11 @@ public unsafe void Append(byte[] keyBytes, int keyOffset, int keyLength, string {header.SetBit()};{(header.Name == "Connection" ? $@" HasConnection = true;" : "")}{(header.Name == "Transfer-Encoding" ? $@" HasTransferEncoding = true;" : "")}{(header.Name == "Content-Length" ? $@" - HasContentLength = true;" : "")} + HasContentLength = true;" : "")}{ + (loop.ClassName == "FrameResponseHeaders" && header.Name == "Date" ? $@" + HasDefaultDate = false;" : "")}{ + (loop.ClassName == "FrameResponseHeaders" && header.Name == "Server" ? $@" + HasDefaultServer = false;" : "")} _{header.Identifier} = new StringValues(value); }} return; @@ -410,7 +470,21 @@ public bool MoveNext() goto state_default; }} {Each(loop.Headers, header => $@" - state{header.Index}: + state{header.Index}:{ + (loop.ClassName == "FrameResponseHeaders" && header.Name == "Date" ? $@" + if (_collection.HasDefaultDate) + {{ + _current = new KeyValuePair(""{header.Name}"", DateTime.UtcNow.ToString(Constants.RFC1123DateFormat)); + _state = {header.Index + 1}; + return true; + }}" : "")}{ + (loop.ClassName == "FrameResponseHeaders" && header.Name == "Server" ? $@" + if (_collection.HasDefaultServer) + {{ + _current = new KeyValuePair(""{header.Name}"", ""Kestrel""); + _state = {header.Index + 1}; + return true; + }}" : "")} if ({header.TestBit()}) {{ _current = new KeyValuePair(""{header.Name}"", _collection._{header.Identifier}); @@ -472,4 +546,4 @@ public virtual void AfterCompile(AfterCompileContext context) { } } -} +} \ No newline at end of file From 319616586600f41bda686030f6a0461c40413758 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Thu, 12 Nov 2015 11:07:28 +0000 Subject: [PATCH 06/24] use local vars --- .../Http/Frame.cs | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs index 84aa103ed..9b67982f9 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs @@ -561,44 +561,53 @@ private async Task ProduceEnd() private static void OutputAsciiBlock(string data, MemoryPoolBlock2 memoryBlock, ISocketOutput output) { - var end = memoryBlock.Start + memoryBlock.Data.Count; + var start = memoryBlock.Start; + var end = start + memoryBlock.Data.Count; + var array = memoryBlock.Array; + var current = memoryBlock.End; foreach (var chr in data) { - memoryBlock.Array[memoryBlock.End] = (byte)chr; + array[current] = (byte)chr; - memoryBlock.End++; + current++; - if (memoryBlock.End == end) + if (current == end) { output.Write(memoryBlock.Data, immediate: false); - memoryBlock.End = memoryBlock.Start; + current = start; } } + memoryBlock.End = current; } private static void OutputAsciiBlock(byte[] data, MemoryPoolBlock2 memoryBlock, ISocketOutput output) { var offset = 0; var remaining = data.Length; - var end = memoryBlock.Start + memoryBlock.Data.Count; + + var start = memoryBlock.Start; + var end = start + memoryBlock.Data.Count; + var array = memoryBlock.Array; + var current = memoryBlock.End; while (remaining > 0) { - var blockRemaining = end - memoryBlock.End; + var blockRemaining = end - current; var copyAmount = blockRemaining >= remaining ? remaining : blockRemaining; - Buffer.BlockCopy(data, offset, memoryBlock.Array, memoryBlock.End, copyAmount); + Buffer.BlockCopy(data, offset, array, current, copyAmount); - memoryBlock.End += copyAmount; + current += copyAmount; remaining -= copyAmount; offset += copyAmount; - if (memoryBlock.End == end) + if (current == end) { output.Write(memoryBlock.Data, immediate: false); - memoryBlock.End = memoryBlock.Start; + current = start; } } + memoryBlock.End = current; } private Task CreateResponseHeader( From 03243fddd3c9c219e8b05ecfcc4437542bf021f1 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Fri, 13 Nov 2015 06:28:12 +0000 Subject: [PATCH 07/24] Use bool rather than bit twiddel --- .../Http/DateHeaderValueManager.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/DateHeaderValueManager.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/DateHeaderValueManager.cs index e9695ce26..410c7a790 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/DateHeaderValueManager.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/DateHeaderValueManager.cs @@ -18,9 +18,9 @@ public class DateHeaderValueManager : IDisposable private readonly TimeSpan _timerInterval; private volatile string _dateValue; - private volatile int _activeDateBytes; - private byte[] _dateBytes0 = Encoding.ASCII.GetBytes("DDD, dd mmm yyyy hh:mm:ss GMT"); - private byte[] _dateBytes1 = Encoding.ASCII.GetBytes("DDD, dd mmm yyyy hh:mm:ss GMT"); + private volatile bool _activeDateBytes; + private readonly byte[] _dateBytes0 = Encoding.ASCII.GetBytes("DDD, dd mmm yyyy hh:mm:ss GMT"); + private readonly byte[] _dateBytes1 = Encoding.ASCII.GetBytes("DDD, dd mmm yyyy hh:mm:ss GMT"); private object _timerLocker = new object(); private bool _isDisposed = false; private bool _hadRequestsSinceLastTimerTick = false; @@ -69,7 +69,7 @@ public virtual string GetDateHeaderValue() public byte[] GetDateHeaderValueBytes() { PumpTimer(); - return _activeDateBytes == 0 ? _dateBytes0 : _dateBytes1; + return _activeDateBytes ? _dateBytes0 : _dateBytes1; } /// @@ -102,8 +102,8 @@ private void PumpTimer() // 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 ^ 1) == 0 ? _dateBytes0 : _dateBytes1, 0); - _activeDateBytes ^= 1; + Encoding.ASCII.GetBytes(_dateValue, 0, _dateValue.Length, !_activeDateBytes ? _dateBytes0 : _dateBytes1, 0); + _activeDateBytes = !_activeDateBytes; _dateValueTimer = new Timer(UpdateDateValue, state: null, dueTime: _timerInterval, period: _timerInterval); } } @@ -117,8 +117,8 @@ private void UpdateDateValue(object state) // 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 ^ 1) == 0 ? _dateBytes0 : _dateBytes1, 0); - _activeDateBytes ^= 1; + Encoding.ASCII.GetBytes(_dateValue, 0, _dateValue.Length, !_activeDateBytes ? _dateBytes0 : _dateBytes1, 0); + _activeDateBytes = !_activeDateBytes; if (_hadRequestsSinceLastTimerTick) { From 846e59731fb8ba147807c35c9273f10b73733180 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Fri, 13 Nov 2015 08:03:48 +0000 Subject: [PATCH 08/24] Used descriptive const default value --- .../Infrastructure/MemoryPool2.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPool2.cs b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPool2.cs index 5dea314cd..abc84901d 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPool2.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPool2.cs @@ -30,6 +30,12 @@ public class MemoryPool2 : IDisposable /// 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 @@ public class MemoryPool2 : IDisposable /// 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) { From 4698797ff3ba9418c75219fe0b75bb7dd15e35c2 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 11 Nov 2015 12:28:29 +0000 Subject: [PATCH 09/24] Initialize the work queue sizes Rather than resizing the circular buffers as work is added --- .../Infrastructure/KestrelThread.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/KestrelThread.cs b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/KestrelThread.cs index a8608de70..cbb35ae18 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/KestrelThread.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/KestrelThread.cs @@ -24,10 +24,10 @@ public class KestrelThread private Thread _thread; private UvLoopHandle _loop; private UvAsyncHandle _post; - private Queue _workAdding = new Queue(); - private Queue _workRunning = new Queue(); - private Queue _closeHandleAdding = new Queue(); - private Queue _closeHandleRunning = new Queue(); + private Queue _workAdding = new Queue(1024); + private Queue _workRunning = new Queue(1024); + private Queue _closeHandleAdding = new Queue(256); + private Queue _closeHandleRunning = new Queue(256); private object _workSync = new Object(); private bool _stopImmediate = false; private bool _initCompleted = false; From cda22c3b683586ddc5c65dcab032688e54487535 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Fri, 13 Nov 2015 12:49:30 +0000 Subject: [PATCH 10/24] Initalize SocketOutput Queues, Reuse WriteContexts --- .../Http/SocketOutput.cs | 49 +++++++++++++++---- 1 file changed, 40 insertions(+), 9 deletions(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs index d2534206e..9f75719a8 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs @@ -15,6 +15,8 @@ public class SocketOutput : ISocketOutput { private const int _maxPendingWrites = 3; private const int _maxBytesPreCompleted = 65536; + private const int _maxPooledWriteContexts = 16; + private const int _maxPooledBufferQueues = 16; private readonly KestrelThread _thread; private readonly UvStreamHandle _socket; @@ -32,6 +34,7 @@ public class SocketOutput : ISocketOutput private Exception _lastWriteError; private WriteContext _nextWriteContext; private readonly Queue> _tasksPending; + private readonly Queue _writeContexts; public SocketOutput(KestrelThread thread, UvStreamHandle socket, long connectionId, IKestrelTrace log) { @@ -39,7 +42,8 @@ public SocketOutput(KestrelThread thread, UvStreamHandle socket, long connection _socket = socket; _connectionId = connectionId; _log = log; - _tasksPending = new Queue>(); + _tasksPending = new Queue>(16); + _writeContexts = new Queue(_maxPooledWriteContexts); } public Task WriteAsync( @@ -63,7 +67,14 @@ public Task WriteAsync( { if (_nextWriteContext == null) { - _nextWriteContext = new WriteContext(this); + if (_writeContexts.Count > 0) + { + _nextWriteContext = _writeContexts.Dequeue(); + } + else + { + _nextWriteContext = new WriteContext(this); + } } if (buffer.Array != null) @@ -172,13 +183,13 @@ private void WriteAllPending() } // This is called on the libuv event loop - private void OnWriteCompleted(Queue> writtenBuffers, int status, Exception error) + private void OnWriteCompleted(WriteContext write) { - _log.ConnectionWriteCallback(_connectionId, status); + var status = write.WriteStatus; lock (_lockObj) { - _lastWriteError = error; + _lastWriteError = write.WriteError; if (_nextWriteContext != null) { @@ -189,7 +200,7 @@ private void OnWriteCompleted(Queue> writtenBuffers, int stat _writesPending--; } - foreach (var writeBuffer in writtenBuffers) + foreach (var writeBuffer in write.Buffers) { // _numBytesPreCompleted can temporarily go negative in the event there are // completed writes that we haven't triggered callbacks for yet. @@ -208,7 +219,7 @@ private void OnWriteCompleted(Queue> writtenBuffers, int stat _numBytesPreCompleted += bytesToWrite; bytesLeftToBuffer -= bytesToWrite; - if (error == null) + if (write.WriteError == null) { ThreadPool.QueueUserWorkItem( (o) => ((TaskCompletionSource)o).SetResult(null), @@ -216,6 +227,7 @@ private void OnWriteCompleted(Queue> writtenBuffers, int stat } else { + var error = write.WriteError; // error is closure captured ThreadPool.QueueUserWorkItem( (o) => ((TaskCompletionSource)o).SetException(error), @@ -223,9 +235,18 @@ private void OnWriteCompleted(Queue> writtenBuffers, int stat } } + if (_writeContexts.Count < _maxPooledWriteContexts + && write.Buffers.Count <= _maxPooledBufferQueues) + { + write.Reset(); + _writeContexts.Enqueue(write); + } + // Now that the while loop has completed the following invariants should hold true: Debug.Assert(_numBytesPreCompleted >= 0); } + + _log.ConnectionWriteCallback(_connectionId, status); } void ISocketOutput.Write(ArraySegment buffer, bool immediate) @@ -263,7 +284,7 @@ private class WriteContext public WriteContext(SocketOutput self) { Self = self; - Buffers = new Queue>(); + Buffers = new Queue>(_maxPooledBufferQueues); } /// @@ -340,7 +361,17 @@ public void DoDisconnectIfNeeded() public void Complete() { - Self.OnWriteCompleted(Buffers, WriteStatus, WriteError); + Self.OnWriteCompleted(this); + } + + public void Reset() + { + Buffers.Clear(); + SocketDisconnect = false; + SocketShutdownSend = false; + WriteStatus = 0; + WriteError = null; + ShutdownSendStatus = 0; } } } From 14cb795f8c847a5921e544fe06165419c2f96a64 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Fri, 6 Nov 2015 01:46:21 +0000 Subject: [PATCH 11/24] Use pooled array Resolves #334 --- src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs | 14 +++++++++++--- .../Infrastructure/MemoryPool2.cs | 6 ++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs index c08d0eb68..1a41ede48 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs @@ -31,7 +31,6 @@ public partial class Frame : FrameContext, IFrameControl private readonly object _onStartingSync = new Object(); private readonly object _onCompletedSync = new Object(); private readonly FrameRequestHeaders _requestHeaders = new FrameRequestHeaders(); - private readonly byte[] _nullBuffer = new byte[4096]; private readonly FrameResponseHeaders _responseHeaders = new FrameResponseHeaders(); private List, object>> _onStarting; @@ -232,9 +231,18 @@ public async Task RequestProcessingAsync() await ProduceEnd(); - while (await RequestBody.ReadAsync(_nullBuffer, 0, _nullBuffer.Length) != 0) + var block = Memory2.Lease(MemoryPool2.DefaultBlockLength); + try { - // Finish reading the request body in case the app did not. + var segment = block.Data; + while (await RequestBody.ReadAsync(segment.Array, segment.Offset, segment.Count) != 0) + { + // Finish reading the request body in case the app did not. + } + } + finally + { + Memory2.Return(block); } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPool2.cs b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPool2.cs index 86561516c..c06012d5d 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPool2.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPool2.cs @@ -31,6 +31,12 @@ public class MemoryPool2 : IDisposable /// private const int _blockLength = _blockStride - _blockUnused; + /// + /// Default allocation block size for pooled blocks. + /// + public const int DefaultBlockLength = _blockLength; + + /// /// 4096 * 32 gives you a slabLength of 128k contiguous bytes allocated per slab /// From 9f82d216b13c5f8b69aec782ec5ff1f64ec32d6e Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 11 Nov 2015 00:58:18 +0000 Subject: [PATCH 12/24] Use pooled array for header names --- .../Http/Frame.cs | 34 +++++++++++-------- .../Infrastructure/MemoryPoolIterator2.cs | 17 +++++++--- .../FrameTests.cs | 10 +++--- 3 files changed, 38 insertions(+), 23 deletions(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs index 1a41ede48..378794f5f 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs @@ -188,7 +188,7 @@ public async Task RequestProcessingAsync() } } - while (!terminated && !_requestProcessingStopping && !TakeMessageHeaders(SocketInput, _requestHeaders)) + while (!terminated && !_requestProcessingStopping && !TakeMessageHeaders(SocketInput, _requestHeaders, Memory2)) { terminated = SocketInput.RemoteIntakeFin; if (!terminated) @@ -697,12 +697,7 @@ private bool TakeStartLine(SocketInput input) } } - static string GetString(ArraySegment range, int startIndex, int endIndex) - { - return Encoding.UTF8.GetString(range.Array, range.Offset + startIndex, endIndex - startIndex); - } - - public static bool TakeMessageHeaders(SocketInput input, FrameRequestHeaders requestHeaders) + public static bool TakeMessageHeaders(SocketInput input, FrameRequestHeaders requestHeaders, MemoryPool2 memorypool) { var scan = input.ConsumingStart(); var consumed = scan; @@ -762,6 +757,7 @@ public static bool TakeMessageHeaders(SocketInput input, FrameRequestHeaders req scan = beginValue; var wrapping = false; + while (!scan.IsEnd) { if (scan.Seek('\r') == -1) @@ -792,16 +788,24 @@ public static bool TakeMessageHeaders(SocketInput input, FrameRequestHeaders req continue; } - var name = beginName.GetArraySegment(endName); - var value = beginValue.GetString(endValue); - if (wrapping) + var block = memorypool.Lease(MemoryPool2.DefaultBlockLength); + try { - value = value.Replace("\r\n", " "); - } + var name = beginName.GetArraySegment(endName, block.Data); + var value = beginValue.GetString(endValue); + if (wrapping) + { + value = value.Replace("\r\n", " "); + } - consumed = scan; - requestHeaders.Append(name.Array, name.Offset, name.Count, value); - break; + consumed = scan; + requestHeaders.Append(name.Array, name.Offset, name.Count, value); + break; + } + finally + { + memorypool.Return(block); + } } } return false; diff --git a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolIterator2.cs b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolIterator2.cs index 7de5c7fb1..0ae3f7951 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolIterator2.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolIterator2.cs @@ -566,7 +566,7 @@ public string GetString(MemoryPoolIterator2 end) } } - public ArraySegment GetArraySegment(MemoryPoolIterator2 end) + public ArraySegment GetArraySegment(MemoryPoolIterator2 end, ArraySegment scratchBuffer) { if (IsDefault || end.IsDefault) { @@ -578,9 +578,18 @@ public ArraySegment GetArraySegment(MemoryPoolIterator2 end) } var length = GetLength(end); - var array = new byte[length]; - CopyTo(array, 0, length, out length); - return new ArraySegment(array, 0, length); + + if (length < scratchBuffer.Count) + { + CopyTo(scratchBuffer.Array, scratchBuffer.Offset, length, out length); + return new ArraySegment(scratchBuffer.Array, scratchBuffer.Offset, length); + } + else + { + var array = new byte[length]; + CopyTo(array, 0, length, out length); + return new ArraySegment(array, 0, length); + } } public MemoryPoolIterator2 CopyTo(byte[] array, int offset, int count, out int actual) diff --git a/test/Microsoft.AspNet.Server.KestrelTests/FrameTests.cs b/test/Microsoft.AspNet.Server.KestrelTests/FrameTests.cs index 8266a1c2e..422d352ee 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/FrameTests.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/FrameTests.cs @@ -56,11 +56,13 @@ public void EmptyHeaderValuesCanBeParsed(string rawHeaders, int numHeaders) Buffer.BlockCopy(headerArray, 0, inputBuffer.Data.Array, inputBuffer.Data.Offset, headerArray.Length); socketInput.IncomingComplete(headerArray.Length, null); - var success = Frame.TakeMessageHeaders(socketInput, headerCollection); - - Assert.True(success); - Assert.Equal(numHeaders, headerCollection.Count()); + using (var memorypool = new MemoryPool2()) + { + var success = Frame.TakeMessageHeaders(socketInput, headerCollection, memorypool); + Assert.True(success); + Assert.Equal(numHeaders, headerCollection.Count()); + } // Assert TakeMessageHeaders consumed all the input var scan = socketInput.ConsumingStart(); Assert.True(scan.IsEnd); From 79c4a4221d368f5e3884aaea69b244b5643a8db7 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Fri, 13 Nov 2015 09:08:51 +0000 Subject: [PATCH 13/24] Use default value in constructor --- src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs | 4 ++-- .../Infrastructure/MemoryPool2.cs | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs index 378794f5f..98e5bc56e 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs @@ -231,7 +231,7 @@ public async Task RequestProcessingAsync() await ProduceEnd(); - var block = Memory2.Lease(MemoryPool2.DefaultBlockLength); + var block = Memory2.Lease(); try { var segment = block.Data; @@ -788,7 +788,7 @@ public static bool TakeMessageHeaders(SocketInput input, FrameRequestHeaders req continue; } - var block = memorypool.Lease(MemoryPool2.DefaultBlockLength); + var block = memorypool.Lease(); try { var name = beginName.GetArraySegment(endName, block.Data); diff --git a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPool2.cs b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPool2.cs index c06012d5d..faa00e642 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPool2.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPool2.cs @@ -32,11 +32,11 @@ public class MemoryPool2 : IDisposable private const int _blockLength = _blockStride - _blockUnused; /// - /// Default allocation block size for pooled blocks. + /// 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 DefaultBlockLength = _blockLength; - - + public const int MaxPooledBlockLength = _blockLength; + /// /// 4096 * 32 gives you a slabLength of 128k contiguous bytes allocated per slab /// @@ -65,7 +65,7 @@ public class MemoryPool2 : IDisposable /// 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 = MaxPooledBlockLength) { if (minimumSize > _blockLength) { From 7a6dd041b9dee6c0a9cfcf2addb3d090be460444 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sun, 8 Nov 2015 21:53:01 +0000 Subject: [PATCH 14/24] Dispose of MemoryPool2; suppress its finalizers --- .../Http/ListenerContext.cs | 5 +-- .../Infrastructure/KestrelThread.cs | 7 +++ .../Infrastructure/MemoryPool2.cs | 45 ++++++++----------- .../Infrastructure/MemoryPoolBlock2.cs | 5 +++ .../FrameTests.cs | 27 ++++++----- .../TestInput.cs | 13 ++++-- 6 files changed, 56 insertions(+), 46 deletions(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerContext.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerContext.cs index 581720041..0ae440415 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerContext.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerContext.cs @@ -10,13 +10,11 @@ public class ListenerContext : ServiceContext { public ListenerContext() { - Memory2 = new MemoryPool2(); } public ListenerContext(ServiceContext serviceContext) : base(serviceContext) { - Memory2 = new MemoryPool2(); } public ListenerContext(ListenerContext listenerContext) @@ -25,7 +23,6 @@ public ListenerContext(ListenerContext listenerContext) ServerAddress = listenerContext.ServerAddress; Thread = listenerContext.Thread; Application = listenerContext.Application; - Memory2 = listenerContext.Memory2; Log = listenerContext.Log; } @@ -35,6 +32,6 @@ public ListenerContext(ListenerContext listenerContext) public RequestDelegate Application { get; set; } - public MemoryPool2 Memory2 { get; set; } + public MemoryPool2 Memory2 => Thread.Memory2; } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/KestrelThread.cs b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/KestrelThread.cs index a8608de70..50b404420 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/KestrelThread.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/KestrelThread.cs @@ -37,6 +37,7 @@ public class KestrelThread public KestrelThread(KestrelEngine engine) { _engine = engine; + Memory2 = new MemoryPool2(); _appLifetime = engine.AppLifetime; _log = engine.Log; _loop = new UvLoopHandle(_log); @@ -46,6 +47,9 @@ public KestrelThread(KestrelEngine engine) } public UvLoopHandle Loop { get { return _loop; } } + + public MemoryPool2 Memory2 { get; } + public ExceptionDispatchInfo FatalError { get { return _closeError; } } public Action, IntPtr> QueueCloseHandle { get; internal set; } @@ -61,6 +65,7 @@ public void Stop(TimeSpan timeout) { if (!_initCompleted) { + Memory2.Dispose(); return; } @@ -94,6 +99,8 @@ public void Stop(TimeSpan timeout) } } + Memory2.Dispose(); + if (_closeError != null) { _closeError.Throw(); diff --git a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPool2.cs b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPool2.cs index 86561516c..60d39727b 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPool2.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPool2.cs @@ -48,11 +48,6 @@ public class MemoryPool2 : IDisposable /// private readonly ConcurrentStack _slabs = new ConcurrentStack(); - /// - /// This is part of implementing the IDisposable pattern. - /// - private bool _disposedValue = false; // To detect redundant calls - /// /// Called to take a block from the pool. /// @@ -137,39 +132,35 @@ public void Return(MemoryPoolBlock2 block) protected virtual void Dispose(bool disposing) { - if (!_disposedValue) + MemoryPoolSlab2 slab; + while (_slabs.TryPop(out slab)) { - if (disposing) - { - MemoryPoolSlab2 slab; - while (_slabs.TryPop(out slab)) - { - // dispose managed state (managed objects). - slab.Dispose(); - } - } - - // N/A: free unmanaged resources (unmanaged objects) and override a finalizer below. - - // N/A: set large fields to null. + // Free pinned objects + slab.Dispose(); + } - _disposedValue = true; + MemoryPoolBlock2 block; + while (_blocks.TryPop(out block)) + { + // Deactivate finalizers + block.Dispose(); } } - // N/A: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources. - // ~MemoryPool2() { - // // Do not change this code. Put cleanup code in Dispose(bool disposing) above. - // Dispose(false); - // } + // Disposing slabs unpin memory so finalizer is needed. + ~MemoryPool2() + { + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + Dispose(false); + } // This code added to correctly implement the disposable pattern. public void Dispose() { // Do not change this code. Put cleanup code in Dispose(bool disposing) above. Dispose(true); - // N/A: uncomment the following line if the finalizer is overridden above. - // GC.SuppressFinalize(this); + + GC.SuppressFinalize(this); } } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolBlock2.cs b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolBlock2.cs index a93e03186..5ae74457a 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolBlock2.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolBlock2.cs @@ -173,5 +173,10 @@ public MemoryPoolIterator2 GetIterator() { return new MemoryPoolIterator2(this); } + + internal void Dispose() + { + GC.SuppressFinalize(this); + } } } diff --git a/test/Microsoft.AspNet.Server.KestrelTests/FrameTests.cs b/test/Microsoft.AspNet.Server.KestrelTests/FrameTests.cs index 8266a1c2e..1d3889001 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/FrameTests.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/FrameTests.cs @@ -48,22 +48,25 @@ public void ChunkedPrefixMustBeHexCrLfWithoutLeadingZeros(int dataCount, string [InlineData("Connection:\r\n \r\nCookie \r\n", 1)] public void EmptyHeaderValuesCanBeParsed(string rawHeaders, int numHeaders) { - var socketInput = new SocketInput(new MemoryPool2()); - var headerCollection = new FrameRequestHeaders(); + using (var memory = new MemoryPool2()) + { + var socketInput = new SocketInput(memory); + var headerCollection = new FrameRequestHeaders(); - var headerArray = Encoding.ASCII.GetBytes(rawHeaders); - var inputBuffer = socketInput.IncomingStart(headerArray.Length); - Buffer.BlockCopy(headerArray, 0, inputBuffer.Data.Array, inputBuffer.Data.Offset, headerArray.Length); - socketInput.IncomingComplete(headerArray.Length, null); + var headerArray = Encoding.ASCII.GetBytes(rawHeaders); + var inputBuffer = socketInput.IncomingStart(headerArray.Length); + Buffer.BlockCopy(headerArray, 0, inputBuffer.Data.Array, inputBuffer.Data.Offset, headerArray.Length); + socketInput.IncomingComplete(headerArray.Length, null); - var success = Frame.TakeMessageHeaders(socketInput, headerCollection); + var success = Frame.TakeMessageHeaders(socketInput, headerCollection); - Assert.True(success); - Assert.Equal(numHeaders, headerCollection.Count()); + Assert.True(success); + Assert.Equal(numHeaders, headerCollection.Count()); - // Assert TakeMessageHeaders consumed all the input - var scan = socketInput.ConsumingStart(); - Assert.True(scan.IsEnd); + // Assert TakeMessageHeaders consumed all the input + var scan = socketInput.ConsumingStart(); + Assert.True(scan.IsEnd); + } } } } diff --git a/test/Microsoft.AspNet.Server.KestrelTests/TestInput.cs b/test/Microsoft.AspNet.Server.KestrelTests/TestInput.cs index 3db112560..390d09f51 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/TestInput.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/TestInput.cs @@ -9,21 +9,28 @@ namespace Microsoft.AspNet.Server.KestrelTests { - class TestInput : IConnectionControl, IFrameControl + class TestInput : IConnectionControl, IFrameControl, IDisposable { + private readonly MemoryPool2 _memory2; + public TestInput() { var memory = new MemoryPool(); - var memory2 = new MemoryPool2(); + _memory2 = new MemoryPool2(); FrameContext = new FrameContext { - SocketInput = new SocketInput(memory2), + SocketInput = new SocketInput(_memory2), Memory = memory, ConnectionControl = this, FrameControl = this }; } + public void Dispose() + { + _memory2.Dispose(); + } + public FrameContext FrameContext { get; set; } public void Add(string text, bool fin = false) From ee4d9c35fb96c98c51d7d1256ef52f016753d2db Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Fri, 13 Nov 2015 09:21:57 +0000 Subject: [PATCH 15/24] Dispose in tests --- .../MessageBodyTests.cs | 82 ++++++++++--------- 1 file changed, 44 insertions(+), 38 deletions(-) diff --git a/test/Microsoft.AspNet.Server.KestrelTests/MessageBodyTests.cs b/test/Microsoft.AspNet.Server.KestrelTests/MessageBodyTests.cs index b5dbfc021..377fe7486 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/MessageBodyTests.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/MessageBodyTests.cs @@ -19,66 +19,72 @@ public class MessageBodyTests [Fact] public void Http10ConnectionClose() { - var input = new TestInput(); - var body = MessageBody.For("HTTP/1.0", new Dictionary(), input.FrameContext); - var stream = new FrameRequestStream(body); + using (var input = new TestInput()) + { + var body = MessageBody.For("HTTP/1.0", new Dictionary(), input.FrameContext); + var stream = new FrameRequestStream(body); - input.Add("Hello", true); + input.Add("Hello", true); - var buffer1 = new byte[1024]; - var count1 = stream.Read(buffer1, 0, 1024); - AssertASCII("Hello", new ArraySegment(buffer1, 0, 5)); + var buffer1 = new byte[1024]; + var count1 = stream.Read(buffer1, 0, 1024); + AssertASCII("Hello", new ArraySegment(buffer1, 0, 5)); - var buffer2 = new byte[1024]; - var count2 = stream.Read(buffer2, 0, 1024); - Assert.Equal(0, count2); + var buffer2 = new byte[1024]; + var count2 = stream.Read(buffer2, 0, 1024); + Assert.Equal(0, count2); + } } [Fact] public async Task Http10ConnectionCloseAsync() { - var input = new TestInput(); - var body = MessageBody.For("HTTP/1.0", new Dictionary(), input.FrameContext); - var stream = new FrameRequestStream(body); + using (var input = new TestInput()) + { + var body = MessageBody.For("HTTP/1.0", new Dictionary(), input.FrameContext); + var stream = new FrameRequestStream(body); - input.Add("Hello", true); + input.Add("Hello", true); - var buffer1 = new byte[1024]; - var count1 = await stream.ReadAsync(buffer1, 0, 1024); - AssertASCII("Hello", new ArraySegment(buffer1, 0, 5)); + var buffer1 = new byte[1024]; + var count1 = await stream.ReadAsync(buffer1, 0, 1024); + AssertASCII("Hello", new ArraySegment(buffer1, 0, 5)); - var buffer2 = new byte[1024]; - var count2 = await stream.ReadAsync(buffer2, 0, 1024); - Assert.Equal(0, count2); + var buffer2 = new byte[1024]; + var count2 = await stream.ReadAsync(buffer2, 0, 1024); + Assert.Equal(0, count2); + } } [Fact] public async Task CanHandleLargeBlocks() { - var input = new TestInput(); - var body = MessageBody.For("HTTP/1.0", new Dictionary(), input.FrameContext); - var stream = new FrameRequestStream(body); + using (var input = new TestInput()) + { + var body = MessageBody.For("HTTP/1.0", new Dictionary(), input.FrameContext); + var stream = new FrameRequestStream(body); - // Input needs to be greater than 4032 bytes to allocate a block not backed by a slab. - var largeInput = new string('a', 8192); + // Input needs to be greater than 4032 bytes to allocate a block not backed by a slab. + var largeInput = new string('a', 8192); - input.Add(largeInput, true); - // Add a smaller block to the end so that SocketInput attempts to return the large - // block to the memory pool. - input.Add("Hello", true); + input.Add(largeInput, true); + // Add a smaller block to the end so that SocketInput attempts to return the large + // block to the memory pool. + input.Add("Hello", true); - var readBuffer = new byte[8192]; + var readBuffer = new byte[8192]; - var count1 = await stream.ReadAsync(readBuffer, 0, 8192); - Assert.Equal(8192, count1); - AssertASCII(largeInput, new ArraySegment(readBuffer, 0, 8192)); + var count1 = await stream.ReadAsync(readBuffer, 0, 8192); + Assert.Equal(8192, count1); + AssertASCII(largeInput, new ArraySegment(readBuffer, 0, 8192)); - var count2 = await stream.ReadAsync(readBuffer, 0, 8192); - Assert.Equal(5, count2); - AssertASCII("Hello", new ArraySegment(readBuffer, 0, 5)); + var count2 = await stream.ReadAsync(readBuffer, 0, 8192); + Assert.Equal(5, count2); + AssertASCII("Hello", new ArraySegment(readBuffer, 0, 5)); - var count3 = await stream.ReadAsync(readBuffer, 0, 8192); - Assert.Equal(0, count3); + var count3 = await stream.ReadAsync(readBuffer, 0, 8192); + Assert.Equal(0, count3); + } } private void AssertASCII(string expected, ArraySegment actual) From 903793920dd19875f9b4aa1d0615b20afe2373b1 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Fri, 13 Nov 2015 14:06:34 +0000 Subject: [PATCH 16/24] Resuse write requests Is only one per write context --- .../Http/SocketOutput.cs | 47 +++++++++++++++---- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs index 9f75719a8..fbeb964aa 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs @@ -25,6 +25,7 @@ public class SocketOutput : ISocketOutput // This locks access to to all of the below fields private readonly object _lockObj = new object(); + private bool _isDisposed; // The number of write operations that have been scheduled so far // but have not completed. @@ -236,11 +237,16 @@ private void OnWriteCompleted(WriteContext write) } if (_writeContexts.Count < _maxPooledWriteContexts - && write.Buffers.Count <= _maxPooledBufferQueues) + && write.Buffers.Count <= _maxPooledBufferQueues + && !_isDisposed) { write.Reset(); _writeContexts.Enqueue(write); } + else + { + write.Dispose(); + } // Now that the while loop has completed the following invariants should hold true: Debug.Assert(_numBytesPreCompleted >= 0); @@ -268,7 +274,21 @@ Task ISocketOutput.WriteAsync(ArraySegment buffer, bool immediate, Cancell return WriteAsync(buffer, immediate); } - private class WriteContext + private void Dispose() + { + lock (_lockObj) + { + _isDisposed = true; + + while (_writeContexts.Count > 0) + { + _writeContexts.Dequeue().Dispose(); + } + } + + } + + private class WriteContext : IDisposable { public SocketOutput Self; @@ -278,6 +298,7 @@ private class WriteContext public int WriteStatus; public Exception WriteError; + private UvWriteReq _writeReq; public int ShutdownSendStatus; @@ -285,6 +306,8 @@ public WriteContext(SocketOutput self) { Self = self; Buffers = new Queue>(_maxPooledBufferQueues); + _writeReq = new UvWriteReq(Self._log); + _writeReq.Init(Self._thread.Loop); } /// @@ -305,12 +328,9 @@ public void DoWriteIfNeeded() { 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, new ArraySegment>(buffers), (_writeReq, status, error, state) => { - _writeReq.Dispose(); var _this = (WriteContext)state; _this.WriteStatus = status; _this.WriteError = error; @@ -348,11 +368,17 @@ public void DoShutdownIfNeeded() /// public void DoDisconnectIfNeeded() { - if (SocketDisconnect == false || Self._socket.IsClosed) + if (SocketDisconnect == false) { Complete(); return; } + else if (Self._socket.IsClosed) + { + Self.Dispose(); + Complete(); + return; + } Self._socket.Dispose(); Self._log.ConnectionStop(Self._connectionId); @@ -373,6 +399,11 @@ public void Reset() WriteError = null; ShutdownSendStatus = 0; } + + public void Dispose() + { + _writeReq.Dispose(); + } } } } From 29f9f52653aebb7de6fc01f207ad0ab4ae4cdb04 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sat, 14 Nov 2015 02:56:09 +0000 Subject: [PATCH 17/24] Reuse segment array --- .../Http/SocketOutput.cs | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs index fbeb964aa..5951494ad 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs @@ -290,6 +290,8 @@ private void Dispose() private class WriteContext : IDisposable { + private const int BUFFER_COUNT = 4; + public SocketOutput Self; public Queue> Buffers; @@ -298,7 +300,9 @@ private class WriteContext : IDisposable public int WriteStatus; public Exception WriteError; + private UvWriteReq _writeReq; + public ArraySegment[] _segments; public int ShutdownSendStatus; @@ -306,6 +310,7 @@ public WriteContext(SocketOutput self) { Self = self; Buffers = new Queue>(_maxPooledBufferQueues); + _segments = new ArraySegment[BUFFER_COUNT]; _writeReq = new UvWriteReq(Self._log); _writeReq.Init(Self._thread.Loop); } @@ -321,15 +326,23 @@ public void DoWriteIfNeeded() return; } - var buffers = new ArraySegment[Buffers.Count]; + ArraySegment[] segments; + if (Buffers.Count > BUFFER_COUNT) + { + segments = new ArraySegment[Buffers.Count]; + } + else + { + segments = _segments; + } var i = 0; foreach (var buffer in Buffers) { - buffers[i++] = buffer; + segments[i++] = buffer; } - _writeReq.Write(Self._socket, new ArraySegment>(buffers), (_writeReq, status, error, state) => + _writeReq.Write(Self._socket, new ArraySegment>(segments, 0, Buffers.Count), (_writeReq, status, error, state) => { var _this = (WriteContext)state; _this.WriteStatus = status; @@ -398,6 +411,12 @@ public void Reset() WriteStatus = 0; WriteError = null; ShutdownSendStatus = 0; + + var segments = _segments; + for (var i = 0; i < segments.Length; i++) + { + segments[i] = default(ArraySegment); + } } public void Dispose() From e23711252a5b4ca62a5910e0cf98ae32251667f3 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sat, 14 Nov 2015 03:02:18 +0000 Subject: [PATCH 18/24] Initialize UvWriteReq pins size --- src/Microsoft.AspNet.Server.Kestrel/Networking/UvWriteReq.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Networking/UvWriteReq.cs b/src/Microsoft.AspNet.Server.Kestrel/Networking/UvWriteReq.cs index 103ddfc6e..4741e91f4 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Networking/UvWriteReq.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Networking/UvWriteReq.cs @@ -22,7 +22,7 @@ public class UvWriteReq : UvRequest private object _state; private const int BUFFER_COUNT = 4; - private List _pins = new List(); + private List _pins = new List(BUFFER_COUNT + 1); public UvWriteReq(IKestrelTrace logger) : base(logger) { From 65221c8a9ea6a5757a6fdfc14486c9be8eb357bd Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sat, 14 Nov 2015 03:12:31 +0000 Subject: [PATCH 19/24] Remove closure alloc --- src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs | 2 +- src/Microsoft.AspNet.Server.Kestrel/Networking/UvWriteReq.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs index 5951494ad..3bc6da71b 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/SocketOutput.cs @@ -370,7 +370,7 @@ public void DoShutdownIfNeeded() var _this = (WriteContext)state; _this.ShutdownSendStatus = status; - _this.Self._log.ConnectionWroteFin(Self._connectionId, status); + _this.Self._log.ConnectionWroteFin(_this.Self._connectionId, status); _this.DoDisconnectIfNeeded(); }, this); diff --git a/src/Microsoft.AspNet.Server.Kestrel/Networking/UvWriteReq.cs b/src/Microsoft.AspNet.Server.Kestrel/Networking/UvWriteReq.cs index 4741e91f4..dd0678552 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Networking/UvWriteReq.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Networking/UvWriteReq.cs @@ -14,7 +14,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Networking /// public class UvWriteReq : UvRequest { - private readonly static Libuv.uv_write_cb _uv_write_cb = UvWriteCb; + private readonly static Libuv.uv_write_cb _uv_write_cb = (IntPtr ptr, int status) => UvWriteCb(ptr, status); private IntPtr _bufs; From 78538c3dd420b93469cf5ffd86e4b476c11c0810 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sat, 14 Nov 2015 22:37:28 +0000 Subject: [PATCH 20/24] Skip non-consumed rather than reading --- .../Http/Frame.cs | 15 +-- .../Http/MessageBody.cs | 115 ++++++++++++++++++ .../Http/SocketInputExtensions.cs | 22 ++++ .../Infrastructure/MemoryPoolIterator2.cs | 32 +++++ 4 files changed, 172 insertions(+), 12 deletions(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs index 98e5bc56e..f188bd85c 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs @@ -230,19 +230,10 @@ public async Task RequestProcessingAsync() HttpContextFactory.Dispose(httpContext); await ProduceEnd(); - - var block = Memory2.Lease(); - try - { - var segment = block.Data; - while (await RequestBody.ReadAsync(segment.Array, segment.Offset, segment.Count) != 0) - { - // Finish reading the request body in case the app did not. - } - } - finally + + while (await MessageBody.SkipAsync() != 0) { - Memory2.Return(block); + // Finish reading the request body in case the app did not. } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/MessageBody.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/MessageBody.cs index e8e438512..393803e75 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/MessageBody.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/MessageBody.cs @@ -38,8 +38,26 @@ protected MessageBody(FrameContext context) return result; } + public Task SkipAsync(CancellationToken cancellationToken = default(CancellationToken)) + { + Task result = null; + var send100Continue = 0; + result = SkipImplementation(cancellationToken); + if (!result.IsCompleted) + { + send100Continue = Interlocked.Exchange(ref _send100Continue, 0); + } + if (send100Continue == 1) + { + _context.FrameControl.ProduceContinue(); + } + return result; + } + public abstract Task ReadAsyncImplementation(ArraySegment buffer, CancellationToken cancellationToken); + public abstract Task SkipImplementation(CancellationToken cancellationToken); + public static MessageBody For( string httpVersion, IDictionary headers, @@ -110,6 +128,10 @@ public override Task ReadAsyncImplementation(ArraySegment buffer, Can { return _context.SocketInput.ReadAsync(buffer); } + public override Task SkipImplementation(CancellationToken cancellationToken) + { + return _context.SocketInput.SkipAsync(4096); + } } class ForContentLength : MessageBody @@ -146,6 +168,27 @@ public override async Task ReadAsyncImplementation(ArraySegment buffe return actual; } + + public override async Task SkipImplementation(CancellationToken cancellationToken) + { + var input = _context.SocketInput; + + var limit = Math.Min(4096, _inputLength); + if (limit == 0) + { + return 0; + } + + var actual = await _context.SocketInput.SkipAsync(limit); + _inputLength -= actual; + + if (actual == 0) + { + throw new InvalidDataException("Unexpected end of request content"); + } + + return actual; + } } @@ -236,6 +279,78 @@ public override async Task ReadAsyncImplementation(ArraySegment buffe return 0; } + public override async Task SkipImplementation(CancellationToken cancellationToken) + { + var input = _context.SocketInput; + + while (_mode != Mode.Complete) + { + while (_mode == Mode.ChunkPrefix) + { + var chunkSize = 0; + if (!TakeChunkedLine(input, ref chunkSize)) + { + await input; + } + else if (chunkSize == 0) + { + _mode = Mode.Complete; + } + else + { + _mode = Mode.ChunkData; + } + _inputLength = chunkSize; + } + while (_mode == Mode.ChunkData) + { + var limit = Math.Min(4096, _inputLength); + if (limit != 0) + { + await input; + } + + var begin = input.ConsumingStart(); + int actual; + var end = begin.Skip(limit, out actual); + _inputLength -= actual; + input.ConsumingComplete(end, end); + + if (_inputLength == 0) + { + _mode = Mode.ChunkSuffix; + } + if (actual != 0) + { + return actual; + } + } + while (_mode == Mode.ChunkSuffix) + { + var scan = input.ConsumingStart(); + var consumed = scan; + var ch1 = scan.Take(); + var ch2 = scan.Take(); + if (ch1 == -1 || ch2 == -1) + { + input.ConsumingComplete(consumed, scan); + await input; + } + else if (ch1 == '\r' && ch2 == '\n') + { + input.ConsumingComplete(scan, scan); + _mode = Mode.ChunkPrefix; + } + else + { + throw new NotImplementedException("INVALID REQUEST FORMAT"); + } + } + } + + return 0; + } + private static bool TakeChunkedLine(SocketInput baton, ref int chunkSizeOut) { var scan = baton.ConsumingStart(); diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/SocketInputExtensions.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/SocketInputExtensions.cs index 9c5d69070..70a491565 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/SocketInputExtensions.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/SocketInputExtensions.cs @@ -29,5 +29,27 @@ public static async Task ReadAsync(this SocketInput input, ArraySegment SkipAsync(this SocketInput input, int limit) + { + while (true) + { + await input; + + var begin = input.ConsumingStart(); + int actual; + var end = begin.Skip(limit, out actual); + input.ConsumingComplete(end, end); + + if (actual != 0) + { + return actual; + } + if (input.RemoteIntakeFin) + { + return 0; + } + } + } } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolIterator2.cs b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolIterator2.cs index 0ae3f7951..2d0e3025b 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolIterator2.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolIterator2.cs @@ -628,5 +628,37 @@ public MemoryPoolIterator2 CopyTo(byte[] array, int offset, int count, out int a } } } + public MemoryPoolIterator2 Skip(int limit, out int actual) + { + if (IsDefault) + { + actual = 0; + return this; + } + + var block = _block; + var index = _index; + var remaining = limit; + while (true) + { + var following = block.End - index; + if (remaining <= following) + { + actual = limit; + return new MemoryPoolIterator2(block, index + remaining); + } + else if (block.Next == null) + { + actual = limit - remaining + following; + return new MemoryPoolIterator2(block, index + following); + } + else + { + remaining -= following; + block = block.Next; + index = block.Start; + } + } + } } } From 4c9a49275d69e3173a4bd197581b984e0c37f1d4 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sat, 14 Nov 2015 22:42:14 +0000 Subject: [PATCH 21/24] Less fine grained memory lease --- .../Http/Frame.cs | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs index f188bd85c..6fbb2ebf8 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs @@ -749,40 +749,40 @@ public static bool TakeMessageHeaders(SocketInput input, FrameRequestHeaders req var wrapping = false; - while (!scan.IsEnd) + var memoryBlock = memorypool.Lease(); + try { - if (scan.Seek('\r') == -1) + while (!scan.IsEnd) { - // no "\r" in sight, burn used bytes and go back to await more data - return false; - } + if (scan.Seek('\r') == -1) + { + // no "\r" in sight, burn used bytes and go back to await more data + return false; + } - var endValue = scan; - chFirst = scan.Take(); // expecting: /r - chSecond = scan.Take(); // expecting: /n + var endValue = scan; + chFirst = scan.Take(); // expecting: /r + chSecond = scan.Take(); // expecting: /n - if (chSecond != '\n') - { - // "\r" was all by itself, move just after it and try again - scan = endValue; - scan.Take(); - continue; - } + if (chSecond != '\n') + { + // "\r" was all by itself, move just after it and try again + scan = endValue; + scan.Take(); + continue; + } - var chThird = scan.Peek(); - if (chThird == ' ' || chThird == '\t') - { - // special case, "\r\n " or "\r\n\t". - // this is considered wrapping"linear whitespace" and is actually part of the header value - // continue past this for the next - wrapping = true; - continue; - } + var chThird = scan.Peek(); + if (chThird == ' ' || chThird == '\t') + { + // special case, "\r\n " or "\r\n\t". + // this is considered wrapping"linear whitespace" and is actually part of the header value + // continue past this for the next + wrapping = true; + continue; + } - var block = memorypool.Lease(); - try - { - var name = beginName.GetArraySegment(endName, block.Data); + var name = beginName.GetArraySegment(endName, memoryBlock.Data); var value = beginValue.GetString(endValue); if (wrapping) { @@ -793,10 +793,10 @@ public static bool TakeMessageHeaders(SocketInput input, FrameRequestHeaders req requestHeaders.Append(name.Array, name.Offset, name.Count, value); break; } - finally - { - memorypool.Return(block); - } + } + finally + { + memorypool.Return(memoryBlock); } } return false; From b40e17fa051b99f55e7a32fe838edcd848342dc4 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sat, 14 Nov 2015 22:44:18 +0000 Subject: [PATCH 22/24] ConcurrentStack to ConcurrentQueue --- .../Infrastructure/MemoryPool2.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPool2.cs b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPool2.cs index faa00e642..0ef1fb418 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPool2.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPool2.cs @@ -46,7 +46,7 @@ public class MemoryPool2 : IDisposable /// Thread-safe collection of blocks which are currently in the pool. A slab will pre-allocate all of the block tracking objects /// and add them to this collection. When memory is requested it is taken from here first, and when it is returned it is re-added. /// - private readonly ConcurrentStack _blocks = new ConcurrentStack(); + private readonly ConcurrentQueue _blocks = new ConcurrentQueue(); /// /// Thread-safe collection of slabs which have been allocated by this pool. As long as a slab is in this collection and slab.IsActive, @@ -81,7 +81,7 @@ public MemoryPoolBlock2 Lease(int minimumSize = MaxPooledBlockLength) } MemoryPoolBlock2 block; - if (_blocks.TryPop(out block)) + if (_blocks.TryDequeue(out block)) { // block successfully taken from the stack - return it return block; @@ -138,7 +138,7 @@ private MemoryPoolBlock2 AllocateSlab() public void Return(MemoryPoolBlock2 block) { block.Reset(); - _blocks.Push(block); + _blocks.Enqueue(block); } protected virtual void Dispose(bool disposing) From 01b60d6be3187b528a765eb08631e63c6323a06b Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sat, 14 Nov 2015 22:59:37 +0000 Subject: [PATCH 23/24] Add Async in method name --- .../Http/MessageBody.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/MessageBody.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/MessageBody.cs index 393803e75..545325641 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/MessageBody.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/MessageBody.cs @@ -42,7 +42,7 @@ protected MessageBody(FrameContext context) { Task result = null; var send100Continue = 0; - result = SkipImplementation(cancellationToken); + result = SkipAsyncImplementation(cancellationToken); if (!result.IsCompleted) { send100Continue = Interlocked.Exchange(ref _send100Continue, 0); @@ -56,7 +56,7 @@ protected MessageBody(FrameContext context) public abstract Task ReadAsyncImplementation(ArraySegment buffer, CancellationToken cancellationToken); - public abstract Task SkipImplementation(CancellationToken cancellationToken); + public abstract Task SkipAsyncImplementation(CancellationToken cancellationToken); public static MessageBody For( string httpVersion, @@ -128,7 +128,7 @@ public override Task ReadAsyncImplementation(ArraySegment buffer, Can { return _context.SocketInput.ReadAsync(buffer); } - public override Task SkipImplementation(CancellationToken cancellationToken) + public override Task SkipAsyncImplementation(CancellationToken cancellationToken) { return _context.SocketInput.SkipAsync(4096); } @@ -169,7 +169,7 @@ public override async Task ReadAsyncImplementation(ArraySegment buffe return actual; } - public override async Task SkipImplementation(CancellationToken cancellationToken) + public override async Task SkipAsyncImplementation(CancellationToken cancellationToken) { var input = _context.SocketInput; @@ -279,7 +279,7 @@ public override async Task ReadAsyncImplementation(ArraySegment buffe return 0; } - public override async Task SkipImplementation(CancellationToken cancellationToken) + public override async Task SkipAsyncImplementation(CancellationToken cancellationToken) { var input = _context.SocketInput; From 7f50736f97774ee8ef4987df37c4fec51d28c3e3 Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Sun, 1 Nov 2015 07:16:22 +0000 Subject: [PATCH 24/24] Reduce GetString allocs and conversions --- .../Http/Frame.cs | 23 ++-- .../Infrastructure/MemoryPoolIterator2.cs | 128 +++++++++++++++++- .../MemoryPoolBlock2Tests.cs | 12 +- .../UrlPathDecoder.cs | 8 +- 4 files changed, 143 insertions(+), 28 deletions(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs index c13826964..bc00797a5 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs @@ -629,7 +629,7 @@ private bool TakeStartLine(SocketInput input) { return false; } - var method = begin.GetString(scan); + var method = begin.GetAsciiString(ref scan); scan.Take(); begin = scan; @@ -653,7 +653,7 @@ private bool TakeStartLine(SocketInput input) { return false; } - queryString = begin.GetString(scan); + queryString = begin.GetAsciiString(ref scan); } scan.Take(); @@ -662,7 +662,7 @@ private bool TakeStartLine(SocketInput input) { return false; } - var httpVersion = begin.GetString(scan); + var httpVersion = begin.GetAsciiString(ref scan); scan.Take(); if (scan.Take() != '\n') @@ -670,12 +670,16 @@ private bool TakeStartLine(SocketInput input) return false; } + string requestUrlPath; if (needDecode) { pathEnd = UrlPathDecoder.Unescape(pathBegin, pathEnd); + requestUrlPath = pathBegin.GetUtf8String(ref pathEnd); + } + else + { + requestUrlPath = pathBegin.GetAsciiString(ref pathEnd); } - - var requestUrlPath = pathBegin.GetString(pathEnd); consumed = scan; Method = method; @@ -691,11 +695,6 @@ private bool TakeStartLine(SocketInput input) } } - static string GetString(ArraySegment range, int startIndex, int endIndex) - { - return Encoding.UTF8.GetString(range.Array, range.Offset + startIndex, endIndex - startIndex); - } - public static bool TakeMessageHeaders(SocketInput input, FrameRequestHeaders requestHeaders) { var scan = input.ConsumingStart(); @@ -786,8 +785,8 @@ public static bool TakeMessageHeaders(SocketInput input, FrameRequestHeaders req continue; } - var name = beginName.GetArraySegment(endName); - var value = beginValue.GetString(endValue); + var name = beginName.GetArraySegment(ref endName); + var value = beginValue.GetAsciiString(ref endValue); if (wrapping) { value = value.Replace("\r\n", " "); diff --git a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolIterator2.cs b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolIterator2.cs index 7de5c7fb1..3b86cc596 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolIterator2.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Infrastructure/MemoryPoolIterator2.cs @@ -10,6 +10,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Infrastructure { public struct MemoryPoolIterator2 { + private const int _maxStackAllocBytes = 16384; /// /// Array of "minus one" bytes of the length of SIMD operations on the current hardware. Used as an argument in the /// vector dot product that counts matching character occurrence. @@ -459,7 +460,7 @@ public bool Put(byte data) } } - public int GetLength(MemoryPoolIterator2 end) + public int GetLength(ref MemoryPoolIterator2 end) { if (IsDefault || end.IsDefault) { @@ -488,20 +489,135 @@ public int GetLength(MemoryPoolIterator2 end) } } - public string GetString(MemoryPoolIterator2 end) + private static unsafe string SingleBlockAsciiString(byte[] input, int offset, int length) + { + // avoid declaring other local vars, or doing work with stackalloc + // to prevent the .locals init cil flag , see: https://github.com/dotnet/coreclr/issues/1279 + char* output = stackalloc char[length]; + + return SingleBlockAsciiIter(output, input, offset, length); + } + + private static unsafe string SingleBlockAsciiIter(char* output, byte[] input, int offset, int length) + { + for (var i = 0; i < length; i++) + { + output[i] = (char)input[i + offset]; + } + return new string(output, 0, length); + } + + private static unsafe string MultiBlockAsciiString(MemoryPoolBlock2 startBlock, ref MemoryPoolIterator2 end, int inputOffset, int length) + { + // avoid declaring other local vars, or doing work with stackalloc + // to prevent the .locals init cil flag , see: https://github.com/dotnet/coreclr/issues/1279 + char* output = stackalloc char[length]; + + return MultiBlockAsciiIter(output, startBlock, ref end, inputOffset, length); + } + + private static unsafe string MultiBlockAsciiIter(char* output, MemoryPoolBlock2 startBlock, ref MemoryPoolIterator2 end, int inputOffset, int length) + { + var outputOffset = 0; + var block = startBlock; + var remaining = length; + + while(true) + { + int following = (block != end._block ? block.End : end._index) - inputOffset; + + if (following > 0) + { + var input = block.Array; + for (var i = 0; i < following; i++) + { + output[i + outputOffset] = (char)input[i + inputOffset]; + } + + remaining -= following; + outputOffset += following; + } + + if (remaining == 0) + { + return new string(output, 0, length); + } + + block = block.Next; + inputOffset = block.Start; + } + } + + public string GetAsciiStringHeap(MemoryPoolBlock2 startBlock, ref MemoryPoolIterator2 end, int inputOffset, int length) + { + var output = new char[length]; + + var outputOffset = 0; + var block = startBlock; + var remaining = length; + + while (true) + { + int following = (block != end._block ? block.End : end._index) - inputOffset; + + if (following > 0) + { + var input = block.Array; + for (var i = 0; i < following; i++) + { + output[i + outputOffset] = (char)input[i + inputOffset]; + } + + remaining -= following; + outputOffset += following; + } + + if (remaining == 0) + { + return new string(output, 0, length); + } + + block = block.Next; + inputOffset = block.Start; + } + } + + public string GetAsciiString(ref MemoryPoolIterator2 end) { if (IsDefault || end.IsDefault) { return default(string); } + + var length = GetLength(ref end); + + if (length > _maxStackAllocBytes) + { + return GetAsciiStringHeap(_block, ref end, _index, length); + } + if (end._block == _block) { - return _utf8.GetString(_block.Array, _index, end._index - _index); + return SingleBlockAsciiString(_block.Array, _index, length); } + return MultiBlockAsciiString(_block, ref end, _index, length); + } + + public string GetUtf8String(ref MemoryPoolIterator2 end) + { + if (IsDefault || end.IsDefault) + { + return default(string); + } + if (end._block == _block) + { + return _utf8.GetString(_block.Array, _index, end._index - _index); + } + var decoder = _utf8.GetDecoder(); + var length = GetLength(ref end); - var length = GetLength(end); var charLength = length * 2; var chars = new char[charLength]; var charIndex = 0; @@ -566,7 +682,7 @@ public string GetString(MemoryPoolIterator2 end) } } - public ArraySegment GetArraySegment(MemoryPoolIterator2 end) + public ArraySegment GetArraySegment(ref MemoryPoolIterator2 end) { if (IsDefault || end.IsDefault) { @@ -577,7 +693,7 @@ public ArraySegment GetArraySegment(MemoryPoolIterator2 end) return new ArraySegment(_block.Array, _index, end._index - _index); } - var length = GetLength(end); + var length = GetLength(ref end); var array = new byte[length]; CopyTo(array, 0, length, out length); return new ArraySegment(array, 0, length); diff --git a/test/Microsoft.AspNet.Server.KestrelTests/MemoryPoolBlock2Tests.cs b/test/Microsoft.AspNet.Server.KestrelTests/MemoryPoolBlock2Tests.cs index db3b03802..c09eae66c 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/MemoryPoolBlock2Tests.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/MemoryPoolBlock2Tests.cs @@ -21,15 +21,15 @@ public void SeekWorks() { var hit = iterator; hit.Seek(ch); - Assert.Equal(ch, iterator.GetLength(hit)); + Assert.Equal(ch, iterator.GetLength(ref hit)); hit = iterator; hit.Seek(ch, byte.MaxValue); - Assert.Equal(ch, iterator.GetLength(hit)); + Assert.Equal(ch, iterator.GetLength(ref hit)); hit = iterator; hit.Seek(byte.MaxValue, ch); - Assert.Equal(ch, iterator.GetLength(hit)); + Assert.Equal(ch, iterator.GetLength(ref hit)); } } } @@ -72,9 +72,9 @@ private void TestAllLengths(MemoryPoolBlock2 block, int lengths) { var first = block.GetIterator().Add(firstIndex); var last = block.GetIterator().Add(lastIndex); - Assert.Equal(firstIndex, block.GetIterator().GetLength(first)); - Assert.Equal(lastIndex, block.GetIterator().GetLength(last)); - Assert.Equal(lastIndex - firstIndex, first.GetLength(last)); + Assert.Equal(firstIndex, block.GetIterator().GetLength(ref first)); + Assert.Equal(lastIndex, block.GetIterator().GetLength(ref last)); + Assert.Equal(lastIndex - firstIndex, first.GetLength(ref last)); } } } diff --git a/test/Microsoft.AspNet.Server.KestrelTests/UrlPathDecoder.cs b/test/Microsoft.AspNet.Server.KestrelTests/UrlPathDecoder.cs index 928fc84a0..6288f615e 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/UrlPathDecoder.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/UrlPathDecoder.cs @@ -115,7 +115,7 @@ public void DecodeWithBoundary(string raw, int rawLength, string expect, int exp var end = GetIterator(begin, rawLength); var end2 = UrlPathDecoder.Unescape(begin, end); - var result = begin.GetString(end2); + var result = begin.GetUtf8String(ref end2); Assert.Equal(expectLength, result.Length); Assert.Equal(expect, result); @@ -147,7 +147,7 @@ private void PositiveAssert(string raw, string expect) var end = GetIterator(begin, raw.Length); var result = UrlPathDecoder.Unescape(begin, end); - Assert.Equal(expect, begin.GetString(result)); + Assert.Equal(expect, begin.GetUtf8String(ref result)); } private void PositiveAssert(string raw) @@ -156,7 +156,7 @@ private void PositiveAssert(string raw) var end = GetIterator(begin, raw.Length); var result = UrlPathDecoder.Unescape(begin, end); - Assert.NotEqual(raw.Length, begin.GetString(result).Length); + Assert.NotEqual(raw.Length, begin.GetUtf8String(ref result).Length); } private void NegativeAssert(string raw) @@ -165,7 +165,7 @@ private void NegativeAssert(string raw) var end = GetIterator(begin, raw.Length); var resultEnd = UrlPathDecoder.Unescape(begin, end); - var result = begin.GetString(resultEnd); + var result = begin.GetUtf8String(ref resultEnd); Assert.Equal(raw, result); } }