Skip to content

Commit 3e8943e

Browse files
committed
Switch Connection header values for interned values
1 parent 06247f6 commit 3e8943e

8 files changed

+68
-23
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ public static MessageBody For(
128128

129129
if (headers.HasConnection)
130130
{
131-
var connectionOptions = HttpHeaders.ParseConnection(headers.HeaderConnection);
131+
var connectionOptions = HttpHeaders.ParseConnection(headers);
132132

133133
upgrade = (connectionOptions & ConnectionOptions.Upgrade) != 0;
134134
keepAlive = keepAlive || (connectionOptions & ConnectionOptions.KeepAlive) != 0;

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ public StringValues HeaderCacheControl
225225
_headers._CacheControl = value;
226226
}
227227
}
228-
public StringValues HeaderConnection
228+
public override StringValues HeaderConnection
229229
{
230230
get
231231
{
@@ -8229,7 +8229,7 @@ public StringValues HeaderCacheControl
82298229
_headers._CacheControl = value;
82308230
}
82318231
}
8232-
public StringValues HeaderConnection
8232+
public override StringValues HeaderConnection
82338233
{
82348234
get
82358235
{

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

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
using Microsoft.AspNetCore.Http;
1313
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
1414
using Microsoft.Extensions.Primitives;
15+
using Microsoft.Net.Http.Headers;
1516

1617
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
1718
{
@@ -40,6 +41,8 @@ public long? ContentLength
4041
}
4142
}
4243

44+
public abstract StringValues HeaderConnection { get; set; }
45+
4346
StringValues IHeaderDictionary.this[string key]
4447
{
4548
get
@@ -288,7 +291,12 @@ public static void ValidateHeaderNameCharacters(string headerCharacters)
288291
}
289292
}
290293

291-
public static ConnectionOptions ParseConnection(StringValues connection)
294+
private readonly static string KeepAlive = "keep-alive";
295+
private readonly static StringValues ConnectionValueKeepAlive = KeepAlive;
296+
private readonly static StringValues ConnectionValueClose = "close";
297+
private readonly static StringValues ConnectionValueUpgrade = HeaderNames.Upgrade;
298+
299+
public static ConnectionOptions ParseConnection(HttpHeaders headers)
292300
{
293301
// Keep-alive
294302
const ulong lowerCaseKeep = 0x0000_0020_0020_0020; // Don't lowercase hyphen
@@ -301,9 +309,27 @@ public static ConnectionOptions ParseConnection(StringValues connection)
301309
// Close
302310
const ulong closeEnd = 0x0065_0073_006f_006c; // 4 chars "lose"
303311

312+
var connection = headers.HeaderConnection;
313+
var connectionCount = connection.Count;
314+
if (connectionCount == 0)
315+
{
316+
return ConnectionOptions.None;
317+
}
318+
304319
var connectionOptions = ConnectionOptions.None;
305320

306-
var connectionCount = connection.Count;
321+
if (connectionCount == 1)
322+
{
323+
// "keep-alive" is the only value that will be repeated over
324+
// many requests on the same connection; on the first request
325+
// we will have switched it for the readonly static value;
326+
// so we can ptentially short-circuit parsing and use ReferenceEquals.
327+
if (ReferenceEquals(connection.ToString(), KeepAlive))
328+
{
329+
return ConnectionOptions.KeepAlive;
330+
}
331+
}
332+
307333
for (var i = 0; i < connectionCount; i++)
308334
{
309335
var value = connection[i].AsSpan();
@@ -432,6 +458,21 @@ public static ConnectionOptions ParseConnection(StringValues connection)
432458
}
433459
}
434460

461+
// If Connection is a single value, switch it for the interned value
462+
// in case the connection is long lived
463+
if (connectionOptions == ConnectionOptions.Upgrade)
464+
{
465+
headers.HeaderConnection = ConnectionValueUpgrade;
466+
}
467+
else if (connectionOptions == ConnectionOptions.KeepAlive)
468+
{
469+
headers.HeaderConnection = ConnectionValueKeepAlive;
470+
}
471+
else if (connectionOptions == ConnectionOptions.Close)
472+
{
473+
headers.HeaderConnection = ConnectionValueClose;
474+
}
475+
435476
return connectionOptions;
436477
}
437478

src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.FeatureCollection.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
5-
using System.Collections.Generic;
6-
using System.Diagnostics;
75
using System.IO;
86
using System.IO.Pipelines;
97
using System.Net;
@@ -309,7 +307,7 @@ async Task<Stream> IHttpUpgradeFeature.UpgradeAsync()
309307

310308
StatusCode = StatusCodes.Status101SwitchingProtocols;
311309
ReasonPhrase = "Switching Protocols";
312-
ResponseHeaders[HeaderNames.Connection] = "Upgrade";
310+
ResponseHeaders[HeaderNames.Connection] = HeaderNames.Upgrade;
313311

314312
await FlushAsync();
315313

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1122,7 +1122,7 @@ private HttpResponseHeaders CreateResponseHeaders(bool appCompleted)
11221122

11231123
if (_keepAlive &&
11241124
hasConnection &&
1125-
(HttpHeaders.ParseConnection(responseHeaders.HeaderConnection) & ConnectionOptions.KeepAlive) == 0)
1125+
(HttpHeaders.ParseConnection(responseHeaders) & ConnectionOptions.KeepAlive) == 0)
11261126
{
11271127
_keepAlive = false;
11281128
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ private bool AddValueUnknown(string key, StringValues value)
3737
return true;
3838
}
3939

40+
public override StringValues HeaderConnection { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }
41+
4042
public partial struct Enumerator : IEnumerator<KeyValuePair<string, StringValues>>
4143
{
4244
private readonly HttpResponseTrailers _collection;

src/Servers/Kestrel/Core/test/HttpHeadersTests.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,9 @@ public class HttpHeadersTests
135135
public void TestParseConnection(string connection, int intExpectedConnectionOptions)
136136
{
137137
var expectedConnectionOptions = (ConnectionOptions)intExpectedConnectionOptions;
138-
var connectionOptions = HttpHeaders.ParseConnection(connection);
138+
var requestHeaders = new HttpRequestHeaders();
139+
requestHeaders.HeaderConnection = connection;
140+
var connectionOptions = HttpHeaders.ParseConnection(requestHeaders);
139141
Assert.Equal(expectedConnectionOptions, connectionOptions);
140142
}
141143

@@ -159,7 +161,9 @@ public void TestParseConnectionMultipleValues(string value1, string value2, int
159161
{
160162
var expectedConnectionOptions = (ConnectionOptions)intExpectedConnectionOptions;
161163
var connection = new StringValues(new[] { value1, value2 });
162-
var connectionOptions = HttpHeaders.ParseConnection(connection);
164+
var requestHeaders = new HttpRequestHeaders();
165+
requestHeaders.HeaderConnection = connection;
166+
var connectionOptions = HttpHeaders.ParseConnection(requestHeaders);
163167
Assert.Equal(expectedConnectionOptions, connectionOptions);
164168
}
165169

src/Servers/Kestrel/shared/KnownHeaders.cs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ static string AppendValue(bool returnTrue = false) =>
283283
static string AppendHPackSwitchSection(HPackGroup group)
284284
{
285285
var header = group.Header;
286-
if (header.Identifier == "ContentLength")
286+
if (header.Name == HeaderNames.ContentLength)
287287
{
288288
return $@"if (ReferenceEquals(EncodingSelector, KestrelServerOptions.DefaultRequestHeaderEncodingSelector))
289289
{{
@@ -323,7 +323,7 @@ static string AppendSwitchSection(int length, IList<KnownHeader> values)
323323

324324
string GenerateIfBody(KnownHeader header, string extraIndent = "")
325325
{
326-
if (header.Identifier == "ContentLength")
326+
if (header.Name == HeaderNames.ContentLength)
327327
{
328328
return $@"
329329
{extraIndent}if (ReferenceEquals(EncodingSelector, KestrelServerOptions.DefaultRequestHeaderEncodingSelector))
@@ -755,8 +755,8 @@ internal partial class {loop.ClassName}
755755
{Each(loop.Headers.Where(header => header.FastCount), header => $@"
756756
public int {header.Identifier}Count => _headers._{header.Identifier}.Count;")}
757757
{Each(loop.Headers, header => $@"
758-
public StringValues Header{header.Identifier}
759-
{{{(header.Identifier == "ContentLength" ? $@"
758+
public {(header.Name == HeaderNames.Connection ? "override " : "")}StringValues Header{header.Identifier}
759+
{{{(header.Name == HeaderNames.ContentLength ? $@"
760760
get
761761
{{
762762
StringValues value = default;
@@ -806,7 +806,7 @@ protected override bool TryGetValueFast(string key, out StringValues value)
806806
case {byLength.Key}:
807807
{{{Each(byLength.OrderBy(h => !h.PrimaryHeader), header => $@"
808808
if (ReferenceEquals(HeaderNames.{header.Identifier}, key))
809-
{{{(header.Identifier == "ContentLength" ? @"
809+
{{{(header.Name == HeaderNames.ContentLength ? @"
810810
if (_contentLength.HasValue)
811811
{
812812
value = HeaderUtilities.FormatNonNegativeInt64(_contentLength.Value);
@@ -822,7 +822,7 @@ protected override bool TryGetValueFast(string key, out StringValues value)
822822
}}")}
823823
{Each(byLength.OrderBy(h => !h.PrimaryHeader), header => $@"
824824
if (HeaderNames.{header.Identifier}.Equals(key, StringComparison.OrdinalIgnoreCase))
825-
{{{(header.Identifier == "ContentLength" ? @"
825+
{{{(header.Name == HeaderNames.ContentLength ? @"
826826
if (_contentLength.HasValue)
827827
{
828828
value = HeaderUtilities.FormatNonNegativeInt64(_contentLength.Value);
@@ -851,7 +851,7 @@ protected override void SetValueFast(string key, StringValues value)
851851
case {byLength.Key}:
852852
{{{Each(byLength.OrderBy(h => !h.PrimaryHeader), header => $@"
853853
if (ReferenceEquals(HeaderNames.{header.Identifier}, key))
854-
{{{(header.Identifier == "ContentLength" ? $@"
854+
{{{(header.Name == HeaderNames.ContentLength ? $@"
855855
_contentLength = ParseContentLength(value.ToString());" : $@"
856856
{header.SetBit()};
857857
_headers._{header.Identifier} = value;{(header.EnhancedSetter == false ? "" : $@"
@@ -860,7 +860,7 @@ protected override void SetValueFast(string key, StringValues value)
860860
}}")}
861861
{Each(byLength.OrderBy(h => !h.PrimaryHeader), header => $@"
862862
if (HeaderNames.{header.Identifier}.Equals(key, StringComparison.OrdinalIgnoreCase))
863-
{{{(header.Identifier == "ContentLength" ? $@"
863+
{{{(header.Name == HeaderNames.ContentLength ? $@"
864864
_contentLength = ParseContentLength(value.ToString());" : $@"
865865
{header.SetBit()};
866866
_headers._{header.Identifier} = value;{(header.EnhancedSetter == false ? "" : $@"
@@ -882,7 +882,7 @@ protected override bool AddValueFast(string key, StringValues value)
882882
case {byLength.Key}:
883883
{{{Each(byLength.OrderBy(h => !h.PrimaryHeader), header => $@"
884884
if (ReferenceEquals(HeaderNames.{header.Identifier}, key))
885-
{{{(header.Identifier == "ContentLength" ? $@"
885+
{{{(header.Name == HeaderNames.ContentLength ? $@"
886886
if (!_contentLength.HasValue)
887887
{{
888888
_contentLength = ParseContentLength(value);
@@ -900,7 +900,7 @@ protected override bool AddValueFast(string key, StringValues value)
900900
}}")}
901901
{Each(byLength.OrderBy(h => !h.PrimaryHeader), header => $@"
902902
if (HeaderNames.{header.Identifier}.Equals(key, StringComparison.OrdinalIgnoreCase))
903-
{{{(header.Identifier == "ContentLength" ? $@"
903+
{{{(header.Name == HeaderNames.ContentLength ? $@"
904904
if (!_contentLength.HasValue)
905905
{{
906906
_contentLength = ParseContentLength(value);
@@ -930,7 +930,7 @@ protected override bool RemoveFast(string key)
930930
case {byLength.Key}:
931931
{{{Each(byLength.OrderBy(h => !h.PrimaryHeader), header => $@"
932932
if (ReferenceEquals(HeaderNames.{header.Identifier}, key))
933-
{{{(header.Identifier == "ContentLength" ? @"
933+
{{{(header.Name == HeaderNames.ContentLength ? @"
934934
if (_contentLength.HasValue)
935935
{
936936
_contentLength = null;
@@ -948,7 +948,7 @@ protected override bool RemoveFast(string key)
948948
}}")}
949949
{Each(byLength.OrderBy(h => !h.PrimaryHeader), header => $@"
950950
if (HeaderNames.{header.Identifier}.Equals(key, StringComparison.OrdinalIgnoreCase))
951-
{{{(header.Identifier == "ContentLength" ? @"
951+
{{{(header.Name == HeaderNames.ContentLength ? @"
952952
if (_contentLength.HasValue)
953953
{
954954
_contentLength = null;

0 commit comments

Comments
 (0)