Skip to content

Commit 8009350

Browse files
authored
Log message when connection reaches maximum concurrent streams (#22452)
1 parent 7de516e commit 8009350

File tree

6 files changed

+47
-1
lines changed

6 files changed

+47
-1
lines changed

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -971,7 +971,13 @@ private void StartStream()
971971
throw new Http2StreamErrorException(_currentHeadersStream.StreamId, CoreStrings.Http2ErrorMissingMandatoryPseudoHeaderFields, Http2ErrorCode.PROTOCOL_ERROR);
972972
}
973973

974-
if (_clientActiveStreamCount > _serverSettings.MaxConcurrentStreams)
974+
if (_clientActiveStreamCount == _serverSettings.MaxConcurrentStreams)
975+
{
976+
// Provide feedback in server logs that the client hit the number of maximum concurrent streams,
977+
// and that the client is likely waiting for existing streams to be completed before it can continue.
978+
Log.Http2MaxConcurrentStreamsReached(_context.ConnectionId);
979+
}
980+
else if (_clientActiveStreamCount > _serverSettings.MaxConcurrentStreams)
975981
{
976982
// The protocol default stream limit is infinite so the client can exceed our limit at the start of the connection.
977983
// Refused streams can be retried, by which time the client must have received our settings frame with our limit information.

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,5 +74,7 @@ internal interface IKestrelTrace : ILogger
7474
void Http2FrameReceived(string connectionId, Http2Frame frame);
7575

7676
void Http2FrameSending(string connectionId, Http2Frame frame);
77+
78+
void Http2MaxConcurrentStreamsReached(string connectionId);
7779
}
7880
}

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,10 @@ internal class KestrelTrace : IKestrelTrace
113113
private static readonly Action<ILogger, string, Exception> _connectionAccepted =
114114
LoggerMessage.Define<string>(LogLevel.Debug, new EventId(39, nameof(ConnectionAccepted)), @"Connection id ""{ConnectionId}"" accepted.");
115115

116+
private static readonly Action<ILogger, string, Exception> _http2MaxConcurrentStreamsReached =
117+
LoggerMessage.Define<string>(LogLevel.Debug, new EventId(40, nameof(Http2MaxConcurrentStreamsReached)),
118+
@"Connection id ""{ConnectionId}"" reached the maximum number of concurrent HTTP/2 streams allowed.");
119+
116120
protected readonly ILogger _logger;
117121

118122
public KestrelTrace(ILogger logger)
@@ -286,6 +290,11 @@ public void Http2FrameSending(string connectionId, Http2Frame frame)
286290
}
287291
}
288292

293+
public void Http2MaxConcurrentStreamsReached(string connectionId)
294+
{
295+
_http2MaxConcurrentStreamsReached(_logger, connectionId, null);
296+
}
297+
289298
public virtual void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
290299
=> _logger.Log(logLevel, eventId, state, exception, formatter);
291300

src/Servers/Kestrel/perf/Kestrel.Performance/Mocks/MockTrace.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,5 +55,6 @@ public void Http2ConnectionClosing(string connectionId) { }
5555
public void Http2ConnectionClosed(string connectionId, int highestOpenedStreamId) { }
5656
public void Http2FrameReceived(string connectionId, Http2Frame frame) { }
5757
public void Http2FrameSending(string connectionId, Http2Frame frame) { }
58+
public void Http2MaxConcurrentStreamsReached(string connectionId) { }
5859
}
5960
}

src/Servers/Kestrel/shared/test/CompositeKestrelTrace.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,5 +229,11 @@ public void Http2FrameSending(string connectionId, Http2Frame frame)
229229
_trace1.Http2FrameSending(connectionId, frame);
230230
_trace2.Http2FrameSending(connectionId, frame);
231231
}
232+
233+
public void Http2MaxConcurrentStreamsReached(string connectionId)
234+
{
235+
_trace1.Http2MaxConcurrentStreamsReached(connectionId);
236+
_trace2.Http2MaxConcurrentStreamsReached(connectionId);
237+
}
232238
}
233239
}

src/Servers/Kestrel/test/InMemory.FunctionalTests/Http2/Http2ConnectionTests.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,28 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
2828
{
2929
public class Http2ConnectionTests : Http2TestBase
3030
{
31+
[Fact]
32+
public async Task MaxConcurrentStreamsLogging_ReachLimit_MessageLogged()
33+
{
34+
await InitializeConnectionAsync(_echoApplication);
35+
36+
_connection.ServerSettings.MaxConcurrentStreams = 2;
37+
38+
await StartStreamAsync(1, _browserRequestHeaders, endStream: false);
39+
Assert.Equal(0, TestApplicationErrorLogger.Messages.Count(m => m.EventId.Name == "Http2MaxConcurrentStreamsReached"));
40+
41+
// Log message because we've reached the stream limit
42+
await StartStreamAsync(3, _browserRequestHeaders, endStream: false);
43+
Assert.Equal(1, TestApplicationErrorLogger.Messages.Count(m => m.EventId.Name == "Http2MaxConcurrentStreamsReached"));
44+
45+
// This stream will error because it exceeds max concurrent streams
46+
await StartStreamAsync(5, _browserRequestHeaders, endStream: true);
47+
await WaitForStreamErrorAsync(5, Http2ErrorCode.REFUSED_STREAM, CoreStrings.Http2ErrorMaxStreams);
48+
Assert.Equal(1, TestApplicationErrorLogger.Messages.Count(m => m.EventId.Name == "Http2MaxConcurrentStreamsReached"));
49+
50+
await StopConnectionAsync(expectedLastStreamId: 5, ignoreNonGoAwayFrames: false);
51+
}
52+
3153
[Fact]
3254
public async Task FlowControl_NoAvailability_ResponseHeadersStillFlushed()
3355
{

0 commit comments

Comments
 (0)