diff --git a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/H2StaticTable.Http2.cs b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/H2StaticTable.Http2.cs new file mode 100644 index 00000000000000..71ef9019bc0258 --- /dev/null +++ b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/H2StaticTable.Http2.cs @@ -0,0 +1,100 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Text; + +namespace System.Net.Http.HPack +{ + internal static partial class H2StaticTable + { + public static int Count => s_staticDecoderTable.Length; + + public static ref readonly HeaderField Get(int index) => ref s_staticDecoderTable[index]; + + public static int GetStatusIndex(int status) => + status switch + { + 200 => 8, + 204 => 9, + 206 => 10, + 304 => 11, + 400 => 12, + 404 => 13, + 500 => 14, + _ => throw new ArgumentOutOfRangeException() + }; + + private static readonly HeaderField[] s_staticDecoderTable = new HeaderField[] + { + CreateHeaderField(":authority", ""), + CreateHeaderField(":method", "GET"), + CreateHeaderField(":method", "POST"), + CreateHeaderField(":path", "/"), + CreateHeaderField(":path", "/index.html"), + CreateHeaderField(":scheme", "http"), + CreateHeaderField(":scheme", "https"), + CreateHeaderField(":status", "200"), + CreateHeaderField(":status", "204"), + CreateHeaderField(":status", "206"), + CreateHeaderField(":status", "304"), + CreateHeaderField(":status", "400"), + CreateHeaderField(":status", "404"), + CreateHeaderField(":status", "500"), + CreateHeaderField("accept-charset", ""), + CreateHeaderField("accept-encoding", "gzip, deflate"), + CreateHeaderField("accept-language", ""), + CreateHeaderField("accept-ranges", ""), + CreateHeaderField("accept", ""), + CreateHeaderField("access-control-allow-origin", ""), + CreateHeaderField("age", ""), + CreateHeaderField("allow", ""), + CreateHeaderField("authorization", ""), + CreateHeaderField("cache-control", ""), + CreateHeaderField("content-disposition", ""), + CreateHeaderField("content-encoding", ""), + CreateHeaderField("content-language", ""), + CreateHeaderField("content-length", ""), + CreateHeaderField("content-location", ""), + CreateHeaderField("content-range", ""), + CreateHeaderField("content-type", ""), + CreateHeaderField("cookie", ""), + CreateHeaderField("date", ""), + CreateHeaderField("etag", ""), + CreateHeaderField("expect", ""), + CreateHeaderField("expires", ""), + CreateHeaderField("from", ""), + CreateHeaderField("host", ""), + CreateHeaderField("if-match", ""), + CreateHeaderField("if-modified-since", ""), + CreateHeaderField("if-none-match", ""), + CreateHeaderField("if-range", ""), + CreateHeaderField("if-unmodified-since", ""), + CreateHeaderField("last-modified", ""), + CreateHeaderField("link", ""), + CreateHeaderField("location", ""), + CreateHeaderField("max-forwards", ""), + CreateHeaderField("proxy-authenticate", ""), + CreateHeaderField("proxy-authorization", ""), + CreateHeaderField("range", ""), + CreateHeaderField("referer", ""), + CreateHeaderField("refresh", ""), + CreateHeaderField("retry-after", ""), + CreateHeaderField("server", ""), + CreateHeaderField("set-cookie", ""), + CreateHeaderField("strict-transport-security", ""), + CreateHeaderField("transfer-encoding", ""), + CreateHeaderField("user-agent", ""), + CreateHeaderField("vary", ""), + CreateHeaderField("via", ""), + CreateHeaderField("www-authenticate", "") + }; + + // TODO: The HeaderField constructor will allocate and copy again. We should avoid this. + // Tackle as part of header table allocation strategy in general (see note in HeaderField constructor). + + private static HeaderField CreateHeaderField(string name, string value) => + new HeaderField( + Encoding.ASCII.GetBytes(name), + value.Length != 0 ? Encoding.ASCII.GetBytes(value) : Array.Empty()); + } +} diff --git a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/H2StaticTable.cs b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/H2StaticTable.cs index 9324c502ee0e37..926d1fb71f5ed9 100644 --- a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/H2StaticTable.cs +++ b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/H2StaticTable.cs @@ -1,103 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Collections.Generic; -using System.Text; - namespace System.Net.Http.HPack { - internal static class H2StaticTable + internal static partial class H2StaticTable { - public static int Count => s_staticDecoderTable.Length; - - public static ref readonly HeaderField Get(int index) => ref s_staticDecoderTable[index]; - - public static int GetStatusIndex(int status) => - status switch - { - 200 => 8, - 204 => 9, - 206 => 10, - 304 => 11, - 400 => 12, - 404 => 13, - 500 => 14, - _ => throw new ArgumentOutOfRangeException() - }; - - private static readonly HeaderField[] s_staticDecoderTable = new HeaderField[] - { - CreateHeaderField(":authority", ""), - CreateHeaderField(":method", "GET"), - CreateHeaderField(":method", "POST"), - CreateHeaderField(":path", "/"), - CreateHeaderField(":path", "/index.html"), - CreateHeaderField(":scheme", "http"), - CreateHeaderField(":scheme", "https"), - CreateHeaderField(":status", "200"), - CreateHeaderField(":status", "204"), - CreateHeaderField(":status", "206"), - CreateHeaderField(":status", "304"), - CreateHeaderField(":status", "400"), - CreateHeaderField(":status", "404"), - CreateHeaderField(":status", "500"), - CreateHeaderField("accept-charset", ""), - CreateHeaderField("accept-encoding", "gzip, deflate"), - CreateHeaderField("accept-language", ""), - CreateHeaderField("accept-ranges", ""), - CreateHeaderField("accept", ""), - CreateHeaderField("access-control-allow-origin", ""), - CreateHeaderField("age", ""), - CreateHeaderField("allow", ""), - CreateHeaderField("authorization", ""), - CreateHeaderField("cache-control", ""), - CreateHeaderField("content-disposition", ""), - CreateHeaderField("content-encoding", ""), - CreateHeaderField("content-language", ""), - CreateHeaderField("content-length", ""), - CreateHeaderField("content-location", ""), - CreateHeaderField("content-range", ""), - CreateHeaderField("content-type", ""), - CreateHeaderField("cookie", ""), - CreateHeaderField("date", ""), - CreateHeaderField("etag", ""), - CreateHeaderField("expect", ""), - CreateHeaderField("expires", ""), - CreateHeaderField("from", ""), - CreateHeaderField("host", ""), - CreateHeaderField("if-match", ""), - CreateHeaderField("if-modified-since", ""), - CreateHeaderField("if-none-match", ""), - CreateHeaderField("if-range", ""), - CreateHeaderField("if-unmodified-since", ""), - CreateHeaderField("last-modified", ""), - CreateHeaderField("link", ""), - CreateHeaderField("location", ""), - CreateHeaderField("max-forwards", ""), - CreateHeaderField("proxy-authenticate", ""), - CreateHeaderField("proxy-authorization", ""), - CreateHeaderField("range", ""), - CreateHeaderField("referer", ""), - CreateHeaderField("refresh", ""), - CreateHeaderField("retry-after", ""), - CreateHeaderField("server", ""), - CreateHeaderField("set-cookie", ""), - CreateHeaderField("strict-transport-security", ""), - CreateHeaderField("transfer-encoding", ""), - CreateHeaderField("user-agent", ""), - CreateHeaderField("vary", ""), - CreateHeaderField("via", ""), - CreateHeaderField("www-authenticate", "") - }; - - // TODO: The HeaderField constructor will allocate and copy again. We should avoid this. - // Tackle as part of header table allocation strategy in general (see note in HeaderField constructor). - - private static HeaderField CreateHeaderField(string name, string value) => - new HeaderField( - Encoding.ASCII.GetBytes(name), - value.Length != 0 ? Encoding.ASCII.GetBytes(value) : Array.Empty()); - // Values for encoding. // Unused values are omitted. public const int Authority = 1; diff --git a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http3/QPack/H3StaticTable.Http3.cs b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http3/QPack/H3StaticTable.Http3.cs new file mode 100644 index 00000000000000..efa5f9b81d62e1 --- /dev/null +++ b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http3/QPack/H3StaticTable.Http3.cs @@ -0,0 +1,154 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using System.Text; + +namespace System.Net.Http.QPack +{ + internal static partial class H3StaticTable + { + private static readonly Dictionary s_statusIndex = new Dictionary + { + [103] = 24, + [200] = 25, + [304] = 26, + [404] = 27, + [503] = 28, + [100] = 63, + [204] = 64, + [206] = 65, + [302] = 66, + [400] = 67, + [403] = 68, + [421] = 69, + [425] = 70, + [500] = 71, + }; + + private static readonly Dictionary s_methodIndex = new Dictionary + { + // TODO connect is internal to system.net.http + [HttpMethod.Delete] = 16, + [HttpMethod.Get] = 17, + [HttpMethod.Head] = 18, + [HttpMethod.Options] = 19, + [HttpMethod.Post] = 20, + [HttpMethod.Put] = 21, + }; + + public static int Count => s_staticTable.Length; + + // TODO: just use Dictionary directly to avoid interface dispatch. + public static IReadOnlyDictionary StatusIndex => s_statusIndex; + public static IReadOnlyDictionary MethodIndex => s_methodIndex; + + public static HeaderField GetHeaderFieldAt(int index) => s_staticTable[index]; + + private static readonly HeaderField[] s_staticTable = new HeaderField[] + { + CreateHeaderField(":authority", ""), // 0 + CreateHeaderField(":path", "/"), // 1 + CreateHeaderField("age", "0"), // 2 + CreateHeaderField("content-disposition", ""), + CreateHeaderField("content-length", "0"), + CreateHeaderField("cookie", ""), + CreateHeaderField("date", ""), + CreateHeaderField("etag", ""), + CreateHeaderField("if-modified-since", ""), + CreateHeaderField("if-none-match", ""), + CreateHeaderField("last-modified", ""), // 10 + CreateHeaderField("link", ""), + CreateHeaderField("location", ""), + CreateHeaderField("referer", ""), + CreateHeaderField("set-cookie", ""), + CreateHeaderField(":method", "CONNECT"), + CreateHeaderField(":method", "DELETE"), + CreateHeaderField(":method", "GET"), + CreateHeaderField(":method", "HEAD"), + CreateHeaderField(":method", "OPTIONS"), + CreateHeaderField(":method", "POST"), // 20 + CreateHeaderField(":method", "PUT"), + CreateHeaderField(":scheme", "http"), + CreateHeaderField(":scheme", "https"), + CreateHeaderField(":status", "103"), + CreateHeaderField(":status", "200"), + CreateHeaderField(":status", "304"), + CreateHeaderField(":status", "404"), + CreateHeaderField(":status", "503"), + CreateHeaderField("accept", "*/*"), + CreateHeaderField("accept", "application/dns-message"), // 30 + CreateHeaderField("accept-encoding", "gzip, deflate, br"), + CreateHeaderField("accept-ranges", "bytes"), + CreateHeaderField("access-control-allow-headers", "cache-control"), + CreateHeaderField("access-control-allow-origin", "content-type"), + CreateHeaderField("access-control-allow-origin", "*"), + CreateHeaderField("cache-control", "max-age=0"), + CreateHeaderField("cache-control", "max-age=2592000"), + CreateHeaderField("cache-control", "max-age=604800"), + CreateHeaderField("cache-control", "no-cache"), + CreateHeaderField("cache-control", "no-store"), // 40 + CreateHeaderField("cache-control", "public, max-age=31536000"), + CreateHeaderField("content-encoding", "br"), + CreateHeaderField("content-encoding", "gzip"), + CreateHeaderField("content-type", "application/dns-message"), + CreateHeaderField("content-type", "application/javascript"), + CreateHeaderField("content-type", "application/json"), + CreateHeaderField("content-type", "application/x-www-form-urlencoded"), + CreateHeaderField("content-type", "image/gif"), + CreateHeaderField("content-type", "image/jpeg"), + CreateHeaderField("content-type", "image/png"), // 50 + CreateHeaderField("content-type", "text/css"), + CreateHeaderField("content-type", "text/html; charset=utf-8"), + CreateHeaderField("content-type", "text/plain"), + CreateHeaderField("content-type", "text/plain;charset=utf-8"), + CreateHeaderField("range", "bytes=0-"), + CreateHeaderField("strict-transport-security", "max-age=31536000"), + CreateHeaderField("strict-transport-security", "max-age=31536000;includesubdomains"), // TODO confirm spaces here don't matter? + CreateHeaderField("strict-transport-security", "max-age=31536000;includesubdomains; preload"), + CreateHeaderField("vary", "accept-encoding"), + CreateHeaderField("vary", "origin"), // 60 + CreateHeaderField("x-content-type-options", "nosniff"), + CreateHeaderField("x-xss-protection", "1; mode=block"), + CreateHeaderField(":status", "100"), + CreateHeaderField(":status", "204"), + CreateHeaderField(":status", "206"), + CreateHeaderField(":status", "302"), + CreateHeaderField(":status", "400"), + CreateHeaderField(":status", "403"), + CreateHeaderField(":status", "421"), + CreateHeaderField(":status", "425"), // 70 + CreateHeaderField(":status", "500"), + CreateHeaderField("accept-language", ""), + CreateHeaderField("access-control-allow-credentials", "FALSE"), + CreateHeaderField("access-control-allow-credentials", "TRUE"), + CreateHeaderField("access-control-allow-headers", "*"), + CreateHeaderField("access-control-allow-methods", "get"), + CreateHeaderField("access-control-allow-methods", "get, post, options"), + CreateHeaderField("access-control-allow-methods", "options"), + CreateHeaderField("access-control-expose-headers", "content-length"), + CreateHeaderField("access-control-request-headers", "content-type"), // 80 + CreateHeaderField("access-control-request-method", "get"), + CreateHeaderField("access-control-request-method", "post"), + CreateHeaderField("alt-svc", "clear"), + CreateHeaderField("authorization", ""), + CreateHeaderField("content-security-policy", "script-src 'none'; object-src 'none'; base-uri 'none'"), + CreateHeaderField("early-data", "1"), + CreateHeaderField("expect-ct", ""), + CreateHeaderField("forwarded", ""), + CreateHeaderField("if-range", ""), + CreateHeaderField("origin", ""), // 90 + CreateHeaderField("purpose", "prefetch"), + CreateHeaderField("server", ""), + CreateHeaderField("timing-allow-origin", "*"), + CreateHeaderField("upgrading-insecure-requests", "1"), + CreateHeaderField("user-agent", ""), + CreateHeaderField("x-forwarded-for", ""), + CreateHeaderField("x-frame-options", "deny"), + CreateHeaderField("x-frame-options", "sameorigin"), + }; + + private static HeaderField CreateHeaderField(string name, string value) + => new HeaderField(Encoding.ASCII.GetBytes(name), Encoding.ASCII.GetBytes(value)); + } +} diff --git a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http3/QPack/H3StaticTable.cs b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http3/QPack/H3StaticTable.cs index ae605971da6547..145da1216fb477 100644 --- a/src/libraries/Common/src/System/Net/Http/aspnetcore/Http3/QPack/H3StaticTable.cs +++ b/src/libraries/Common/src/System/Net/Http/aspnetcore/Http3/QPack/H3StaticTable.cs @@ -1,156 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Collections.Generic; -using System.Text; - namespace System.Net.Http.QPack { - internal static class H3StaticTable + internal static partial class H3StaticTable { - private static readonly Dictionary s_statusIndex = new Dictionary - { - [103] = 24, - [200] = 25, - [304] = 26, - [404] = 27, - [503] = 28, - [100] = 63, - [204] = 64, - [206] = 65, - [302] = 66, - [400] = 67, - [403] = 68, - [421] = 69, - [425] = 70, - [500] = 71, - }; - - private static readonly Dictionary s_methodIndex = new Dictionary - { - // TODO connect is internal to system.net.http - [HttpMethod.Delete] = 16, - [HttpMethod.Get] = 17, - [HttpMethod.Head] = 18, - [HttpMethod.Options] = 19, - [HttpMethod.Post] = 20, - [HttpMethod.Put] = 21, - }; - - public static int Count => s_staticTable.Length; - - // TODO: just use Dictionary directly to avoid interface dispatch. - public static IReadOnlyDictionary StatusIndex => s_statusIndex; - public static IReadOnlyDictionary MethodIndex => s_methodIndex; - - public static HeaderField GetHeaderFieldAt(int index) => s_staticTable[index]; - - private static readonly HeaderField[] s_staticTable = new HeaderField[] - { - CreateHeaderField(":authority", ""), // 0 - CreateHeaderField(":path", "/"), // 1 - CreateHeaderField("age", "0"), // 2 - CreateHeaderField("content-disposition", ""), - CreateHeaderField("content-length", "0"), - CreateHeaderField("cookie", ""), - CreateHeaderField("date", ""), - CreateHeaderField("etag", ""), - CreateHeaderField("if-modified-since", ""), - CreateHeaderField("if-none-match", ""), - CreateHeaderField("last-modified", ""), // 10 - CreateHeaderField("link", ""), - CreateHeaderField("location", ""), - CreateHeaderField("referer", ""), - CreateHeaderField("set-cookie", ""), - CreateHeaderField(":method", "CONNECT"), - CreateHeaderField(":method", "DELETE"), - CreateHeaderField(":method", "GET"), - CreateHeaderField(":method", "HEAD"), - CreateHeaderField(":method", "OPTIONS"), - CreateHeaderField(":method", "POST"), // 20 - CreateHeaderField(":method", "PUT"), - CreateHeaderField(":scheme", "http"), - CreateHeaderField(":scheme", "https"), - CreateHeaderField(":status", "103"), - CreateHeaderField(":status", "200"), - CreateHeaderField(":status", "304"), - CreateHeaderField(":status", "404"), - CreateHeaderField(":status", "503"), - CreateHeaderField("accept", "*/*"), - CreateHeaderField("accept", "application/dns-message"), // 30 - CreateHeaderField("accept-encoding", "gzip, deflate, br"), - CreateHeaderField("accept-ranges", "bytes"), - CreateHeaderField("access-control-allow-headers", "cache-control"), - CreateHeaderField("access-control-allow-origin", "content-type"), - CreateHeaderField("access-control-allow-origin", "*"), - CreateHeaderField("cache-control", "max-age=0"), - CreateHeaderField("cache-control", "max-age=2592000"), - CreateHeaderField("cache-control", "max-age=604800"), - CreateHeaderField("cache-control", "no-cache"), - CreateHeaderField("cache-control", "no-store"), // 40 - CreateHeaderField("cache-control", "public, max-age=31536000"), - CreateHeaderField("content-encoding", "br"), - CreateHeaderField("content-encoding", "gzip"), - CreateHeaderField("content-type", "application/dns-message"), - CreateHeaderField("content-type", "application/javascript"), - CreateHeaderField("content-type", "application/json"), - CreateHeaderField("content-type", "application/x-www-form-urlencoded"), - CreateHeaderField("content-type", "image/gif"), - CreateHeaderField("content-type", "image/jpeg"), - CreateHeaderField("content-type", "image/png"), // 50 - CreateHeaderField("content-type", "text/css"), - CreateHeaderField("content-type", "text/html; charset=utf-8"), - CreateHeaderField("content-type", "text/plain"), - CreateHeaderField("content-type", "text/plain;charset=utf-8"), - CreateHeaderField("range", "bytes=0-"), - CreateHeaderField("strict-transport-security", "max-age=31536000"), - CreateHeaderField("strict-transport-security", "max-age=31536000;includesubdomains"), // TODO confirm spaces here don't matter? - CreateHeaderField("strict-transport-security", "max-age=31536000;includesubdomains; preload"), - CreateHeaderField("vary", "accept-encoding"), - CreateHeaderField("vary", "origin"), // 60 - CreateHeaderField("x-content-type-options", "nosniff"), - CreateHeaderField("x-xss-protection", "1; mode=block"), - CreateHeaderField(":status", "100"), - CreateHeaderField(":status", "204"), - CreateHeaderField(":status", "206"), - CreateHeaderField(":status", "302"), - CreateHeaderField(":status", "400"), - CreateHeaderField(":status", "403"), - CreateHeaderField(":status", "421"), - CreateHeaderField(":status", "425"), // 70 - CreateHeaderField(":status", "500"), - CreateHeaderField("accept-language", ""), - CreateHeaderField("access-control-allow-credentials", "FALSE"), - CreateHeaderField("access-control-allow-credentials", "TRUE"), - CreateHeaderField("access-control-allow-headers", "*"), - CreateHeaderField("access-control-allow-methods", "get"), - CreateHeaderField("access-control-allow-methods", "get, post, options"), - CreateHeaderField("access-control-allow-methods", "options"), - CreateHeaderField("access-control-expose-headers", "content-length"), - CreateHeaderField("access-control-request-headers", "content-type"), // 80 - CreateHeaderField("access-control-request-method", "get"), - CreateHeaderField("access-control-request-method", "post"), - CreateHeaderField("alt-svc", "clear"), - CreateHeaderField("authorization", ""), - CreateHeaderField("content-security-policy", "script-src 'none'; object-src 'none'; base-uri 'none'"), - CreateHeaderField("early-data", "1"), - CreateHeaderField("expect-ct", ""), - CreateHeaderField("forwarded", ""), - CreateHeaderField("if-range", ""), - CreateHeaderField("origin", ""), // 90 - CreateHeaderField("purpose", "prefetch"), - CreateHeaderField("server", ""), - CreateHeaderField("timing-allow-origin", "*"), - CreateHeaderField("upgrading-insecure-requests", "1"), - CreateHeaderField("user-agent", ""), - CreateHeaderField("x-forwarded-for", ""), - CreateHeaderField("x-frame-options", "deny"), - CreateHeaderField("x-frame-options", "sameorigin"), - }; - - private static HeaderField CreateHeaderField(string name, string value) - => new HeaderField(Encoding.ASCII.GetBytes(name), Encoding.ASCII.GetBytes(value)); - public const int Authority = 0; public const int PathSlash = 1; public const int Age0 = 2; diff --git a/src/libraries/Common/tests/Common.Tests.csproj b/src/libraries/Common/tests/Common.Tests.csproj index 8a8a8b0f466ffd..ba76497cfc5568 100644 --- a/src/libraries/Common/tests/Common.Tests.csproj +++ b/src/libraries/Common/tests/Common.Tests.csproj @@ -62,6 +62,8 @@ Link="Common\System\Net\Http\aspnetcore\Http2\Hpack\IntegerEncoder.cs" /> + + + + + @@ -215,9 +221,7 @@ Link="Common\System\Text\ValueStringBuilder.cs" /> - - Common\System\Net\Http\aspnetcore\IHttpHeadersHandler.cs @@ -251,8 +255,8 @@ Common\System\Net\Http\aspnetcore\Http2\Hpack\IntegerEncoder.cs - - Common\System\Net\Http\aspnetcore\Http2\Hpack\H2StaticTable.cs + + Common\System\Net\Http\aspnetcore\Http2\Hpack\H2StaticTable.Http2.cs Common\System\Net\Http\aspnetcore\Http2\Hpack\StatusCodes.cs @@ -290,8 +294,8 @@ Common\System\Net\Http\aspnetcore\Http3\QPack\QPackEncodingException.cs - - Common\System\Net\Http\aspnetcore\Http3\QPack\H3StaticTable.cs + + Common\System\Net\Http\aspnetcore\Http3\QPack\H3StaticTable.Http3.cs diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/KnownHeader.Http2And3.cs b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/KnownHeader.Http2And3.cs new file mode 100644 index 00000000000000..1a2d6891e366ed --- /dev/null +++ b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/KnownHeader.Http2And3.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; +using System.Net.Http.HPack; + +namespace System.Net.Http.Headers +{ + internal sealed partial class KnownHeader + { + [MemberNotNull(nameof(Http2EncodedName))] + [MemberNotNull(nameof(Http3EncodedName))] + partial void Initialize(int? http2StaticTableIndex, int? http3StaticTableIndex) + { + Http2EncodedName = http2StaticTableIndex.HasValue ? + HPackEncoder.EncodeLiteralHeaderFieldWithoutIndexingToAllocatedArray(http2StaticTableIndex.GetValueOrDefault()) : + HPackEncoder.EncodeLiteralHeaderFieldWithoutIndexingNewNameToAllocatedArray(Name); + + Http3EncodedName = http3StaticTableIndex.HasValue ? + QPack.QPackEncoder.EncodeLiteralHeaderFieldWithStaticNameReferenceToArray(http3StaticTableIndex.GetValueOrDefault()) : + QPack.QPackEncoder.EncodeLiteralHeaderFieldWithoutNameReferenceToArray(Name); + } + + public byte[] Http2EncodedName { get; private set; } + public byte[] Http3EncodedName { get; private set; } + } +} diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/KnownHeader.cs b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/KnownHeader.cs index 29e33d15c3b6d8..0163db073a852e 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/Headers/KnownHeader.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/Headers/KnownHeader.cs @@ -2,12 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; -using System.Net.Http.HPack; using System.Text; namespace System.Net.Http.Headers { - internal sealed class KnownHeader + internal sealed partial class KnownHeader { public KnownHeader(string name, int? http2StaticTableIndex = null, int? http3StaticTableIndex = null) : this(name, HttpHeaderType.Custom, parser: null, knownValues: null, http2StaticTableIndex, http3StaticTableIndex) @@ -26,13 +25,7 @@ public KnownHeader(string name, HttpHeaderType headerType, HttpHeaderParser? par Parser = parser; KnownValues = knownValues; - Http2EncodedName = http2StaticTableIndex.HasValue ? - HPackEncoder.EncodeLiteralHeaderFieldWithoutIndexingToAllocatedArray(http2StaticTableIndex.GetValueOrDefault()) : - HPackEncoder.EncodeLiteralHeaderFieldWithoutIndexingNewNameToAllocatedArray(name); - - Http3EncodedName = http3StaticTableIndex.HasValue ? - QPack.QPackEncoder.EncodeLiteralHeaderFieldWithStaticNameReferenceToArray(http3StaticTableIndex.GetValueOrDefault()) : - QPack.QPackEncoder.EncodeLiteralHeaderFieldWithoutNameReferenceToArray(name); + Initialize(http2StaticTableIndex, http3StaticTableIndex); var asciiBytesWithColonSpace = new byte[name.Length + 2]; // + 2 for ':' and ' ' int asciiBytes = Encoding.ASCII.GetBytes(name, asciiBytesWithColonSpace); @@ -42,6 +35,8 @@ public KnownHeader(string name, HttpHeaderType headerType, HttpHeaderParser? par AsciiBytesWithColonSpace = asciiBytesWithColonSpace; } + partial void Initialize(int? http2StaticTableIndex, int? http3StaticTableIndex); + public string Name { get; } public HttpHeaderParser? Parser { get; } public HttpHeaderType HeaderType { get; } @@ -52,7 +47,5 @@ public KnownHeader(string name, HttpHeaderType headerType, HttpHeaderParser? par public string[]? KnownValues { get; } public byte[] AsciiBytesWithColonSpace { get; } public HeaderDescriptor Descriptor => new HeaderDescriptor(this); - public byte[] Http2EncodedName { get; } - public byte[] Http3EncodedName { get; } } } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpMethod.Http3.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpMethod.Http3.cs new file mode 100644 index 00000000000000..c8580d1394569a --- /dev/null +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpMethod.Http3.cs @@ -0,0 +1,29 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Net.Http.QPack; +using System.Threading; + +namespace System.Net.Http +{ + public partial class HttpMethod + { + private byte[]? _http3EncodedBytes; + + internal byte[] Http3EncodedBytes + { + get + { + byte[]? http3EncodedBytes = Volatile.Read(ref _http3EncodedBytes); + if (http3EncodedBytes is null) + { + Volatile.Write(ref _http3EncodedBytes, http3EncodedBytes = _http3Index is int index && index >= 0 ? + QPackEncoder.EncodeStaticIndexedHeaderFieldToArray(index) : + QPackEncoder.EncodeLiteralHeaderFieldWithStaticNameReferenceToArray(H3StaticTable.MethodGet, _method)); + } + + return http3EncodedBytes; + } + } + } +} diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/HttpMethod.cs b/src/libraries/System.Net.Http/src/System/Net/Http/HttpMethod.cs index 917e769b11f054..6fddc9dcb964fc 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/HttpMethod.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/HttpMethod.cs @@ -1,20 +1,17 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Net.Http.QPack; -using System.Threading; namespace System.Net.Http { - public class HttpMethod : IEquatable + public partial class HttpMethod : IEquatable { private readonly string _method; private readonly int? _http3Index; - private byte[]? _http3EncodedBytes; private int _hashcode; private static readonly HttpMethod s_getMethod = new HttpMethod("GET", http3StaticTableIndex: H3StaticTable.MethodGet); @@ -80,20 +77,6 @@ public string Method get { return _method; } } - internal byte[] Http3EncodedBytes - { - get { - byte[]? http3EncodedBytes = Volatile.Read(ref _http3EncodedBytes); - if (http3EncodedBytes is null) { - Volatile.Write (ref _http3EncodedBytes, http3EncodedBytes = _http3Index is int index && index >= 0 ? - QPackEncoder.EncodeStaticIndexedHeaderFieldToArray(index) : - QPackEncoder.EncodeLiteralHeaderFieldWithStaticNameReferenceToArray(H3StaticTable.MethodGet, _method)); - } - - return http3EncodedBytes; - } - } - public HttpMethod(string method) { if (string.IsNullOrEmpty(method)) 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 9476e3c5637021..da0a1809d0bcca 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 @@ -148,6 +148,8 @@ Link="ProductionCode\System\Net\Http\Headers\KnownHeaders.cs" /> + + +