Skip to content

Commit 06247f6

Browse files
committed
Use interned headernames for known headers not in the preallocated block
1 parent 69a88d9 commit 06247f6

File tree

8 files changed

+261
-119
lines changed

8 files changed

+261
-119
lines changed

src/Http/Headers/src/HeaderNames.cs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public static class HeaderNames
6565
public static readonly string Authorization = "Authorization";
6666

6767
/// <summary>Gets the <c>baggage</c> HTTP header name.</summary>
68-
public static readonly string Baggage = "baggage";
68+
public static readonly string Baggage = "Baggage";
6969

7070
/// <summary>Gets the <c>Cache-Control</c> HTTP header name.</summary>
7171
public static readonly string CacheControl = "Cache-Control";
@@ -166,6 +166,9 @@ public static class HeaderNames
166166
/// <summary>Gets the <c>Last-Modified</c> HTTP header name.</summary>
167167
public static readonly string LastModified = "Last-Modified";
168168

169+
/// <summary>Gets the <c>Link</c> HTTP header name.</summary>
170+
public static readonly string Link = "Link";
171+
169172
/// <summary>Gets the <c>Location</c> HTTP header name.</summary>
170173
public static readonly string Location = "Location";
171174

@@ -245,10 +248,10 @@ public static class HeaderNames
245248
public static readonly string Translate = "Translate";
246249

247250
/// <summary>Gets the <c>traceparent</c> HTTP header name.</summary>
248-
public static readonly string TraceParent = "traceparent";
251+
public static readonly string TraceParent = "TraceParent";
249252

250253
/// <summary>Gets the <c>tracestate</c> HTTP header name.</summary>
251-
public static readonly string TraceState = "tracestate";
254+
public static readonly string TraceState = "TraceState";
252255

253256
/// <summary>Gets the <c>Upgrade</c> HTTP header name.</summary>
254257
public static readonly string Upgrade = "Upgrade";
@@ -274,10 +277,16 @@ public static class HeaderNames
274277
/// <summary>Gets the <c>WWW-Authenticate</c> HTTP header name.</summary>
275278
public static readonly string WWWAuthenticate = "WWW-Authenticate";
276279

280+
/// <summary>Gets the <c>X-Content-Type-Options</c> HTTP header name.</summary>
281+
public static readonly string XContentTypeOptions = "X-Content-Type-Options";
282+
277283
/// <summary>Gets the <c>X-Frame-Options</c> HTTP header name.</summary>
278284
public static readonly string XFrameOptions = "X-Frame-Options";
279285

280286
/// <summary>Gets the <c>X-Requested-With</c> HTTP header name.</summary>
281287
public static readonly string XRequestedWith = "X-Requested-With";
288+
289+
/// <summary>Gets the <c>X-XSS-Protection</c> HTTP header name.</summary>
290+
public static readonly string XXssProtection = "X-XSS-Protection";
282291
}
283292
}

src/Http/Headers/src/PublicAPI.Unshipped.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,7 @@
33
Microsoft.Net.Http.Headers.MediaTypeHeaderValue.MatchesMediaType(Microsoft.Extensions.Primitives.StringSegment otherMediaType) -> bool
44
Microsoft.Net.Http.Headers.RangeConditionHeaderValue.RangeConditionHeaderValue(Microsoft.Net.Http.Headers.EntityTagHeaderValue! entityTag) -> void
55
static readonly Microsoft.Net.Http.Headers.HeaderNames.Baggage -> string!
6+
static readonly Microsoft.Net.Http.Headers.HeaderNames.Link -> string!
67
static readonly Microsoft.Net.Http.Headers.HeaderNames.ProxyConnection -> string!
8+
static readonly Microsoft.Net.Http.Headers.HeaderNames.XContentTypeOptions -> string!
9+
static readonly Microsoft.Net.Http.Headers.HeaderNames.XXssProtection -> string!

src/Servers/Kestrel/Core/src/Internal/Http/HttpHeaders.Generated.cs

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,106 @@ internal enum KnownHeaderType
9999
WWWAuthenticate,
100100
}
101101

102+
internal partial class HttpHeaders
103+
{
104+
protected readonly static HashSet<string> s_internedHeaderNames = new HashSet<string>(93, StringComparer.OrdinalIgnoreCase)
105+
{
106+
HeaderNames.Accept,
107+
HeaderNames.AcceptCharset,
108+
HeaderNames.AcceptEncoding,
109+
HeaderNames.AcceptLanguage,
110+
HeaderNames.AcceptRanges,
111+
HeaderNames.AccessControlAllowCredentials,
112+
HeaderNames.AccessControlAllowHeaders,
113+
HeaderNames.AccessControlAllowMethods,
114+
HeaderNames.AccessControlAllowOrigin,
115+
HeaderNames.AccessControlExposeHeaders,
116+
HeaderNames.AccessControlMaxAge,
117+
HeaderNames.AccessControlRequestHeaders,
118+
HeaderNames.AccessControlRequestMethod,
119+
HeaderNames.Age,
120+
HeaderNames.Allow,
121+
HeaderNames.AltSvc,
122+
HeaderNames.Authority,
123+
HeaderNames.Authorization,
124+
HeaderNames.Baggage,
125+
HeaderNames.CacheControl,
126+
HeaderNames.Connection,
127+
HeaderNames.ContentDisposition,
128+
HeaderNames.ContentEncoding,
129+
HeaderNames.ContentLanguage,
130+
HeaderNames.ContentLength,
131+
HeaderNames.ContentLocation,
132+
HeaderNames.ContentMD5,
133+
HeaderNames.ContentRange,
134+
HeaderNames.ContentSecurityPolicy,
135+
HeaderNames.ContentSecurityPolicyReportOnly,
136+
HeaderNames.ContentType,
137+
HeaderNames.CorrelationContext,
138+
HeaderNames.Cookie,
139+
HeaderNames.Date,
140+
HeaderNames.DNT,
141+
HeaderNames.ETag,
142+
HeaderNames.Expires,
143+
HeaderNames.Expect,
144+
HeaderNames.From,
145+
HeaderNames.GrpcAcceptEncoding,
146+
HeaderNames.GrpcEncoding,
147+
HeaderNames.GrpcMessage,
148+
HeaderNames.GrpcStatus,
149+
HeaderNames.GrpcTimeout,
150+
HeaderNames.Host,
151+
HeaderNames.KeepAlive,
152+
HeaderNames.IfMatch,
153+
HeaderNames.IfModifiedSince,
154+
HeaderNames.IfNoneMatch,
155+
HeaderNames.IfRange,
156+
HeaderNames.IfUnmodifiedSince,
157+
HeaderNames.LastModified,
158+
HeaderNames.Link,
159+
HeaderNames.Location,
160+
HeaderNames.MaxForwards,
161+
HeaderNames.Method,
162+
HeaderNames.Origin,
163+
HeaderNames.Path,
164+
HeaderNames.Pragma,
165+
HeaderNames.ProxyAuthenticate,
166+
HeaderNames.ProxyAuthorization,
167+
HeaderNames.ProxyConnection,
168+
HeaderNames.Range,
169+
HeaderNames.Referer,
170+
HeaderNames.RetryAfter,
171+
HeaderNames.RequestId,
172+
HeaderNames.Scheme,
173+
HeaderNames.SecWebSocketAccept,
174+
HeaderNames.SecWebSocketKey,
175+
HeaderNames.SecWebSocketProtocol,
176+
HeaderNames.SecWebSocketVersion,
177+
HeaderNames.Server,
178+
HeaderNames.SetCookie,
179+
HeaderNames.Status,
180+
HeaderNames.StrictTransportSecurity,
181+
HeaderNames.TE,
182+
HeaderNames.Trailer,
183+
HeaderNames.TransferEncoding,
184+
HeaderNames.Translate,
185+
HeaderNames.TraceParent,
186+
HeaderNames.TraceState,
187+
HeaderNames.Upgrade,
188+
HeaderNames.UpgradeInsecureRequests,
189+
HeaderNames.UserAgent,
190+
HeaderNames.Vary,
191+
HeaderNames.Via,
192+
HeaderNames.Warning,
193+
HeaderNames.WebSocketSubProtocols,
194+
HeaderNames.WWWAuthenticate,
195+
HeaderNames.XContentTypeOptions,
196+
HeaderNames.XFrameOptions,
197+
HeaderNames.XRequestedWith,
198+
HeaderNames.XXssProtection,
199+
};
200+
}
201+
102202
internal partial class HttpRequestHeaders
103203
{
104204
private HeaderReferences _headers;

src/Servers/Kestrel/Core/src/Internal/Http/HttpHeaders.cs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@
1515

1616
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
1717
{
18-
internal abstract class HttpHeaders : IHeaderDictionary
18+
internal abstract partial class HttpHeaders : IHeaderDictionary
1919
{
2020
protected long _bits = 0;
2121
protected long? _contentLength;
2222
protected bool _isReadOnly;
2323
protected Dictionary<string, StringValues>? MaybeUnknown;
24-
protected Dictionary<string, StringValues> Unknown => MaybeUnknown ?? (MaybeUnknown = new Dictionary<string, StringValues>(StringComparer.OrdinalIgnoreCase));
24+
protected Dictionary<string, StringValues> Unknown => MaybeUnknown ??= new Dictionary<string, StringValues>(StringComparer.OrdinalIgnoreCase);
2525

2626
public long? ContentLength
2727
{
@@ -126,6 +126,18 @@ public void Reset()
126126
ClearFast();
127127
}
128128

129+
protected static string GetInternedHeaderName(string name)
130+
{
131+
// Some headers can be very long lived; for example those on a WebSocket connection
132+
// so we exchange these for the preallocated strings predefined in HeaderNames
133+
if (s_internedHeaderNames.TryGetValue(name, out var internedName))
134+
{
135+
return internedName;
136+
}
137+
138+
return name;
139+
}
140+
129141
[MethodImpl(MethodImplOptions.NoInlining)]
130142
protected static StringValues AppendValue(StringValues existing, string append)
131143
{

src/Servers/Kestrel/Core/src/Internal/Http/HttpRequestHeaders.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,20 +121,21 @@ private void AppendContentLengthCustomEncoding(ReadOnlySpan<byte> value, Encodin
121121
[MethodImpl(MethodImplOptions.NoInlining)]
122122
private void SetValueUnknown(string key, StringValues value)
123123
{
124-
Unknown[key] = value;
124+
Unknown[GetInternedHeaderName(key)] = value;
125125
}
126126

127127
[MethodImpl(MethodImplOptions.NoInlining)]
128128
private bool AddValueUnknown(string key, StringValues value)
129129
{
130-
Unknown.Add(key, value);
130+
Unknown.Add(GetInternedHeaderName(key), value);
131131
// Return true, above will throw and exit for false
132132
return true;
133133
}
134134

135135
[MethodImpl(MethodImplOptions.NoInlining)]
136136
private unsafe void AppendUnknownHeaders(string name, string valueString)
137137
{
138+
name = GetInternedHeaderName(name);
138139
Unknown.TryGetValue(name, out var existing);
139140
Unknown[name] = AppendValue(existing, valueString);
140141
}

src/Servers/Kestrel/Core/src/Internal/Http/HttpResponseHeaders.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,14 @@ private static void ThrowInvalidContentLengthException(string value)
7676
private void SetValueUnknown(string key, StringValues value)
7777
{
7878
ValidateHeaderNameCharacters(key);
79-
Unknown[key] = value;
79+
Unknown[GetInternedHeaderName(key)] = value;
8080
}
8181

8282
[MethodImpl(MethodImplOptions.NoInlining)]
8383
private bool AddValueUnknown(string key, StringValues value)
8484
{
8585
ValidateHeaderNameCharacters(key);
86-
Unknown.Add(key, value);
86+
Unknown.Add(GetInternedHeaderName(key), value);
8787
// Return true, above will throw and exit for false
8888
return true;
8989
}

src/Servers/Kestrel/Core/src/Internal/Http/HttpResponseTrailers.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,14 @@ protected override IEnumerator<KeyValuePair<string, StringValues>> GetEnumerator
2525
private void SetValueUnknown(string key, StringValues value)
2626
{
2727
ValidateHeaderNameCharacters(key);
28-
Unknown[key] = value;
28+
Unknown[GetInternedHeaderName(key)] = value;
2929
}
3030

3131
[MethodImpl(MethodImplOptions.NoInlining)]
3232
private bool AddValueUnknown(string key, StringValues value)
3333
{
3434
ValidateHeaderNameCharacters(key);
35-
Unknown.Add(key, value);
35+
Unknown.Add(GetInternedHeaderName(key), value);
3636
// Return true, above will throw and exit for false
3737
return true;
3838
}

0 commit comments

Comments
 (0)