From da2d666674c2f326d37b47cdcd762d48fb83cfcf Mon Sep 17 00:00:00 2001 From: Ben Adams Date: Wed, 23 Dec 2015 13:08:24 +0000 Subject: [PATCH] Faster output header handling --- .../Http/Frame.cs | 11 +- .../Http/FrameHeaders.Generated.cs | 412 +++++++++++------- .../Http/FrameResponseHeaders.cs | 13 +- .../KnownHeaders.cs | 39 +- 4 files changed, 282 insertions(+), 193 deletions(-) diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs index a69c5fc0d..b4df3806e 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs @@ -640,14 +640,13 @@ private Task CreateResponseHeader( { var begin = SocketOutput.ProducingStart(); var end = begin; - if (_keepAlive) + if (_keepAlive && _responseHeaders.HasConnection) { - foreach (var connectionValue in _responseHeaders.HeaderConnection) + var connection = _responseHeaders.HeaderConnection.ToString(); + + if (connection.IndexOf("close", StringComparison.OrdinalIgnoreCase) != -1) { - if (connectionValue.IndexOf("close", StringComparison.OrdinalIgnoreCase) != -1) - { - _keepAlive = false; - } + _keepAlive = false; } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/FrameHeaders.Generated.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameHeaders.Generated.cs index 16c80b398..d13ce4a46 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/FrameHeaders.Generated.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameHeaders.Generated.cs @@ -56,6 +56,7 @@ public partial class FrameRequestHeaders private StringValues _UserAgent; + public StringValues HeaderCacheControl { get @@ -5109,6 +5110,10 @@ public partial class FrameResponseHeaders private byte[] _rawContentLength; private byte[] _rawServer; + public bool HasConnection => ((_bits & 2L) != 0); + public bool HasTransferEncoding => ((_bits & 64L) != 0); + public bool HasContentLength => ((_bits & 2048L) != 0); + public StringValues HeaderCacheControl { get @@ -8027,27 +8032,33 @@ protected override void CopyToFast(KeyValuePair[] array, i protected void CopyToFast(ref MemoryPoolIterator2 output) { - - if (((_bits & 1L) != 0)) - { - foreach(var value in _CacheControl) + + if (((_bits & 1L) != 0)) + { + var ul = _CacheControl.Count; + for (var i = 0; i < ul; i++) { + var value = _CacheControl[i]; if (value != null) { output.CopyFrom(_headerBytes, 0, 17); output.CopyFromAscii(value); } } - } - - if (((_bits & 2L) != 0)) - { - if (_rawConnection != null) - { - output.CopyFrom(_rawConnection, 0, _rawConnection.Length); - } else - foreach(var value in _Connection) + } + + if (((_bits & 2L) != 0)) + { + if (_rawConnection != null) + { + output.CopyFrom(_rawConnection, 0, _rawConnection.Length); + } + else + { + var ul = _Connection.Count; + for (var i = 0; i < ul; i++) { + var value = _Connection[i]; if (value != null) { output.CopyFrom(_headerBytes, 17, 14); @@ -8055,15 +8066,20 @@ protected void CopyToFast(ref MemoryPoolIterator2 output) } } } - - if (((_bits & 4L) != 0)) - { - if (_rawDate != null) - { - output.CopyFrom(_rawDate, 0, _rawDate.Length); - } else - foreach(var value in _Date) + } + + if (((_bits & 4L) != 0)) + { + if (_rawDate != null) + { + output.CopyFrom(_rawDate, 0, _rawDate.Length); + } + else + { + var ul = _Date.Count; + for (var i = 0; i < ul; i++) { + var value = _Date[i]; if (value != null) { output.CopyFrom(_headerBytes, 31, 8); @@ -8071,51 +8087,62 @@ protected void CopyToFast(ref MemoryPoolIterator2 output) } } } - - if (((_bits & 8L) != 0)) - { - foreach(var value in _KeepAlive) + } + + if (((_bits & 8L) != 0)) + { + var ul = _KeepAlive.Count; + for (var i = 0; i < ul; i++) { + var value = _KeepAlive[i]; if (value != null) { output.CopyFrom(_headerBytes, 39, 14); output.CopyFromAscii(value); } } - } - - if (((_bits & 16L) != 0)) - { - foreach(var value in _Pragma) + } + + if (((_bits & 16L) != 0)) + { + var ul = _Pragma.Count; + for (var i = 0; i < ul; i++) { + var value = _Pragma[i]; if (value != null) { output.CopyFrom(_headerBytes, 53, 10); output.CopyFromAscii(value); } } - } - - if (((_bits & 32L) != 0)) - { - foreach(var value in _Trailer) + } + + if (((_bits & 32L) != 0)) + { + var ul = _Trailer.Count; + for (var i = 0; i < ul; i++) { + var value = _Trailer[i]; if (value != null) { output.CopyFrom(_headerBytes, 63, 11); output.CopyFromAscii(value); } } - } - - if (((_bits & 64L) != 0)) - { - if (_rawTransferEncoding != null) - { - output.CopyFrom(_rawTransferEncoding, 0, _rawTransferEncoding.Length); - } else - foreach(var value in _TransferEncoding) + } + + if (((_bits & 64L) != 0)) + { + if (_rawTransferEncoding != null) + { + output.CopyFrom(_rawTransferEncoding, 0, _rawTransferEncoding.Length); + } + else + { + var ul = _TransferEncoding.Count; + for (var i = 0; i < ul; i++) { + var value = _TransferEncoding[i]; if (value != null) { output.CopyFrom(_headerBytes, 74, 21); @@ -8123,63 +8150,76 @@ protected void CopyToFast(ref MemoryPoolIterator2 output) } } } - - if (((_bits & 128L) != 0)) - { - foreach(var value in _Upgrade) + } + + if (((_bits & 128L) != 0)) + { + var ul = _Upgrade.Count; + for (var i = 0; i < ul; i++) { + var value = _Upgrade[i]; if (value != null) { output.CopyFrom(_headerBytes, 95, 11); output.CopyFromAscii(value); } } - } - - if (((_bits & 256L) != 0)) - { - foreach(var value in _Via) + } + + if (((_bits & 256L) != 0)) + { + var ul = _Via.Count; + for (var i = 0; i < ul; i++) { + var value = _Via[i]; if (value != null) { output.CopyFrom(_headerBytes, 106, 7); output.CopyFromAscii(value); } } - } - - if (((_bits & 512L) != 0)) - { - foreach(var value in _Warning) + } + + if (((_bits & 512L) != 0)) + { + var ul = _Warning.Count; + for (var i = 0; i < ul; i++) { + var value = _Warning[i]; if (value != null) { output.CopyFrom(_headerBytes, 113, 11); output.CopyFromAscii(value); } } - } - - if (((_bits & 1024L) != 0)) - { - foreach(var value in _Allow) + } + + if (((_bits & 1024L) != 0)) + { + var ul = _Allow.Count; + for (var i = 0; i < ul; i++) { + var value = _Allow[i]; if (value != null) { output.CopyFrom(_headerBytes, 124, 9); output.CopyFromAscii(value); } } - } - - if (((_bits & 2048L) != 0)) - { - if (_rawContentLength != null) - { - output.CopyFrom(_rawContentLength, 0, _rawContentLength.Length); - } else - foreach(var value in _ContentLength) + } + + if (((_bits & 2048L) != 0)) + { + if (_rawContentLength != null) + { + output.CopyFrom(_rawContentLength, 0, _rawContentLength.Length); + } + else + { + var ul = _ContentLength.Count; + for (var i = 0; i < ul; i++) { + var value = _ContentLength[i]; if (value != null) { output.CopyFrom(_headerBytes, 133, 18); @@ -8187,183 +8227,216 @@ protected void CopyToFast(ref MemoryPoolIterator2 output) } } } - - if (((_bits & 4096L) != 0)) - { - foreach(var value in _ContentType) + } + + if (((_bits & 4096L) != 0)) + { + var ul = _ContentType.Count; + for (var i = 0; i < ul; i++) { + var value = _ContentType[i]; if (value != null) { output.CopyFrom(_headerBytes, 151, 16); output.CopyFromAscii(value); } } - } - - if (((_bits & 8192L) != 0)) - { - foreach(var value in _ContentEncoding) + } + + if (((_bits & 8192L) != 0)) + { + var ul = _ContentEncoding.Count; + for (var i = 0; i < ul; i++) { + var value = _ContentEncoding[i]; if (value != null) { output.CopyFrom(_headerBytes, 167, 20); output.CopyFromAscii(value); } } - } - - if (((_bits & 16384L) != 0)) - { - foreach(var value in _ContentLanguage) + } + + if (((_bits & 16384L) != 0)) + { + var ul = _ContentLanguage.Count; + for (var i = 0; i < ul; i++) { + var value = _ContentLanguage[i]; if (value != null) { output.CopyFrom(_headerBytes, 187, 20); output.CopyFromAscii(value); } } - } - - if (((_bits & 32768L) != 0)) - { - foreach(var value in _ContentLocation) + } + + if (((_bits & 32768L) != 0)) + { + var ul = _ContentLocation.Count; + for (var i = 0; i < ul; i++) { + var value = _ContentLocation[i]; if (value != null) { output.CopyFrom(_headerBytes, 207, 20); output.CopyFromAscii(value); } } - } - - if (((_bits & 65536L) != 0)) - { - foreach(var value in _ContentMD5) + } + + if (((_bits & 65536L) != 0)) + { + var ul = _ContentMD5.Count; + for (var i = 0; i < ul; i++) { + var value = _ContentMD5[i]; if (value != null) { output.CopyFrom(_headerBytes, 227, 15); output.CopyFromAscii(value); } } - } - - if (((_bits & 131072L) != 0)) - { - foreach(var value in _ContentRange) + } + + if (((_bits & 131072L) != 0)) + { + var ul = _ContentRange.Count; + for (var i = 0; i < ul; i++) { + var value = _ContentRange[i]; if (value != null) { output.CopyFrom(_headerBytes, 242, 17); output.CopyFromAscii(value); } } - } - - if (((_bits & 262144L) != 0)) - { - foreach(var value in _Expires) + } + + if (((_bits & 262144L) != 0)) + { + var ul = _Expires.Count; + for (var i = 0; i < ul; i++) { + var value = _Expires[i]; if (value != null) { output.CopyFrom(_headerBytes, 259, 11); output.CopyFromAscii(value); } } - } - - if (((_bits & 524288L) != 0)) - { - foreach(var value in _LastModified) + } + + if (((_bits & 524288L) != 0)) + { + var ul = _LastModified.Count; + for (var i = 0; i < ul; i++) { + var value = _LastModified[i]; if (value != null) { output.CopyFrom(_headerBytes, 270, 17); output.CopyFromAscii(value); } } - } - - if (((_bits & 1048576L) != 0)) - { - foreach(var value in _AcceptRanges) + } + + if (((_bits & 1048576L) != 0)) + { + var ul = _AcceptRanges.Count; + for (var i = 0; i < ul; i++) { + var value = _AcceptRanges[i]; if (value != null) { output.CopyFrom(_headerBytes, 287, 17); output.CopyFromAscii(value); } } - } - - if (((_bits & 2097152L) != 0)) - { - foreach(var value in _Age) + } + + if (((_bits & 2097152L) != 0)) + { + var ul = _Age.Count; + for (var i = 0; i < ul; i++) { + var value = _Age[i]; if (value != null) { output.CopyFrom(_headerBytes, 304, 7); output.CopyFromAscii(value); } } - } - - if (((_bits & 4194304L) != 0)) - { - foreach(var value in _ETag) + } + + if (((_bits & 4194304L) != 0)) + { + var ul = _ETag.Count; + for (var i = 0; i < ul; i++) { + var value = _ETag[i]; if (value != null) { output.CopyFrom(_headerBytes, 311, 8); output.CopyFromAscii(value); } } - } - - if (((_bits & 8388608L) != 0)) - { - foreach(var value in _Location) + } + + if (((_bits & 8388608L) != 0)) + { + var ul = _Location.Count; + for (var i = 0; i < ul; i++) { + var value = _Location[i]; if (value != null) { output.CopyFrom(_headerBytes, 319, 12); output.CopyFromAscii(value); } } - } - - if (((_bits & 16777216L) != 0)) - { - foreach(var value in _ProxyAutheticate) + } + + if (((_bits & 16777216L) != 0)) + { + var ul = _ProxyAutheticate.Count; + for (var i = 0; i < ul; i++) { + var value = _ProxyAutheticate[i]; if (value != null) { output.CopyFrom(_headerBytes, 331, 21); output.CopyFromAscii(value); } } - } - - if (((_bits & 33554432L) != 0)) - { - foreach(var value in _RetryAfter) + } + + if (((_bits & 33554432L) != 0)) + { + var ul = _RetryAfter.Count; + for (var i = 0; i < ul; i++) { + var value = _RetryAfter[i]; if (value != null) { output.CopyFrom(_headerBytes, 352, 15); output.CopyFromAscii(value); } } - } - - if (((_bits & 67108864L) != 0)) - { - if (_rawServer != null) - { - output.CopyFrom(_rawServer, 0, _rawServer.Length); - } else - foreach(var value in _Server) + } + + if (((_bits & 67108864L) != 0)) + { + if (_rawServer != null) + { + output.CopyFrom(_rawServer, 0, _rawServer.Length); + } + else + { + var ul = _Server.Count; + for (var i = 0; i < ul; i++) { + var value = _Server[i]; if (value != null) { output.CopyFrom(_headerBytes, 367, 10); @@ -8371,43 +8444,50 @@ protected void CopyToFast(ref MemoryPoolIterator2 output) } } } - - if (((_bits & 134217728L) != 0)) - { - foreach(var value in _SetCookie) + } + + if (((_bits & 134217728L) != 0)) + { + var ul = _SetCookie.Count; + for (var i = 0; i < ul; i++) { + var value = _SetCookie[i]; if (value != null) { output.CopyFrom(_headerBytes, 377, 14); output.CopyFromAscii(value); } } - } - - if (((_bits & 268435456L) != 0)) - { - foreach(var value in _Vary) + } + + if (((_bits & 268435456L) != 0)) + { + var ul = _Vary.Count; + for (var i = 0; i < ul; i++) { + var value = _Vary[i]; if (value != null) { output.CopyFrom(_headerBytes, 391, 8); output.CopyFromAscii(value); } } - } - - if (((_bits & 536870912L) != 0)) - { - foreach(var value in _WWWAuthenticate) + } + + if (((_bits & 536870912L) != 0)) + { + var ul = _WWWAuthenticate.Count; + for (var i = 0; i < ul; i++) { + var value = _WWWAuthenticate[i]; if (value != null) { output.CopyFrom(_headerBytes, 399, 20); output.CopyFromAscii(value); } } - } - + } + } public unsafe void Append(byte[] keyBytes, int keyOffset, int keyLength, string value) { diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/FrameResponseHeaders.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameResponseHeaders.cs index a5a42e6a9..9b0f44fa5 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/FrameResponseHeaders.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameResponseHeaders.cs @@ -13,13 +13,6 @@ public partial class FrameResponseHeaders : FrameHeaders private static byte[] _CrLf = new[] { (byte)'\r', (byte)'\n' }; private static byte[] _colonSpace = new[] { (byte)':', (byte)' ' }; - public bool HasConnection => HeaderConnection.Count != 0; - - public bool HasTransferEncoding => HeaderTransferEncoding.Count != 0; - - public bool HasContentLength => HeaderContentLength.Count != 0; - - public Enumerator GetEnumerator() { return new Enumerator(this); @@ -33,12 +26,14 @@ protected override IEnumerator> GetEnumerator public void CopyTo(ref MemoryPoolIterator2 output) { CopyToFast(ref output); - if (MaybeUnknown != null) + if (MaybeUnknown != null && MaybeUnknown.Count > 0) { foreach (var kv in MaybeUnknown) { - foreach (var value in kv.Value) + var ul = kv.Value.Count; + for (var i = 0; i < ul; i++) { + var value = kv.Value[i]; if (value != null) { output.CopyFrom(_CrLf, 0, 2); diff --git a/tools/Microsoft.AspNet.Server.Kestrel.GeneratedCode/KnownHeaders.cs b/tools/Microsoft.AspNet.Server.Kestrel.GeneratedCode/KnownHeaders.cs index df3cef028..64ae523e0 100644 --- a/tools/Microsoft.AspNet.Server.Kestrel.GeneratedCode/KnownHeaders.cs +++ b/tools/Microsoft.AspNet.Server.Kestrel.GeneratedCode/KnownHeaders.cs @@ -25,6 +25,7 @@ class KnownHeader public int BytesOffset { get; set; } public int BytesCount { get; set; } public bool EnhancedSetter { get; set; } + public bool FastCheck { get; set; } public string TestBit() => $"((_bits & {1L << Index}L) != 0)"; public string SetBit() => $"_bits |= {1L << Index}L"; public string ClearBit() => $"_bits &= ~{1L << Index}L"; @@ -139,6 +140,12 @@ public static string GeneratedFile() "Transfer-Encoding", "Content-Length", }; + var fastCheckHeaders = new[] + { + "Connection", + "Transfer-Encoding", + "Content-Length", + }; var responseHeaders = commonHeaders.Concat(new[] { "Accept-Ranges", @@ -155,7 +162,8 @@ public static string GeneratedFile() { Name = header, Index = index, - EnhancedSetter = enhancedHeaders.Contains(header) + EnhancedSetter = enhancedHeaders.Contains(header), + FastCheck = fastCheckHeaders.Contains(header) }).ToArray(); var loops = new[] { @@ -208,6 +216,8 @@ public partial class {loop.ClassName} private StringValues _" + header.Identifier + ";")} {Each(loop.Headers.Where(header => header.EnhancedSetter), header => @" private byte[] _raw" + header.Identifier + ";")} + {Each(loop.Headers.Where(header => header.FastCheck), header => @" + public bool Has" + header.Identifier + $" => {header.TestBit()};")} {Each(loop.Headers, header => $@" public StringValues Header{header.Identifier} {{ @@ -382,23 +392,28 @@ protected override void CopyToFast(KeyValuePair[] array, i {(loop.ClassName == "FrameResponseHeaders" ? $@" protected void CopyToFast(ref MemoryPoolIterator2 output) {{ - {Each(loop.Headers, header => $@" - if ({header.TestBit()}) - {{ {(header.EnhancedSetter == false ? "" : $@" - if (_raw{header.Identifier} != null) - {{ - output.CopyFrom(_raw{header.Identifier}, 0, _raw{header.Identifier}.Length); - }} else ")} - foreach(var value in _{header.Identifier}) + {Each(loop.Headers, header => $@" + if ({header.TestBit()}) + {{ {(header.EnhancedSetter == false ? "" : $@" + if (_raw{header.Identifier} != null) + {{ + output.CopyFrom(_raw{header.Identifier}, 0, _raw{header.Identifier}.Length); + }} + else + {{")} + var ul = _{header.Identifier}.Count; + for (var i = 0; i < ul; i++) {{ + var value = _{header.Identifier}[i]; if (value != null) {{ output.CopyFrom(_headerBytes, {header.BytesOffset}, {header.BytesCount}); output.CopyFromAscii(value); }} - }} - }} - ")} + }}{(header.EnhancedSetter == false ? "" : @" + }")} + }} + ")} }}" : "")} public unsafe void Append(byte[] keyBytes, int keyOffset, int keyLength, string value) {{