From 4e9f5e8a78886715ae365a69e73b19e4bee3765a Mon Sep 17 00:00:00 2001 From: Marek Safar Date: Tue, 7 Jul 2020 06:15:59 +0200 Subject: [PATCH] Move rarely used lookup table out of HeaderDescriptor static ctor (#38761) * Move rarely used lookup table out of HeaderDescriptor static ctor * Move QPackStaticTable to its own file --- .../System.Net.Http/src/System.Net.Http.csproj | 1 + .../System/Net/Http/Headers/HeaderDescriptor.cs | 113 +------------------- .../System/Net/Http/Headers/QPackStaticTable.cs | 114 +++++++++++++++++++++ .../UnitTests/System.Net.Http.Unit.Tests.csproj | 2 + 4 files changed, 121 insertions(+), 109 deletions(-) create mode 100644 src/libraries/System.Net.Http/src/System/Net/Http/Headers/QPackStaticTable.cs diff --git a/src/libraries/System.Net.Http/src/System.Net.Http.csproj b/src/libraries/System.Net.Http/src/System.Net.Http.csproj index fee004f..dd382e6 100644 --- a/src/libraries/System.Net.Http/src/System.Net.Http.csproj +++ b/src/libraries/System.Net.Http/src/System.Net.Http.csproj @@ -83,6 +83,7 @@ + diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/HeaderDescriptor.cs b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/HeaderDescriptor.cs index 2bb0185..dd46c16 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/HeaderDescriptor.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/HeaderDescriptor.cs @@ -25,7 +25,7 @@ namespace System.Net.Http.Headers } // This should not be used directly; use static TryGet below - private HeaderDescriptor(string headerName) + internal HeaderDescriptor(string headerName) { _headerName = headerName; _knownHeader = null; @@ -90,10 +90,11 @@ namespace System.Net.Http.Headers internal static bool TryGetStaticQPackHeader(int index, out HeaderDescriptor descriptor, [NotNullWhen(true)] out string? knownValue) { Debug.Assert(index >= 0); - Debug.Assert(s_qpackHeaderLookup.Length == 99); // Micro-opt: store field to variable to prevent Length re-read and use unsigned to avoid bounds check. - (HeaderDescriptor descriptor, string value)[] qpackStaticTable = s_qpackHeaderLookup; + (HeaderDescriptor descriptor, string value)[] qpackStaticTable = QPackStaticTable.HeaderLookup; + Debug.Assert(qpackStaticTable.Length == 99); + uint uindex = (uint)index; if (uindex < (uint)qpackStaticTable.Length) @@ -263,111 +264,5 @@ namespace System.Net.Http.Headers decoded = null; return false; } - - // QPack Static Table - // https://tools.ietf.org/html/draft-ietf-quic-qpack-11#appendix-A - // TODO: can we put some of this logic into H3StaticTable and/or generate it using data that is already there? - private static readonly (HeaderDescriptor descriptor, string value)[] s_qpackHeaderLookup = new (HeaderDescriptor descriptor, string value)[] - { - (new HeaderDescriptor(":authority"), ""), - (new HeaderDescriptor(":path"), "/"), - (new HeaderDescriptor(KnownHeaders.Age), "0"), - (new HeaderDescriptor(KnownHeaders.ContentDisposition), ""), - (new HeaderDescriptor(KnownHeaders.ContentLength), "0"), - (new HeaderDescriptor(KnownHeaders.Date), ""), - (new HeaderDescriptor(KnownHeaders.ETag), ""), - (new HeaderDescriptor(KnownHeaders.IfModifiedSince), ""), - (new HeaderDescriptor(KnownHeaders.IfNoneMatch), ""), - (new HeaderDescriptor(KnownHeaders.LastModified), ""), - (new HeaderDescriptor(KnownHeaders.Link), ""), - (new HeaderDescriptor(KnownHeaders.Location), ""), - (new HeaderDescriptor(KnownHeaders.Referer), ""), - (new HeaderDescriptor(KnownHeaders.SetCookie), ""), - (new HeaderDescriptor(":method"), "CONNECT"), - (new HeaderDescriptor(":method"), "DELETE"), - (new HeaderDescriptor(":method"), "GET"), - (new HeaderDescriptor(":method"), "HEAD"), - (new HeaderDescriptor(":method"), "OPTIONS"), - (new HeaderDescriptor(":method"), "POST"), - (new HeaderDescriptor(":method"), "PUT"), - (new HeaderDescriptor(":scheme"), "http"), - (new HeaderDescriptor(":scheme"), "https"), - (new HeaderDescriptor(KnownHeaders.PseudoStatus), "103"), - (new HeaderDescriptor(KnownHeaders.PseudoStatus), "200"), - (new HeaderDescriptor(KnownHeaders.PseudoStatus), "304"), - (new HeaderDescriptor(KnownHeaders.PseudoStatus), "404"), - (new HeaderDescriptor(KnownHeaders.PseudoStatus), "503"), - (new HeaderDescriptor(KnownHeaders.Accept), "*/*"), - (new HeaderDescriptor(KnownHeaders.Accept), "application/dns-message"), - (new HeaderDescriptor(KnownHeaders.AcceptEncoding), "gzip, deflate, br"), - (new HeaderDescriptor(KnownHeaders.AcceptRanges), "bytes"), - (new HeaderDescriptor(KnownHeaders.AccessControlAllowHeaders), "cache-control"), - (new HeaderDescriptor(KnownHeaders.AccessControlAllowHeaders), "content-type"), - (new HeaderDescriptor(KnownHeaders.AccessControlAllowHeaders), "*"), - (new HeaderDescriptor(KnownHeaders.AccessControlAllowOrigin), "*"), - (new HeaderDescriptor(KnownHeaders.CacheControl), "max-age=0"), - (new HeaderDescriptor(KnownHeaders.CacheControl), "max-age=2592000"), - (new HeaderDescriptor(KnownHeaders.CacheControl), "max-age=604800"), - (new HeaderDescriptor(KnownHeaders.CacheControl), "no-cache"), - (new HeaderDescriptor(KnownHeaders.CacheControl), "no-store"), - (new HeaderDescriptor(KnownHeaders.CacheControl), "public, max-age=31536000"), - (new HeaderDescriptor(KnownHeaders.ContentEncoding), "br"), - (new HeaderDescriptor(KnownHeaders.ContentEncoding), "gzip"), - (new HeaderDescriptor(KnownHeaders.ContentType), "application/dns-message"), - (new HeaderDescriptor(KnownHeaders.ContentType), "application/javascript"), - (new HeaderDescriptor(KnownHeaders.ContentType), "application/json"), - (new HeaderDescriptor(KnownHeaders.ContentType), "application/x-www-form-urlencoded"), - (new HeaderDescriptor(KnownHeaders.ContentType), "image/gif"), - (new HeaderDescriptor(KnownHeaders.ContentType), "image/jpeg"), - (new HeaderDescriptor(KnownHeaders.ContentType), "image/png"), - (new HeaderDescriptor(KnownHeaders.ContentType), "text/css"), - (new HeaderDescriptor(KnownHeaders.ContentType), "text/html; charset=utf-8"), // Whitespace is correct, see spec. - (new HeaderDescriptor(KnownHeaders.ContentType), "text/plain"), - (new HeaderDescriptor(KnownHeaders.ContentType), "text/plain;charset=utf-8"), // Whitespace is correct, see spec. - (new HeaderDescriptor(KnownHeaders.Range), "bytes=0-"), - (new HeaderDescriptor(KnownHeaders.StrictTransportSecurity), "max-age=31536000"), - (new HeaderDescriptor(KnownHeaders.StrictTransportSecurity), "max-age=31536000; includesubdomains"), - (new HeaderDescriptor(KnownHeaders.StrictTransportSecurity), "max-age=31536000; includesubdomains; preload"), - (new HeaderDescriptor(KnownHeaders.Vary), "accept-encoding"), - (new HeaderDescriptor(KnownHeaders.Vary), "origin"), - (new HeaderDescriptor(KnownHeaders.XContentTypeOptions), "nosniff"), - (new HeaderDescriptor("x-xss-protection"), "1; mode=block"), - (new HeaderDescriptor(KnownHeaders.PseudoStatus), "100"), - (new HeaderDescriptor(KnownHeaders.PseudoStatus), "204"), - (new HeaderDescriptor(KnownHeaders.PseudoStatus), "206"), - (new HeaderDescriptor(KnownHeaders.PseudoStatus), "302"), - (new HeaderDescriptor(KnownHeaders.PseudoStatus), "400"), - (new HeaderDescriptor(KnownHeaders.PseudoStatus), "403"), - (new HeaderDescriptor(KnownHeaders.PseudoStatus), "421"), - (new HeaderDescriptor(KnownHeaders.PseudoStatus), "425"), - (new HeaderDescriptor(KnownHeaders.PseudoStatus), "500"), - (new HeaderDescriptor(KnownHeaders.AcceptLanguage), ""), - (new HeaderDescriptor(KnownHeaders.AccessControlAllowCredentials), "FALSE"), - (new HeaderDescriptor(KnownHeaders.AccessControlAllowCredentials), "TRUE"), - (new HeaderDescriptor(KnownHeaders.AccessControlAllowHeaders), "*"), - (new HeaderDescriptor(KnownHeaders.AccessControlAllowMethods), "get"), - (new HeaderDescriptor(KnownHeaders.AccessControlAllowMethods), "get, post, options"), - (new HeaderDescriptor(KnownHeaders.AccessControlAllowMethods), "options"), - (new HeaderDescriptor(KnownHeaders.AccessControlExposeHeaders), "content-length"), - (new HeaderDescriptor("access-control-request-headers"), "content-type"), - (new HeaderDescriptor("access-control-request-method"), "get"), - (new HeaderDescriptor("access-control-request-method"), "post"), - (new HeaderDescriptor(KnownHeaders.AltSvc), "clear"), - (new HeaderDescriptor(KnownHeaders.Authorization), ""), - (new HeaderDescriptor(KnownHeaders.ContentSecurityPolicy), "script-src 'none'; object-src 'none'; base-uri 'none'"), - (new HeaderDescriptor("early-data"), "1"), - (new HeaderDescriptor("expect-ct"), ""), - (new HeaderDescriptor("forwarded"), ""), - (new HeaderDescriptor(KnownHeaders.IfRange), ""), - (new HeaderDescriptor(KnownHeaders.Origin), ""), - (new HeaderDescriptor("purpose"), "prefetch"), - (new HeaderDescriptor(KnownHeaders.Server), ""), - (new HeaderDescriptor("timing-allow-origin"), "*"), - (new HeaderDescriptor(KnownHeaders.UpgradeInsecureRequests), "1"), - (new HeaderDescriptor(KnownHeaders.UserAgent), ""), - (new HeaderDescriptor("x-forwarded-for"), ""), - (new HeaderDescriptor(KnownHeaders.XFrameOptions), "deny"), - (new HeaderDescriptor(KnownHeaders.XFrameOptions), "sameorigin") - }; } } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/QPackStaticTable.cs b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/QPackStaticTable.cs new file mode 100644 index 0000000..c8e0cd7 --- /dev/null +++ b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/QPackStaticTable.cs @@ -0,0 +1,114 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace System.Net.Http.Headers +{ + internal static class QPackStaticTable + { + // https://tools.ietf.org/html/draft-ietf-quic-qpack-11#appendix-A + // TODO: can we put some of this logic into H3StaticTable and/or generate it using data that is already there? + internal static (HeaderDescriptor descriptor, string value)[] HeaderLookup { get; } = new (HeaderDescriptor descriptor, string value)[] + { + (new HeaderDescriptor(":authority"), ""), + (new HeaderDescriptor(":path"), "/"), + (new HeaderDescriptor(KnownHeaders.Age), "0"), + (new HeaderDescriptor(KnownHeaders.ContentDisposition), ""), + (new HeaderDescriptor(KnownHeaders.ContentLength), "0"), + (new HeaderDescriptor(KnownHeaders.Date), ""), + (new HeaderDescriptor(KnownHeaders.ETag), ""), + (new HeaderDescriptor(KnownHeaders.IfModifiedSince), ""), + (new HeaderDescriptor(KnownHeaders.IfNoneMatch), ""), + (new HeaderDescriptor(KnownHeaders.LastModified), ""), + (new HeaderDescriptor(KnownHeaders.Link), ""), + (new HeaderDescriptor(KnownHeaders.Location), ""), + (new HeaderDescriptor(KnownHeaders.Referer), ""), + (new HeaderDescriptor(KnownHeaders.SetCookie), ""), + (new HeaderDescriptor(":method"), "CONNECT"), + (new HeaderDescriptor(":method"), "DELETE"), + (new HeaderDescriptor(":method"), "GET"), + (new HeaderDescriptor(":method"), "HEAD"), + (new HeaderDescriptor(":method"), "OPTIONS"), + (new HeaderDescriptor(":method"), "POST"), + (new HeaderDescriptor(":method"), "PUT"), + (new HeaderDescriptor(":scheme"), "http"), + (new HeaderDescriptor(":scheme"), "https"), + (new HeaderDescriptor(KnownHeaders.PseudoStatus), "103"), + (new HeaderDescriptor(KnownHeaders.PseudoStatus), "200"), + (new HeaderDescriptor(KnownHeaders.PseudoStatus), "304"), + (new HeaderDescriptor(KnownHeaders.PseudoStatus), "404"), + (new HeaderDescriptor(KnownHeaders.PseudoStatus), "503"), + (new HeaderDescriptor(KnownHeaders.Accept), "*/*"), + (new HeaderDescriptor(KnownHeaders.Accept), "application/dns-message"), + (new HeaderDescriptor(KnownHeaders.AcceptEncoding), "gzip, deflate, br"), + (new HeaderDescriptor(KnownHeaders.AcceptRanges), "bytes"), + (new HeaderDescriptor(KnownHeaders.AccessControlAllowHeaders), "cache-control"), + (new HeaderDescriptor(KnownHeaders.AccessControlAllowHeaders), "content-type"), + (new HeaderDescriptor(KnownHeaders.AccessControlAllowHeaders), "*"), + (new HeaderDescriptor(KnownHeaders.AccessControlAllowOrigin), "*"), + (new HeaderDescriptor(KnownHeaders.CacheControl), "max-age=0"), + (new HeaderDescriptor(KnownHeaders.CacheControl), "max-age=2592000"), + (new HeaderDescriptor(KnownHeaders.CacheControl), "max-age=604800"), + (new HeaderDescriptor(KnownHeaders.CacheControl), "no-cache"), + (new HeaderDescriptor(KnownHeaders.CacheControl), "no-store"), + (new HeaderDescriptor(KnownHeaders.CacheControl), "public, max-age=31536000"), + (new HeaderDescriptor(KnownHeaders.ContentEncoding), "br"), + (new HeaderDescriptor(KnownHeaders.ContentEncoding), "gzip"), + (new HeaderDescriptor(KnownHeaders.ContentType), "application/dns-message"), + (new HeaderDescriptor(KnownHeaders.ContentType), "application/javascript"), + (new HeaderDescriptor(KnownHeaders.ContentType), "application/json"), + (new HeaderDescriptor(KnownHeaders.ContentType), "application/x-www-form-urlencoded"), + (new HeaderDescriptor(KnownHeaders.ContentType), "image/gif"), + (new HeaderDescriptor(KnownHeaders.ContentType), "image/jpeg"), + (new HeaderDescriptor(KnownHeaders.ContentType), "image/png"), + (new HeaderDescriptor(KnownHeaders.ContentType), "text/css"), + (new HeaderDescriptor(KnownHeaders.ContentType), "text/html; charset=utf-8"), // Whitespace is correct, see spec. + (new HeaderDescriptor(KnownHeaders.ContentType), "text/plain"), + (new HeaderDescriptor(KnownHeaders.ContentType), "text/plain;charset=utf-8"), // Whitespace is correct, see spec. + (new HeaderDescriptor(KnownHeaders.Range), "bytes=0-"), + (new HeaderDescriptor(KnownHeaders.StrictTransportSecurity), "max-age=31536000"), + (new HeaderDescriptor(KnownHeaders.StrictTransportSecurity), "max-age=31536000; includesubdomains"), + (new HeaderDescriptor(KnownHeaders.StrictTransportSecurity), "max-age=31536000; includesubdomains; preload"), + (new HeaderDescriptor(KnownHeaders.Vary), "accept-encoding"), + (new HeaderDescriptor(KnownHeaders.Vary), "origin"), + (new HeaderDescriptor(KnownHeaders.XContentTypeOptions), "nosniff"), + (new HeaderDescriptor("x-xss-protection"), "1; mode=block"), + (new HeaderDescriptor(KnownHeaders.PseudoStatus), "100"), + (new HeaderDescriptor(KnownHeaders.PseudoStatus), "204"), + (new HeaderDescriptor(KnownHeaders.PseudoStatus), "206"), + (new HeaderDescriptor(KnownHeaders.PseudoStatus), "302"), + (new HeaderDescriptor(KnownHeaders.PseudoStatus), "400"), + (new HeaderDescriptor(KnownHeaders.PseudoStatus), "403"), + (new HeaderDescriptor(KnownHeaders.PseudoStatus), "421"), + (new HeaderDescriptor(KnownHeaders.PseudoStatus), "425"), + (new HeaderDescriptor(KnownHeaders.PseudoStatus), "500"), + (new HeaderDescriptor(KnownHeaders.AcceptLanguage), ""), + (new HeaderDescriptor(KnownHeaders.AccessControlAllowCredentials), "FALSE"), + (new HeaderDescriptor(KnownHeaders.AccessControlAllowCredentials), "TRUE"), + (new HeaderDescriptor(KnownHeaders.AccessControlAllowHeaders), "*"), + (new HeaderDescriptor(KnownHeaders.AccessControlAllowMethods), "get"), + (new HeaderDescriptor(KnownHeaders.AccessControlAllowMethods), "get, post, options"), + (new HeaderDescriptor(KnownHeaders.AccessControlAllowMethods), "options"), + (new HeaderDescriptor(KnownHeaders.AccessControlExposeHeaders), "content-length"), + (new HeaderDescriptor("access-control-request-headers"), "content-type"), + (new HeaderDescriptor("access-control-request-method"), "get"), + (new HeaderDescriptor("access-control-request-method"), "post"), + (new HeaderDescriptor(KnownHeaders.AltSvc), "clear"), + (new HeaderDescriptor(KnownHeaders.Authorization), ""), + (new HeaderDescriptor(KnownHeaders.ContentSecurityPolicy), "script-src 'none'; object-src 'none'; base-uri 'none'"), + (new HeaderDescriptor("early-data"), "1"), + (new HeaderDescriptor("expect-ct"), ""), + (new HeaderDescriptor("forwarded"), ""), + (new HeaderDescriptor(KnownHeaders.IfRange), ""), + (new HeaderDescriptor(KnownHeaders.Origin), ""), + (new HeaderDescriptor("purpose"), "prefetch"), + (new HeaderDescriptor(KnownHeaders.Server), ""), + (new HeaderDescriptor("timing-allow-origin"), "*"), + (new HeaderDescriptor(KnownHeaders.UpgradeInsecureRequests), "1"), + (new HeaderDescriptor(KnownHeaders.UserAgent), ""), + (new HeaderDescriptor("x-forwarded-for"), ""), + (new HeaderDescriptor(KnownHeaders.XFrameOptions), "deny"), + (new HeaderDescriptor(KnownHeaders.XFrameOptions), "sameorigin") + }; + } +} diff --git a/src/libraries/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj b/src/libraries/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj index 176edd8..fda67ec 100644 --- a/src/libraries/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj +++ b/src/libraries/System.Net.Http/tests/UnitTests/System.Net.Http.Unit.Tests.csproj @@ -160,6 +160,8 @@ Link="ProductionCode\System\Net\Http\Headers\ProductInfoHeaderParser.cs" /> +