Skip to content

Commit 08c46cc

Browse files
committed
Don't close connections after upgrade requests without a 101 response
1 parent 9f4d474 commit 08c46cc

File tree

6 files changed

+22
-23
lines changed

6 files changed

+22
-23
lines changed

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,9 @@ internal sealed class Http1ChunkedEncodingMessageBody : Http1MessageBody
3030
private readonly Pipe _requestBodyPipe;
3131
private ReadResult _readResult;
3232

33-
public Http1ChunkedEncodingMessageBody(bool keepAlive, Http1Connection context)
34-
: base(context)
33+
public Http1ChunkedEncodingMessageBody(Http1Connection context, bool keepAlive)
34+
: base(context, keepAlive)
3535
{
36-
RequestKeepAlive = keepAlive;
3736
_requestBodyPipe = CreateRequestBodyPipe(context);
3837
}
3938

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

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@
1010

1111
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
1212
{
13-
using BadHttpRequestException = Microsoft.AspNetCore.Http.BadHttpRequestException;
14-
1513
internal sealed class Http1ContentLengthMessageBody : Http1MessageBody
1614
{
1715
private ReadResult _readResult;
@@ -23,12 +21,11 @@ internal sealed class Http1ContentLengthMessageBody : Http1MessageBody
2321
private bool _finalAdvanceCalled;
2422
private bool _cannotResetInputPipe;
2523

26-
public Http1ContentLengthMessageBody(bool keepAlive, long contentLength, Http1Connection context)
27-
: base(context)
24+
public Http1ContentLengthMessageBody(Http1Connection context, long contentLength, bool keepAlive)
25+
: base(context, keepAlive)
2826
{
29-
RequestKeepAlive = keepAlive;
3027
_contentLength = contentLength;
31-
_unexaminedInputLength = _contentLength;
28+
_unexaminedInputLength = contentLength;
3229
}
3330

3431
public override async ValueTask<ReadResult> ReadAsyncInternal(CancellationToken cancellationToken = default)

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

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ internal abstract class Http1MessageBody : MessageBody
1818
protected readonly Http1Connection _context;
1919
private bool _readerCompleted;
2020

21-
protected Http1MessageBody(Http1Connection context) : base(context)
21+
protected Http1MessageBody(Http1Connection context, bool keepAlive) : base(context)
2222
{
2323
_context = context;
24+
RequestKeepAlive = keepAlive;
2425
}
2526

2627
public override ValueTask<ReadResult> ReadAsync(CancellationToken cancellationToken = default)
@@ -123,14 +124,15 @@ public static MessageBody For(
123124
{
124125
// see also http://tools.ietf.org/html/rfc2616#section-4.4
125126
var keepAlive = httpVersion != HttpVersion.Http10;
126-
127127
var upgrade = false;
128+
128129
if (headers.HasConnection)
129130
{
130131
var connectionOptions = HttpHeaders.ParseConnection(headers.HeaderConnection);
131132

132133
upgrade = (connectionOptions & ConnectionOptions.Upgrade) != 0;
133-
keepAlive = (connectionOptions & ConnectionOptions.KeepAlive) != 0;
134+
keepAlive = keepAlive || (connectionOptions & ConnectionOptions.KeepAlive) != 0;
135+
keepAlive = keepAlive && (connectionOptions & ConnectionOptions.Close) == 0;
134136
}
135137

136138
if (upgrade)
@@ -141,7 +143,7 @@ public static MessageBody For(
141143
}
142144

143145
context.OnTrailersComplete(); // No trailers for these.
144-
return new Http1UpgradeMessageBody(context);
146+
return new Http1UpgradeMessageBody(context, keepAlive);
145147
}
146148

147149
if (headers.HasTransferEncoding)
@@ -162,7 +164,7 @@ public static MessageBody For(
162164

163165
// TODO may push more into the wrapper rather than just calling into the message body
164166
// NBD for now.
165-
return new Http1ChunkedEncodingMessageBody(keepAlive, context);
167+
return new Http1ChunkedEncodingMessageBody(context, keepAlive);
166168
}
167169

168170
if (headers.ContentLength.HasValue)
@@ -174,7 +176,7 @@ public static MessageBody For(
174176
return keepAlive ? MessageBody.ZeroContentLengthKeepAlive : MessageBody.ZeroContentLengthClose;
175177
}
176178

177-
return new Http1ContentLengthMessageBody(keepAlive, contentLength, context);
179+
return new Http1ContentLengthMessageBody(context, contentLength, keepAlive);
178180
}
179181

180182
// If we got here, request contains no Content-Length or Transfer-Encoding header.

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
1414
/// </summary>
1515
internal sealed class Http1UpgradeMessageBody : Http1MessageBody
1616
{
17-
public Http1UpgradeMessageBody(Http1Connection context)
18-
: base(context)
17+
public Http1UpgradeMessageBody(Http1Connection context, bool keepAlive)
18+
: base(context, keepAlive)
1919
{
2020
RequestUpgrade = true;
2121
}

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,13 +1105,13 @@ private HttpResponseHeaders CreateResponseHeaders(bool appCompleted)
11051105
{
11061106
RejectNonBodyTransferEncodingResponse(appCompleted);
11071107
}
1108+
else if (StatusCode == StatusCodes.Status101SwitchingProtocols)
1109+
{
1110+
_keepAlive = false;
1111+
}
11081112
else if (!hasTransferEncoding && !responseHeaders.ContentLength.HasValue)
11091113
{
1110-
if (StatusCode == StatusCodes.Status101SwitchingProtocols)
1111-
{
1112-
_keepAlive = false;
1113-
}
1114-
else if ((appCompleted || !_canWriteResponseBody) && !_hasAdvanced) // Avoid setting contentLength of 0 if we wrote data before calling CreateResponseHeaders
1114+
if ((appCompleted || !_canWriteResponseBody) && !_hasAdvanced) // Avoid setting contentLength of 0 if we wrote data before calling CreateResponseHeaders
11151115
{
11161116
// Don't set the Content-Length header automatically for HEAD requests, 204 responses, or 304 responses.
11171117
if (CanAutoSetContentLengthZeroResponseHeader())

src/Servers/Kestrel/perf/Kestrel.Performance/Http1ReadingBenchmark.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
using Microsoft.AspNetCore.Server.Kestrel.Core;
1313
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
1414
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
15+
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2;
1516
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
1617
using Microsoft.AspNetCore.Testing;
1718

@@ -112,7 +113,7 @@ private TestHttp1Connection MakeHttp1Connection()
112113
});
113114

114115
http1Connection.Reset();
115-
http1Connection.InitializeBodyControl(new Http1ContentLengthMessageBody(keepAlive: true, 100, http1Connection));
116+
http1Connection.InitializeBodyControl(new Http1ContentLengthMessageBody(http1Connection, contentLength: 100, keepAlive: true));
116117
serviceContext.DateHeaderValueManager.OnHeartbeat(DateTimeOffset.UtcNow);
117118

118119
return http1Connection;

0 commit comments

Comments
 (0)