Skip to content

Commit c510461

Browse files
committed
Update
1 parent a15f83c commit c510461

File tree

6 files changed

+81
-27
lines changed

6 files changed

+81
-27
lines changed

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -659,7 +659,7 @@ private async Task ProcessRequests<TContext>(IHttpApplication<TContext> applicat
659659
var messageBody = CreateMessageBody();
660660
if (!messageBody.RequestKeepAlive)
661661
{
662-
_keepAlive = false;
662+
DisableKeepAlive(ConnectionEndReason.RequestNoKeepAlive);
663663
}
664664

665665
IsUpgradableRequest = messageBody.RequestUpgrade;
@@ -1032,7 +1032,7 @@ protected Task ProduceEnd()
10321032
if (HasResponseStarted)
10331033
{
10341034
// We can no longer change the response, so we simply close the connection.
1035-
_keepAlive = false;
1035+
DisableKeepAlive(ConnectionEndReason.ErrorAfterStartingResponse);
10361036
OnErrorAfterResponseStarted();
10371037
return Task.CompletedTask;
10381038
}
@@ -1139,7 +1139,7 @@ private HttpResponseHeaders CreateResponseHeaders(bool appCompleted)
11391139
hasConnection &&
11401140
(HttpHeaders.ParseConnection(responseHeaders) & ConnectionOptions.KeepAlive) == 0)
11411141
{
1142-
_keepAlive = false;
1142+
DisableKeepAlive(ConnectionEndReason.ResponseNoKeepAlive);
11431143
}
11441144

11451145
// https://tools.ietf.org/html/rfc7230#section-3.3.1
@@ -1150,7 +1150,7 @@ private HttpResponseHeaders CreateResponseHeaders(bool appCompleted)
11501150
if (hasTransferEncoding &&
11511151
HttpHeaders.GetFinalTransferCoding(responseHeaders.HeaderTransferEncoding) != TransferCoding.Chunked)
11521152
{
1153-
_keepAlive = false;
1153+
DisableKeepAlive(ConnectionEndReason.ResponseNoKeepAlive);
11541154
}
11551155

11561156
// Set whether response can have body
@@ -1186,7 +1186,7 @@ private HttpResponseHeaders CreateResponseHeaders(bool appCompleted)
11861186
}
11871187
else if (StatusCode == StatusCodes.Status101SwitchingProtocols)
11881188
{
1189-
_keepAlive = false;
1189+
DisableKeepAlive(ConnectionEndReason.ResponseNoKeepAlive);
11901190
}
11911191
else if (!hasTransferEncoding && !responseHeaders.ContentLength.HasValue)
11921192
{
@@ -1216,7 +1216,7 @@ private HttpResponseHeaders CreateResponseHeaders(bool appCompleted)
12161216
}
12171217
else
12181218
{
1219-
_keepAlive = false;
1219+
DisableKeepAlive(ConnectionEndReason.ResponseNoKeepAlive);
12201220
}
12211221
}
12221222

src/Servers/Kestrel/Core/src/Internal/Http2/Http2OutputProducer.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,8 +239,8 @@ public void Complete()
239239
}
240240
}
241241

242-
// This is called when a CancellationToken fires mid-write. In HTTP/1.x, this aborts the entire connection.
243-
// For HTTP/2 we abort the stream.
242+
// This is called when a CancellationToken fires mid-write.
243+
// In HTTP/1.x, this aborts the entire connection. For HTTP/2 we abort the stream.
244244
void IHttpOutputAborter.Abort(ConnectionAbortedException abortReason, ConnectionEndReason reason)
245245
{
246246
_stream.ResetAndAbort(abortReason, Http2ErrorCode.INTERNAL_ERROR);

src/Servers/Kestrel/Core/src/Internal/Http3/Http3OutputProducer.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ public void Dispose()
9595
}
9696
}
9797

98+
// In HTTP/1.x, this aborts the entire connection. For HTTP/3 we abort the stream.
9899
void IHttpOutputAborter.Abort(ConnectionAbortedException abortReason, ConnectionEndReason reason)
99100
{
100101
_stream.Abort(abortReason, Http3ErrorCode.InternalError);

src/Servers/Kestrel/Core/src/Internal/Infrastructure/KestrelMetrics.cs

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -446,16 +446,6 @@ public static void AddConnectionEndReason(ConnectionMetricsContext? context, Con
446446

447447
if (context != null)
448448
{
449-
if (reason == ConnectionEndReason.Unexpected)
450-
{
451-
if (context.ConnectionEndReason == null)
452-
{
453-
Debugger.Launch();
454-
}
455-
456-
return;
457-
}
458-
459449
if (TryGetErrorType(reason, out _))
460450
{
461451
if (context.ConnectionEndReason == null || overwrite)
@@ -480,6 +470,9 @@ internal static bool TryGetErrorType(ConnectionEndReason reason, [NotNullWhen(tr
480470
ConnectionEndReason.ClientGoAway => null, // Not an error
481471
ConnectionEndReason.TransportCompleted => null, // Not an error
482472
ConnectionEndReason.GracefulAppShutdown => null, // Not an error
473+
ConnectionEndReason.RequestNoKeepAlive => null, // Not an error
474+
ConnectionEndReason.ResponseNoKeepAlive => null, // Not an error
475+
ConnectionEndReason.ErrorAfterStartingResponse => "error_after_headers",
483476
ConnectionEndReason.ConnectionReset => "connection_reset",
484477
ConnectionEndReason.FlowControlWindowExceeded => "flow_control_window_exceeded",
485478
ConnectionEndReason.KeepAliveTimeout => "keep_alive_timeout",

src/Servers/Kestrel/test/InMemory.FunctionalTests/ResponseTests.cs

Lines changed: 66 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -672,6 +672,9 @@ public async Task AttemptingToWriteZeroContentLengthFor2xxResponsesOnConnect_Con
672672

673673
private async Task AttemptingToWriteNonzeroContentLengthFails(int statusCode, HttpMethod method)
674674
{
675+
var testMeterFactory = new TestMeterFactory();
676+
using var connectionDuration = new MetricCollector<double>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel.connection.duration");
677+
675678
var responseWriteTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
676679

677680
await using (var server = new TestServer(async httpContext =>
@@ -690,7 +693,7 @@ private async Task AttemptingToWriteNonzeroContentLengthFails(int statusCode, Ht
690693
}
691694

692695
responseWriteTcs.TrySetResult();
693-
}, new TestServiceContext(LoggerFactory)))
696+
}, new TestServiceContext(LoggerFactory, metrics: new KestrelMetrics(testMeterFactory))))
694697
{
695698
using (var connection = server.CreateConnection())
696699
{
@@ -704,6 +707,11 @@ await connection.Send(
704707
Assert.Equal(CoreStrings.FormatHeaderNotAllowedOnResponse("Content-Length", statusCode), ex.Message);
705708
}
706709
}
710+
711+
Assert.Collection(connectionDuration.GetMeasurementSnapshot(), m =>
712+
{
713+
Assert.DoesNotContain(KestrelMetrics.ErrorType, m.Tags.Keys);
714+
});
707715
}
708716

709717
private async Task AttemptingToWriteZeroContentLength_ContentLengthRemoved(int statusCode, HttpMethod method)
@@ -944,7 +952,10 @@ await connection.Receive(
944952
[Fact]
945953
public async Task ThrowsAndClosesConnectionWhenAppWritesMoreThanContentLengthWrite()
946954
{
947-
var serviceContext = new TestServiceContext(LoggerFactory)
955+
var testMeterFactory = new TestMeterFactory();
956+
using var connectionDuration = new MetricCollector<double>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel.connection.duration");
957+
958+
var serviceContext = new TestServiceContext(LoggerFactory, metrics: new KestrelMetrics(testMeterFactory))
948959
{
949960
ServerOptions = { AllowSynchronousIO = true }
950961
};
@@ -979,12 +990,19 @@ await connection.Receive(
979990
Assert.Equal(
980991
$"Response Content-Length mismatch: too many bytes written (12 of 11).",
981992
logMessage.Exception.Message);
993+
994+
Assert.Collection(connectionDuration.GetMeasurementSnapshot(), m =>
995+
{
996+
Assert.Equal(KestrelMetrics.GetErrorType(ConnectionEndReason.ResponseContentLengthMismatch), m.Tags[KestrelMetrics.ErrorType]);
997+
});
982998
}
983999

9841000
[Fact]
9851001
public async Task ThrowsAndClosesConnectionWhenAppWritesMoreThanContentLengthWriteAsync()
9861002
{
987-
var serviceContext = new TestServiceContext(LoggerFactory);
1003+
var testMeterFactory = new TestMeterFactory();
1004+
using var connectionDuration = new MetricCollector<double>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel.connection.duration");
1005+
var serviceContext = new TestServiceContext(LoggerFactory, metrics: new KestrelMetrics(testMeterFactory));
9881006

9891007
await using (var server = new TestServer(async httpContext =>
9901008
{
@@ -1013,6 +1031,11 @@ await connection.ReceiveEnd(
10131031
Assert.Equal(
10141032
$"Response Content-Length mismatch: too many bytes written (12 of 11).",
10151033
logMessage.Exception.Message);
1034+
1035+
Assert.Collection(connectionDuration.GetMeasurementSnapshot(), m =>
1036+
{
1037+
Assert.Equal(KestrelMetrics.GetErrorType(ConnectionEndReason.ResponseContentLengthMismatch), m.Tags[KestrelMetrics.ErrorType]);
1038+
});
10161039
}
10171040

10181041
[Fact]
@@ -2404,7 +2427,9 @@ await connection.ReceiveEnd(
24042427
[Fact]
24052428
public async Task ThrowingResultsIn500Response()
24062429
{
2407-
var testContext = new TestServiceContext(LoggerFactory);
2430+
var testMeterFactory = new TestMeterFactory();
2431+
using var connectionDuration = new MetricCollector<double>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel.connection.duration");
2432+
var testContext = new TestServiceContext(LoggerFactory, metrics: new KestrelMetrics(testMeterFactory));
24082433

24092434
bool onStartingCalled = false;
24102435

@@ -2449,6 +2474,11 @@ await connection.ReceiveEnd(
24492474

24502475
Assert.False(onStartingCalled);
24512476
Assert.Equal(2, LogMessages.Where(message => message.LogLevel == LogLevel.Error).Count());
2477+
2478+
Assert.Collection(connectionDuration.GetMeasurementSnapshot(), m =>
2479+
{
2480+
Assert.DoesNotContain(KestrelMetrics.ErrorType, m.Tags.Keys);
2481+
});
24522482
}
24532483

24542484
[Fact]
@@ -2603,7 +2633,9 @@ await connection.Receive(
26032633
[Fact]
26042634
public async Task ThrowingInOnCompletedIsLogged()
26052635
{
2606-
var testContext = new TestServiceContext(LoggerFactory);
2636+
var testMeterFactory = new TestMeterFactory();
2637+
using var connectionDuration = new MetricCollector<double>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel.connection.duration");
2638+
var testContext = new TestServiceContext(LoggerFactory, metrics: new KestrelMetrics(testMeterFactory));
26072639

26082640
var onCompletedCalled1 = false;
26092641
var onCompletedCalled2 = false;
@@ -2647,12 +2679,19 @@ await connection.Receive(
26472679
Assert.Equal(2, LogMessages.Where(message => message.LogLevel == LogLevel.Error).Count());
26482680
Assert.True(onCompletedCalled1);
26492681
Assert.True(onCompletedCalled2);
2682+
2683+
Assert.Collection(connectionDuration.GetMeasurementSnapshot(), m =>
2684+
{
2685+
Assert.DoesNotContain(KestrelMetrics.ErrorType, m.Tags.Keys);
2686+
});
26502687
}
26512688

26522689
[Fact]
26532690
public async Task ThrowingAfterWritingKillsConnection()
26542691
{
2655-
var testContext = new TestServiceContext(LoggerFactory);
2692+
var testMeterFactory = new TestMeterFactory();
2693+
using var connectionDuration = new MetricCollector<double>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel.connection.duration");
2694+
var testContext = new TestServiceContext(LoggerFactory, metrics: new KestrelMetrics(testMeterFactory));
26562695

26572696
bool onStartingCalled = false;
26582697

@@ -2688,12 +2727,19 @@ await connection.ReceiveEnd(
26882727

26892728
Assert.True(onStartingCalled);
26902729
Assert.Single(LogMessages, message => message.LogLevel == LogLevel.Error);
2730+
2731+
Assert.Collection(connectionDuration.GetMeasurementSnapshot(), m =>
2732+
{
2733+
Assert.Equal(KestrelMetrics.GetErrorType(ConnectionEndReason.ErrorAfterStartingResponse), m.Tags[KestrelMetrics.ErrorType]);
2734+
});
26912735
}
26922736

26932737
[Fact]
26942738
public async Task ThrowingAfterPartialWriteKillsConnection()
26952739
{
2696-
var testContext = new TestServiceContext(LoggerFactory);
2740+
var testMeterFactory = new TestMeterFactory();
2741+
using var connectionDuration = new MetricCollector<double>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel.connection.duration");
2742+
var testContext = new TestServiceContext(LoggerFactory, metrics: new KestrelMetrics(testMeterFactory));
26972743

26982744
bool onStartingCalled = false;
26992745

@@ -2729,6 +2775,11 @@ await connection.ReceiveEnd(
27292775

27302776
Assert.True(onStartingCalled);
27312777
Assert.Single(LogMessages, message => message.LogLevel == LogLevel.Error);
2778+
2779+
Assert.Collection(connectionDuration.GetMeasurementSnapshot(), m =>
2780+
{
2781+
Assert.Equal(KestrelMetrics.GetErrorType(ConnectionEndReason.ErrorAfterStartingResponse), m.Tags[KestrelMetrics.ErrorType]);
2782+
});
27322783
}
27332784

27342785
[Fact]
@@ -2788,7 +2839,9 @@ await connection.Send(
27882839
[Fact]
27892840
public async Task AppAbortIsLogged()
27902841
{
2791-
var testContext = new TestServiceContext(LoggerFactory);
2842+
var testMeterFactory = new TestMeterFactory();
2843+
using var connectionDuration = new MetricCollector<double>(testMeterFactory, "Microsoft.AspNetCore.Server.Kestrel", "kestrel.connection.duration");
2844+
var testContext = new TestServiceContext(LoggerFactory, metrics: new KestrelMetrics(testMeterFactory));
27922845

27932846
await using (var server = new TestServer(httpContext =>
27942847
{
@@ -2808,6 +2861,11 @@ await connection.Send(
28082861
}
28092862

28102863
Assert.Single(LogMessages.Where(m => m.Message.Contains(CoreStrings.ConnectionAbortedByApplication)));
2864+
2865+
Assert.Collection(connectionDuration.GetMeasurementSnapshot(), m =>
2866+
{
2867+
Assert.Equal(KestrelMetrics.GetErrorType(ConnectionEndReason.AbortedByApp), m.Tags[KestrelMetrics.ErrorType]);
2868+
});
28112869
}
28122870

28132871
[Fact]

src/Shared/ServerInfrastructure/Http2/ConnectionEndReason.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,5 +52,7 @@ internal enum ConnectionEndReason
5252
UnexpectedEndOfRequestContent,
5353
MaxConcurrentConnectionsExceeded,
5454
ResponseContentLengthMismatch,
55-
Unexpected
55+
RequestNoKeepAlive,
56+
ResponseNoKeepAlive,
57+
ErrorAfterStartingResponse
5658
}

0 commit comments

Comments
 (0)