Only reset Http2Stream on stream completion (#19431)
This commit is contained in:
parent
3df570fac6
commit
065f0d001c
|
|
@ -45,7 +45,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
|
||||
public Http1Connection(HttpConnectionContext context)
|
||||
{
|
||||
Initialize(context);
|
||||
Initialize(context, reset: true);
|
||||
|
||||
_context = context;
|
||||
_parser = ServiceContext.HttpParser;
|
||||
|
|
|
|||
|
|
@ -76,13 +76,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
|||
private Stream _requestStreamInternal;
|
||||
private Stream _responseStreamInternal;
|
||||
|
||||
public void Initialize(HttpConnectionContext context)
|
||||
public void Initialize(HttpConnectionContext context, bool reset)
|
||||
{
|
||||
_context = context;
|
||||
|
||||
ServerOptions = ServiceContext.ServerOptions;
|
||||
|
||||
Reset();
|
||||
if (reset)
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
HttpResponseControl = this;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,9 +33,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
private StreamCompletionFlags _completionState;
|
||||
private readonly object _completionLock = new object();
|
||||
|
||||
public void Initialize(Http2StreamContext context)
|
||||
/// <summary>
|
||||
/// Initialize the stream with the specified context.
|
||||
/// </summary>
|
||||
/// <param name="context">The context.</param>
|
||||
/// <param name="reset">
|
||||
/// A value indicating whether to reset the protocol instance.
|
||||
/// We want to reset when the stream is created, and when the stream is returned to the pool.
|
||||
/// The stream shouldn't be reset when fetched from the pool.
|
||||
/// </param>
|
||||
public void InitializePooled(Http2StreamContext context, bool reset)
|
||||
{
|
||||
base.Initialize(context);
|
||||
base.Initialize(context, reset);
|
||||
|
||||
_decrementCalled = false;
|
||||
_completionState = StreamCompletionFlags.None;
|
||||
|
|
@ -71,7 +80,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
public void InitializeWithExistingContext(int streamId)
|
||||
{
|
||||
_context.StreamId = streamId;
|
||||
Initialize(_context);
|
||||
|
||||
// The stream was reset when it was completed.
|
||||
// We don't want to reset it twice because reused headers will be discarded.
|
||||
InitializePooled(_context, reset: false);
|
||||
}
|
||||
|
||||
public int StreamId => _context.StreamId;
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
|||
|
||||
public Http2Stream(IHttpApplication<TContext> application, Http2StreamContext context)
|
||||
{
|
||||
Initialize(context);
|
||||
InitializePooled(context, reset: true);
|
||||
_application = application;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http3
|
|||
|
||||
public Http3Stream(Http3Connection http3Connection, Http3StreamContext context)
|
||||
{
|
||||
Initialize(context);
|
||||
Initialize(context, reset: true);
|
||||
|
||||
InputRemaining = null;
|
||||
|
||||
|
|
|
|||
|
|
@ -251,7 +251,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
{
|
||||
public TestHttp2Stream(Http2StreamContext context)
|
||||
{
|
||||
Initialize(context);
|
||||
InitializePooled(context, reset: true);
|
||||
}
|
||||
|
||||
public override void Execute()
|
||||
|
|
|
|||
|
|
@ -25,6 +25,53 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
{
|
||||
public class Http2ConnectionTests : Http2TestBase
|
||||
{
|
||||
[Fact]
|
||||
public async Task RequestHeaderStringReuse_MultipleStreams_KnownHeaderReused()
|
||||
{
|
||||
IEnumerable<KeyValuePair<string, string>> requestHeaders = new[]
|
||||
{
|
||||
new KeyValuePair<string, string>(HeaderNames.Method, "GET"),
|
||||
new KeyValuePair<string, string>(HeaderNames.Path, "/hello"),
|
||||
new KeyValuePair<string, string>(HeaderNames.Scheme, "http"),
|
||||
new KeyValuePair<string, string>(HeaderNames.Authority, "localhost:80"),
|
||||
new KeyValuePair<string, string>(HeaderNames.ContentType, "application/json")
|
||||
};
|
||||
|
||||
await InitializeConnectionAsync(_readHeadersApplication);
|
||||
|
||||
await StartStreamAsync(1, requestHeaders, endStream: true);
|
||||
|
||||
await ExpectAsync(Http2FrameType.HEADERS,
|
||||
withLength: 55,
|
||||
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
|
||||
withStreamId: 1);
|
||||
|
||||
var contentType1 = _receivedHeaders["Content-Type"];
|
||||
|
||||
// Ping will trigger the stream to be returned to the pool so we can assert it
|
||||
await SendPingAsync(Http2PingFrameFlags.NONE);
|
||||
await ExpectAsync(Http2FrameType.PING,
|
||||
withLength: 8,
|
||||
withFlags: (byte)Http2PingFrameFlags.ACK,
|
||||
withStreamId: 0);
|
||||
|
||||
// Stream has been returned to the pool
|
||||
Assert.Equal(1, _connection.StreamPool.Count);
|
||||
|
||||
await StartStreamAsync(3, requestHeaders, endStream: true);
|
||||
|
||||
await ExpectAsync(Http2FrameType.HEADERS,
|
||||
withLength: 55,
|
||||
withFlags: (byte)(Http2HeadersFrameFlags.END_HEADERS | Http2HeadersFrameFlags.END_STREAM),
|
||||
withStreamId: 3);
|
||||
|
||||
var contentType2 = _receivedHeaders["Content-Type"];
|
||||
|
||||
Assert.Same(contentType1, contentType2);
|
||||
|
||||
await StopConnectionAsync(expectedLastStreamId: 3, ignoreNonGoAwayFrames: false);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task StreamPool_SingleStream_ReturnedToPool()
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue