From 668f8e3b4b4f6e74e7a36433439847575889058e Mon Sep 17 00:00:00 2001 From: Stephen Halter Date: Mon, 4 Dec 2017 15:59:12 -0800 Subject: [PATCH] Lazily allocate protocol-specific connection objects (#2190) * Refactor Http[12]?Connection --- ...Http1ConnectionParsingOverheadBenchmark.cs | 4 +- .../Http1WritingBenchmark.cs | 21 +- .../HttpProtocolFeatureCollection.cs | 4 +- .../RequestParsingBenchmark.cs | 4 +- .../ResponseHeaderCollectionBenchmark.cs | 2 +- .../ResponseHeadersWritingBenchmark.cs | 21 +- .../Internal/Http/Http1Connection.cs | 8 +- .../Internal/Http/Http1ConnectionOfT.cs | 27 --- .../Internal/Http/HttpProtocol.cs | 16 +- .../Internal/Http2/Http2Connection.cs | 23 +- .../Internal/Http2/Http2Stream.cs | 2 +- .../Internal/Http2/Http2StreamOfT.cs | 27 --- src/Kestrel.Core/Internal/HttpConnection.cs | 203 ++++++++++-------- .../Internal/IRequestProcessor.cs | 16 ++ .../Http1ConnectionTests.cs | 26 +-- .../Http2ConnectionTests.cs | 2 +- .../Kestrel.Core.Tests/HttpConnectionTests.cs | 30 +-- .../HttpResponseHeadersTests.cs | 2 +- test/Kestrel.Core.Tests/TestInput.cs | 2 +- .../LibuvOutputConsumerTests.cs | 2 +- test/shared/TestHttp1Connection.cs | 8 +- 21 files changed, 206 insertions(+), 244 deletions(-) delete mode 100644 src/Kestrel.Core/Internal/Http/Http1ConnectionOfT.cs delete mode 100644 src/Kestrel.Core/Internal/Http2/Http2StreamOfT.cs create mode 100644 src/Kestrel.Core/Internal/IRequestProcessor.cs diff --git a/benchmarks/Kestrel.Performance/Http1ConnectionParsingOverheadBenchmark.cs b/benchmarks/Kestrel.Performance/Http1ConnectionParsingOverheadBenchmark.cs index 35f40de854..42e4cba170 100644 --- a/benchmarks/Kestrel.Performance/Http1ConnectionParsingOverheadBenchmark.cs +++ b/benchmarks/Kestrel.Performance/Http1ConnectionParsingOverheadBenchmark.cs @@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance private const int InnerLoopCount = 512; public ReadableBuffer _buffer; - public Http1Connection _http1Connection; + public Http1Connection _http1Connection; [IterationSetup] public void Setup() @@ -32,7 +32,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance HttpParser = NullParser.Instance }; - var http1Connection = new Http1Connection(application: null, context: new Http1ConnectionContext + var http1Connection = new Http1Connection(new Http1ConnectionContext { ServiceContext = serviceContext, ConnectionFeatures = new FeatureCollection(), diff --git a/benchmarks/Kestrel.Performance/Http1WritingBenchmark.cs b/benchmarks/Kestrel.Performance/Http1WritingBenchmark.cs index ea5e13646f..717009affa 100644 --- a/benchmarks/Kestrel.Performance/Http1WritingBenchmark.cs +++ b/benchmarks/Kestrel.Performance/Http1WritingBenchmark.cs @@ -25,7 +25,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance private static readonly Task _psuedoAsyncTask = Task.FromResult(27); private static readonly Func _psuedoAsyncTaskFunc = (obj) => _psuedoAsyncTask; - private readonly TestHttp1Connection _http1Connection; + private readonly TestHttp1Connection _http1Connection; private (IPipeConnection Transport, IPipeConnection Application) _pair; private readonly byte[] _writeData; @@ -92,7 +92,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance return _http1Connection.ResponseBody.WriteAsync(_writeData, 0, _writeData.Length, default(CancellationToken)); } - private TestHttp1Connection MakeHttp1Connection() + private TestHttp1Connection MakeHttp1Connection() { using (var memoryPool = new MemoryPool()) { @@ -107,15 +107,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance HttpParser = new HttpParser() }; - var http1Connection = new TestHttp1Connection( - application: null, context: new Http1ConnectionContext - { - ServiceContext = serviceContext, - ConnectionFeatures = new FeatureCollection(), - BufferPool = memoryPool, - Application = pair.Application, - Transport = pair.Transport - }); + var http1Connection = new TestHttp1Connection(new Http1ConnectionContext + { + ServiceContext = serviceContext, + ConnectionFeatures = new FeatureCollection(), + BufferPool = memoryPool, + Application = pair.Application, + Transport = pair.Transport + }); http1Connection.Reset(); http1Connection.InitializeStreams(MessageBody.ZeroContentLengthKeepAlive); diff --git a/benchmarks/Kestrel.Performance/HttpProtocolFeatureCollection.cs b/benchmarks/Kestrel.Performance/HttpProtocolFeatureCollection.cs index 1df078f0e5..b9cc8b27c4 100644 --- a/benchmarks/Kestrel.Performance/HttpProtocolFeatureCollection.cs +++ b/benchmarks/Kestrel.Performance/HttpProtocolFeatureCollection.cs @@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance [ParameterizedJobConfig(typeof(CoreConfig))] public class HttpProtocolFeatureCollection { - private readonly Http1Connection _http1Connection; + private readonly Http1Connection _http1Connection; private IFeatureCollection _collection; [Benchmark(Baseline = true)] @@ -89,7 +89,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance HttpParser = new HttpParser() }; - var http1Connection = new Http1Connection(application: null, context: new Http1ConnectionContext + var http1Connection = new Http1Connection(new Http1ConnectionContext { ServiceContext = serviceContext, ConnectionFeatures = new FeatureCollection(), diff --git a/benchmarks/Kestrel.Performance/RequestParsingBenchmark.cs b/benchmarks/Kestrel.Performance/RequestParsingBenchmark.cs index d8c002c543..52b0bdca68 100644 --- a/benchmarks/Kestrel.Performance/RequestParsingBenchmark.cs +++ b/benchmarks/Kestrel.Performance/RequestParsingBenchmark.cs @@ -17,7 +17,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance { public IPipe Pipe { get; set; } - public Http1Connection Http1Connection { get; set; } + public Http1Connection Http1Connection { get; set; } [IterationSetup] public void Setup() @@ -33,7 +33,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance HttpParser = new HttpParser() }; - var http1Connection = new Http1Connection(application: null, context: new Http1ConnectionContext + var http1Connection = new Http1Connection(new Http1ConnectionContext { ServiceContext = serviceContext, ConnectionFeatures = new FeatureCollection(), diff --git a/benchmarks/Kestrel.Performance/ResponseHeaderCollectionBenchmark.cs b/benchmarks/Kestrel.Performance/ResponseHeaderCollectionBenchmark.cs index 6f9ac3b78a..664567cd88 100644 --- a/benchmarks/Kestrel.Performance/ResponseHeaderCollectionBenchmark.cs +++ b/benchmarks/Kestrel.Performance/ResponseHeaderCollectionBenchmark.cs @@ -182,7 +182,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance HttpParser = new HttpParser() }; - var http1Connection = new Http1Connection(application: null, context: new Http1ConnectionContext + var http1Connection = new Http1Connection(new Http1ConnectionContext { ServiceContext = serviceContext, ConnectionFeatures = new FeatureCollection(), diff --git a/benchmarks/Kestrel.Performance/ResponseHeadersWritingBenchmark.cs b/benchmarks/Kestrel.Performance/ResponseHeadersWritingBenchmark.cs index c11e5ceff5..e56c7112e5 100644 --- a/benchmarks/Kestrel.Performance/ResponseHeadersWritingBenchmark.cs +++ b/benchmarks/Kestrel.Performance/ResponseHeadersWritingBenchmark.cs @@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance { private static readonly byte[] _helloWorldPayload = Encoding.ASCII.GetBytes("Hello, World!"); - private TestHttp1Connection _http1Connection; + private TestHttp1Connection _http1Connection; [Params( BenchmarkTypes.TechEmpowerPlaintext, @@ -122,16 +122,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance HttpParser = new HttpParser() }; - var http1Connection = new TestHttp1Connection( - application: null, context: new Http1ConnectionContext - { - ServiceContext = serviceContext, - ConnectionFeatures = new FeatureCollection(), - BufferPool = bufferPool, - TimeoutControl = new MockTimeoutControl(), - Application = pair.Application, - Transport = pair.Transport - }); + var http1Connection = new TestHttp1Connection(new Http1ConnectionContext + { + ServiceContext = serviceContext, + ConnectionFeatures = new FeatureCollection(), + BufferPool = bufferPool, + TimeoutControl = new MockTimeoutControl(), + Application = pair.Application, + Transport = pair.Transport + }); http1Connection.Reset(); diff --git a/src/Kestrel.Core/Internal/Http/Http1Connection.cs b/src/Kestrel.Core/Internal/Http/Http1Connection.cs index a9e74a4220..29b257208b 100644 --- a/src/Kestrel.Core/Internal/Http/Http1Connection.cs +++ b/src/Kestrel.Core/Internal/Http/Http1Connection.cs @@ -6,13 +6,12 @@ using System.Diagnostics; using System.IO.Pipelines; using System.Text; using System.Text.Encodings.Web.Utf8; -using System.Threading.Tasks; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http { - public abstract partial class Http1Connection : HttpProtocol + public partial class Http1Connection : HttpProtocol, IRequestProcessor { private const byte ByteAsterisk = (byte)'*'; private const byte ByteForwardSlash = (byte)'/'; @@ -146,6 +145,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http { TimeoutControl.CancelTimeout(); } + return result; } @@ -337,7 +337,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http QueryString = query.GetAsciiStringNonNullCharacters(); } - private unsafe static string GetUtf8String(Span path) + private static unsafe string GetUtf8String(Span path) { // .NET 451 doesn't have pointer overloads for Encoding.GetString so we // copy to an array @@ -347,7 +347,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http } } - protected void EnsureHostHeaderExists() + private void EnsureHostHeaderExists() { if (_httpVersion == Http.HttpVersion.Http10) { diff --git a/src/Kestrel.Core/Internal/Http/Http1ConnectionOfT.cs b/src/Kestrel.Core/Internal/Http/Http1ConnectionOfT.cs deleted file mode 100644 index 2e0cf25669..0000000000 --- a/src/Kestrel.Core/Internal/Http/Http1ConnectionOfT.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Threading.Tasks; -using Microsoft.AspNetCore.Hosting.Server; - -namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http -{ - public class Http1Connection : Http1Connection - { - private readonly IHttpApplication _application; - - private TContext _httpContext; - - public Http1Connection(IHttpApplication application, Http1ConnectionContext context) - : base(context) - { - _application = application; - } - - protected override void CreateHttpContext() => _httpContext = _application.CreateContext(this); - - protected override void DisposeHttpContext() => _application.DisposeContext(_httpContext, _applicationException); - - protected override Task InvokeApplicationAsync() => _application.ProcessRequestAsync(_httpContext); - } -} diff --git a/src/Kestrel.Core/Internal/Http/HttpProtocol.cs b/src/Kestrel.Core/Internal/Http/HttpProtocol.cs index 0cadbce735..dacd5313f4 100644 --- a/src/Kestrel.Core/Internal/Http/HttpProtocol.cs +++ b/src/Kestrel.Core/Internal/Http/HttpProtocol.cs @@ -12,6 +12,7 @@ using System.Runtime.CompilerServices; using System.Text; using System.Threading; using System.Threading.Tasks; +using Microsoft.AspNetCore.Hosting.Server; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Protocols; @@ -391,12 +392,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http protected abstract bool TryParseRequest(ReadResult result, out bool endConnection); - protected abstract void CreateHttpContext(); - - protected abstract void DisposeHttpContext(); - - protected abstract Task InvokeApplicationAsync(); - private void CancelRequestAbortedToken() { try @@ -440,7 +435,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http HttpRequestHeaders.Append(name, valueString); } - public async Task ProcessRequestsAsync() + public async Task ProcessRequestsAsync(IHttpApplication application) { try { @@ -474,14 +469,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http InitializeStreams(messageBody); - CreateHttpContext(); + var httpContext = application.CreateContext(this); + try { try { KestrelEventSource.Log.RequestStart(this); - await InvokeApplicationAsync(); + await application.ProcessRequestAsync(httpContext); if (_requestAborted == 0) { @@ -563,7 +559,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http } finally { - DisposeHttpContext(); + application.DisposeContext(httpContext, _applicationException); // StopStreams should be called before the end of the "if (!_requestProcessingStopping)" block // to ensure InitializeStreams has been called. diff --git a/src/Kestrel.Core/Internal/Http2/Http2Connection.cs b/src/Kestrel.Core/Internal/Http2/Http2Connection.cs index 83ec0d64f6..9d6ac80794 100644 --- a/src/Kestrel.Core/Internal/Http2/Http2Connection.cs +++ b/src/Kestrel.Core/Internal/Http2/Http2Connection.cs @@ -6,6 +6,7 @@ using System.Collections.Concurrent; using System.IO.Pipelines; using System.Text; using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting.Server; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Protocols; @@ -16,7 +17,7 @@ using Microsoft.Extensions.Logging; namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 { - public class Http2Connection : ITimeoutControl, IHttp2StreamLifetimeHandler, IHttpHeadersHandler + public class Http2Connection : ITimeoutControl, IHttp2StreamLifetimeHandler, IHttpHeadersHandler, IRequestProcessor { private enum RequestHeaderParsingState { @@ -93,13 +94,13 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 _frameWriter.Abort(ex); } - public void Stop() + public void StopProcessingNextRequest() { _stopping = true; Input.CancelPendingRead(); } - public async Task ProcessAsync(IHttpApplication application) + public async Task ProcessRequestsAsync(IHttpApplication application) { Exception error = null; var errorCode = Http2ErrorCode.NO_ERROR; @@ -391,7 +392,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 else { // Start a new stream - _currentHeadersStream = new Http2Stream(application, new Http2StreamContext + _currentHeadersStream = new Http2Stream(new Http2StreamContext { ConnectionId = ConnectionId, StreamId = _incomingFrame.StreamId, @@ -412,7 +413,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 _currentHeadersStream.Reset(); var endHeaders = (_incomingFrame.HeadersFlags & Http2HeadersFrameFlags.END_HEADERS) == Http2HeadersFrameFlags.END_HEADERS; - await DecodeHeadersAsync(endHeaders, _incomingFrame.HeadersPayload); + await DecodeHeadersAsync(application, endHeaders, _incomingFrame.HeadersPayload); } } @@ -541,7 +542,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 throw new Http2ConnectionErrorException(CoreStrings.FormatHttp2ErrorStreamIdNotZero(_incomingFrame.Type), Http2ErrorCode.PROTOCOL_ERROR); } - Stop(); + StopProcessingNextRequest(); return Task.CompletedTask; } @@ -602,7 +603,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 } else { - return DecodeHeadersAsync(endHeaders, _incomingFrame.Payload); + return DecodeHeadersAsync(application, endHeaders, _incomingFrame.Payload); } } @@ -616,7 +617,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 return Task.CompletedTask; } - private Task DecodeHeadersAsync(bool endHeaders, Span payload) + private Task DecodeHeadersAsync(IHttpApplication application, bool endHeaders, Span payload) { try { @@ -624,7 +625,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 if (endHeaders) { - StartStream(); + StartStream(application); ResetRequestHeaderParsingState(); } } @@ -652,7 +653,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 return Task.CompletedTask; } - private void StartStream() + private void StartStream(IHttpApplication application) { if (!_isMethodConnect && (_parsedPseudoHeaderFields & _mandatoryRequestPseudoHeaderFields) != _mandatoryRequestPseudoHeaderFields) { @@ -663,7 +664,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 } _streams[_incomingFrame.StreamId] = _currentHeadersStream; - _ = _currentHeadersStream.ProcessRequestsAsync(); + _ = _currentHeadersStream.ProcessRequestsAsync(application); } private void ResetRequestHeaderParsingState() diff --git a/src/Kestrel.Core/Internal/Http2/Http2Stream.cs b/src/Kestrel.Core/Internal/Http2/Http2Stream.cs index ea8a564d9f..1d96e3f7be 100644 --- a/src/Kestrel.Core/Internal/Http2/Http2Stream.cs +++ b/src/Kestrel.Core/Internal/Http2/Http2Stream.cs @@ -10,7 +10,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 { - public abstract partial class Http2Stream : HttpProtocol + public partial class Http2Stream : HttpProtocol { private readonly Http2StreamContext _context; diff --git a/src/Kestrel.Core/Internal/Http2/Http2StreamOfT.cs b/src/Kestrel.Core/Internal/Http2/Http2StreamOfT.cs deleted file mode 100644 index 5f6c1e7d95..0000000000 --- a/src/Kestrel.Core/Internal/Http2/Http2StreamOfT.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.Threading.Tasks; -using Microsoft.AspNetCore.Hosting.Server; - -namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 -{ - public class Http2Stream : Http2Stream - { - private readonly IHttpApplication _application; - - private TContext _httpContext; - - public Http2Stream(IHttpApplication application, Http2StreamContext context) - : base(context) - { - _application = application; - } - - protected override void CreateHttpContext() => _httpContext = _application.CreateContext(this); - - protected override void DisposeHttpContext() => _application.DisposeContext(_httpContext, _applicationException); - - protected override Task InvokeApplicationAsync() => _application.ProcessRequestAsync(_httpContext); - } -} diff --git a/src/Kestrel.Core/Internal/HttpConnection.cs b/src/Kestrel.Core/Internal/HttpConnection.cs index 2ecc65d253..81bf33f2dc 100644 --- a/src/Kestrel.Core/Internal/HttpConnection.cs +++ b/src/Kestrel.Core/Internal/HttpConnection.cs @@ -21,30 +21,29 @@ using Microsoft.Extensions.Logging; namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal { - public class HttpConnection : ITimeoutControl, IConnectionTimeoutFeature + public class HttpConnection : ITimeoutControl, IConnectionTimeoutFeature, IRequestProcessor { - private const int Http2ConnectionNotStarted = 0; - private const int Http2ConnectionStarted = 1; - private const int Http2ConnectionClosed = 2; - private readonly HttpConnectionContext _context; - private IList _adaptedConnections; private readonly TaskCompletionSource _socketClosedTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + + private IList _adaptedConnections; + private IPipeConnection _adaptedTransport; + + private readonly object _protocolSelectionLock = new object(); + private IRequestProcessor _requestProcessor; private Http1Connection _http1Connection; - private Http2Connection _http2Connection; - private volatile int _http2ConnectionState; private long _lastTimestamp; private long _timeoutTimestamp = long.MaxValue; private TimeoutAction _timeoutAction; - private object _readTimingLock = new object(); + private readonly object _readTimingLock = new object(); private bool _readTimingEnabled; private bool _readTimingPauseRequested; private long _readTimingElapsedTicks; private long _readTimingBytesRead; - private object _writeTimingLock = new object(); + private readonly object _writeTimingLock = new object(); private int _writeTimingWrites; private long _writeTimingTimeoutTimestamp; @@ -53,6 +52,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal public HttpConnection(HttpConnectionContext context) { _context = context; + _requestProcessor = this; } // For testing @@ -102,35 +102,29 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal AdaptedPipeline adaptedPipeline = null; var adaptedPipelineTask = Task.CompletedTask; - var transport = _context.Transport; + + // _adaptedTransport must be set prior to adding the connection to the manager in order + // to allow the connection to be aported prior to protocol selection. + _adaptedTransport = _context.Transport; var application = _context.Application; if (_context.ConnectionAdapters.Count > 0) { - adaptedPipeline = new AdaptedPipeline(transport, + adaptedPipeline = new AdaptedPipeline(_adaptedTransport, application, new Pipe(AdaptedInputPipeOptions), new Pipe(AdaptedOutputPipeOptions)); - transport = adaptedPipeline; + _adaptedTransport = adaptedPipeline; } - // _http1Connection must be initialized before adding the connection to the connection manager - CreateHttp1Connection(httpApplication, transport, application); - - // _http2Connection must be initialized before yielding control to the transport thread, - // to prevent a race condition where _http2Connection.Abort() is called just as - // _http2Connection is about to be initialized. - CreateHttp2Connection(httpApplication, transport, application); - // Do this before the first await so we don't yield control to the transport until we've // added the connection to the connection manager _context.ServiceContext.ConnectionManager.AddConnection(_context.HttpConnectionId, this); _lastTimestamp = _context.ServiceContext.SystemClock.UtcNow.Ticks; - _http1Connection.ConnectionFeatures.Set(this); - _http2Connection.ConnectionFeatures.Set(this); + _context.ConnectionFeatures.Set(this); if (adaptedPipeline != null) { @@ -139,21 +133,41 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal adaptedPipelineTask = adaptedPipeline.RunAsync(stream); } - var protocol = SelectProtocol(); + IRequestProcessor requestProcessor = null; - if (protocol == HttpProtocols.None) + lock (_protocolSelectionLock) { - Abort(ex: null); + // Ensure that the connection hasn't already been stopped. + if (_requestProcessor == this) + { + switch (SelectProtocol()) + { + case HttpProtocols.Http1: + // _http1Connection must be initialized before adding the connection to the connection manager + requestProcessor = _http1Connection = CreateHttp1Connection(_adaptedTransport, application); + break; + case HttpProtocols.Http2: + // _http2Connection must be initialized before yielding control to the transport thread, + // to prevent a race condition where _http2Connection.Abort() is called just as + // _http2Connection is about to be initialized. + requestProcessor = CreateHttp2Connection(_adaptedTransport, application); + break; + case HttpProtocols.None: + // An error was already logged in SelectProtocol(), but we should close the connection. + Abort(ex: null); + break; + default: + // SelectProtocol() only returns Http1, Http2 or None. + throw new NotSupportedException($"{nameof(SelectProtocol)} returned something other than Http1, Http2 or None."); + } + + _requestProcessor = requestProcessor; + } } - // One of these has to run even if no protocol was selected so the abort propagates and everything completes properly - if (protocol == HttpProtocols.Http2 && Interlocked.CompareExchange(ref _http2ConnectionState, Http2ConnectionStarted, Http2ConnectionNotStarted) == Http2ConnectionNotStarted) + if (requestProcessor != null) { - await _http2Connection.ProcessAsync(httpApplication); - } - else - { - await _http1Connection.ProcessRequestsAsync(); + await requestProcessor.ProcessRequestsAsync(httpApplication); } await adaptedPipelineTask; @@ -161,14 +175,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal } catch (Exception ex) { - Log.LogError(0, ex, $"Unexpected exception in {nameof(HttpConnection)}.{nameof(ProcessRequestsAsync)}."); + Log.LogCritical(0, ex, $"Unexpected exception in {nameof(HttpConnection)}.{nameof(ProcessRequestsAsync)}."); } finally { _context.ServiceContext.ConnectionManager.RemoveConnection(_context.HttpConnectionId); DisposeAdaptedConnections(); - if (_http1Connection.IsUpgraded) + if (_http1Connection?.IsUpgraded == true) { _context.ServiceContext.ConnectionManager.UpgradedConnectionCount.ReleaseOne(); } @@ -177,9 +191,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal } } - internal void CreateHttp1Connection(IHttpApplication httpApplication, IPipeConnection transport, IPipeConnection application) + // For testing only + internal void Initialize(IPipeConnection transport, IPipeConnection application) { - _http1Connection = new Http1Connection(httpApplication, new Http1ConnectionContext + _requestProcessor = _http1Connection = CreateHttp1Connection(transport, application); + } + + private Http1Connection CreateHttp1Connection(IPipeConnection transport, IPipeConnection application) + { + return new Http1Connection(new Http1ConnectionContext { ConnectionId = _context.ConnectionId, ConnectionFeatures = _context.ConnectionFeatures, @@ -193,9 +213,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal }); } - internal void CreateHttp2Connection(IHttpApplication httpApplication, IPipeConnection transport, IPipeConnection application) + private Http2Connection CreateHttp2Connection(IPipeConnection transport, IPipeConnection application) { - _http2Connection = new Http2Connection(new Http2ConnectionContext + return new Http2Connection(new Http2ConnectionContext { ConnectionId = _context.ConnectionId, ServiceContext = _context.ServiceContext, @@ -217,16 +237,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal public Task StopProcessingNextRequestAsync() { - Debug.Assert(_http1Connection != null, $"{nameof(_http1Connection)} is null"); - Debug.Assert(_http2Connection != null, $"{nameof(_http2Connection)} is null"); - - if (Interlocked.Exchange(ref _http2ConnectionState, Http2ConnectionClosed) == Http2ConnectionStarted) + lock (_protocolSelectionLock) { - _http2Connection.Stop(); - } - else - { - _http1Connection.StopProcessingNextRequest(); + _requestProcessor?.StopProcessingNextRequest(); + _requestProcessor = null; } return _lifetimeTask; @@ -234,17 +248,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal public void Abort(Exception ex) { - Debug.Assert(_http1Connection != null, $"{nameof(_http1Connection)} is null"); - Debug.Assert(_http2Connection != null, $"{nameof(_http2Connection)} is null"); - - // Abort the connection (if not already aborted) - if (Interlocked.Exchange(ref _http2ConnectionState, Http2ConnectionClosed) == Http2ConnectionStarted) + lock (_protocolSelectionLock) { - _http2Connection.Abort(ex); - } - else - { - _http1Connection.Abort(ex); + _requestProcessor?.Abort(ex); + _requestProcessor = null; } } @@ -255,28 +262,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal return _lifetimeTask; } - public void SendTimeoutResponse() - { - Debug.Assert(_http1Connection != null, $"{nameof(_http1Connection)} is null"); - Debug.Assert(_http2Connection != null, $"{nameof(_http2Connection)} is null"); - - RequestTimedOut = true; - _http1Connection.SendTimeoutResponse(); - } - - public void StopProcessingNextRequest() - { - Debug.Assert(_http1Connection != null, $"{nameof(_http1Connection)} is null"); - Debug.Assert(_http2Connection != null, $"{nameof(_http2Connection)} is null"); - - _http1Connection.StopProcessingNextRequest(); - } - private async Task ApplyConnectionAdaptersAsync() { - Debug.Assert(_http1Connection != null, $"{nameof(_http1Connection)} is null"); - Debug.Assert(_http2Connection != null, $"{nameof(_http2Connection)} is null"); - var connectionAdapters = _context.ConnectionAdapters; var stream = new RawStream(_context.Transport.Input, _context.Transport.Output); var adapterContext = new ConnectionAdapterContext(_context.ConnectionFeatures, stream); @@ -348,14 +335,16 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal public void Tick(DateTimeOffset now) { - Debug.Assert(_http1Connection != null, $"{nameof(_http1Connection)} is null"); - Debug.Assert(_http2Connection != null, $"{nameof(_http2Connection)} is null"); - var timestamp = now.Ticks; CheckForTimeout(timestamp); - CheckForReadDataRateTimeout(timestamp); - CheckForWriteDataRateTimeout(timestamp); + + // HTTP/2 rate timeouts are not yet supported. + if (_http1Connection != null) + { + CheckForReadDataRateTimeout(timestamp); + CheckForWriteDataRateTimeout(timestamp); + } Interlocked.Exchange(ref _lastTimestamp, timestamp); } @@ -372,12 +361,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal switch (_timeoutAction) { case TimeoutAction.StopProcessingNextRequest: - StopProcessingNextRequest(); + // Http/2 keep-alive timeouts are not yet supported. + _http1Connection?.StopProcessingNextRequest(); break; case TimeoutAction.SendTimeoutResponse: - SendTimeoutResponse(); + // HTTP/2 timeout responses are not yet supported. + if (_http1Connection != null) + { + RequestTimedOut = true; + _http1Connection.SendTimeoutResponse(); + } break; case TimeoutAction.AbortConnection: + // This is actually supported with HTTP/2! Abort(new TimeoutException()); break; } @@ -387,6 +383,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal private void CheckForReadDataRateTimeout(long timestamp) { + Debug.Assert(_http1Connection != null); + // The only time when both a timeout is set and the read data rate could be enforced is // when draining the request body. Since there's already a (short) timeout set for draining, // it's safe to not check the data rate at this point. @@ -412,7 +410,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal if (rate < minRequestBodyDataRate.BytesPerSecond && !Debugger.IsAttached) { Log.RequestBodyMininumDataRateNotSatisfied(_context.ConnectionId, _http1Connection.TraceIdentifier, minRequestBodyDataRate.BytesPerSecond); - SendTimeoutResponse(); + RequestTimedOut = true; + _http1Connection.SendTimeoutResponse(); } } @@ -430,6 +429,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal private void CheckForWriteDataRateTimeout(long timestamp) { + Debug.Assert(_http1Connection != null); + lock (_writeTimingLock) { if (_writeTimingWrites > 0 && timestamp > _writeTimingTimeoutTimestamp && !Debugger.IsAttached) @@ -510,6 +511,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal public void StartTimingWrite(long size) { + Debug.Assert(_http1Connection != null); + lock (_writeTimingLock) { var minResponseDataRate = _http1Connection.MinResponseDataRate; @@ -563,5 +566,33 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal ResetTimeout(timeSpan.Ticks, TimeoutAction.AbortConnection); } + + private void CloseUninitializedConnection() + { + Debug.Assert(_adaptedTransport != null); + + // CancelPendingRead signals the transport directly to close the connection + // without any potential interference from connection adapters. + _context.Application.Input.CancelPendingRead(); + + _adaptedTransport.Input.Complete(); + _adaptedTransport.Output.Complete(); + } + + // These IStoppableConnection methods only get called if the server shuts down during initialization. + Task IRequestProcessor.ProcessRequestsAsync(IHttpApplication application) + { + throw new NotSupportedException(); + } + + void IRequestProcessor.StopProcessingNextRequest() + { + CloseUninitializedConnection(); + } + + void IRequestProcessor.Abort(Exception ex) + { + CloseUninitializedConnection(); + } } } diff --git a/src/Kestrel.Core/Internal/IRequestProcessor.cs b/src/Kestrel.Core/Internal/IRequestProcessor.cs new file mode 100644 index 0000000000..5b40735ac8 --- /dev/null +++ b/src/Kestrel.Core/Internal/IRequestProcessor.cs @@ -0,0 +1,16 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Hosting.Server; + +namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal +{ + public interface IRequestProcessor + { + Task ProcessRequestsAsync(IHttpApplication application); + void StopProcessingNextRequest(); + void Abort(Exception ex); + } +} \ No newline at end of file diff --git a/test/Kestrel.Core.Tests/Http1ConnectionTests.cs b/test/Kestrel.Core.Tests/Http1ConnectionTests.cs index 0601a12763..1efca25551 100644 --- a/test/Kestrel.Core.Tests/Http1ConnectionTests.cs +++ b/test/Kestrel.Core.Tests/Http1ConnectionTests.cs @@ -11,7 +11,6 @@ using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; -using Microsoft.AspNetCore.Hosting.Server; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Server.Kestrel.Core.Features; @@ -30,7 +29,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests { private readonly IPipeConnection _transport; private readonly IPipeConnection _application; - private readonly TestHttp1Connection _http1Connection; + private readonly TestHttp1Connection _http1Connection; private readonly ServiceContext _serviceContext; private readonly Http1ConnectionContext _http1ConnectionContext; private readonly BufferPool _pipelineFactory; @@ -38,19 +37,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests private ReadCursor _examined; private Mock _timeoutControl; - private class TestHttp1Connection : Http1Connection - { - public TestHttp1Connection(IHttpApplication application, Http1ConnectionContext context) - : base(application, context) - { - } - - public Task ProduceEndAsync() - { - return ProduceEnd(); - } - } - public Http1ConnectionTests() { _pipelineFactory = new MemoryPool(); @@ -71,7 +57,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests Transport = pair.Transport }; - _http1Connection = new TestHttp1Connection(application: null, context: _http1ConnectionContext); + _http1Connection = new TestHttp1Connection(_http1ConnectionContext); _http1Connection.Reset(); } @@ -509,7 +495,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests [Fact] public void ProcessRequestsAsyncEnablesKeepAliveTimeout() { - var requestProcessingTask = _http1Connection.ProcessRequestsAsync(); + var requestProcessingTask = _http1Connection.ProcessRequestsAsync(null); var expectedKeepAliveTimeout = _serviceContext.ServerOptions.Limits.KeepAliveTimeout.Ticks; _timeoutControl.Verify(cc => cc.SetTimeout(expectedKeepAliveTimeout, TimeoutAction.StopProcessingNextRequest)); @@ -594,7 +580,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests [Fact] public async Task RequestProcessingTaskIsUnwrapped() { - var requestProcessingTask = _http1Connection.ProcessRequestsAsync(); + var requestProcessingTask = _http1Connection.ProcessRequestsAsync(null); var data = Encoding.ASCII.GetBytes("GET / HTTP/1.1\r\nHost:\r\n\r\n"); await _application.Output.WriteAsync(data); @@ -723,7 +709,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests var headers0 = MakeHeaders(header0Count); var headers1 = MakeHeaders(header1Count, header0Count); - var requestProcessingTask = _http1Connection.ProcessRequestsAsync(); + var requestProcessingTask = _http1Connection.ProcessRequestsAsync(null); await _application.Output.WriteAsync(Encoding.ASCII.GetBytes("GET / HTTP/1.0\r\n")); await WaitForCondition(TimeSpan.FromSeconds(1), () => _http1Connection.RequestHeaders != null); @@ -757,7 +743,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests var headers0 = MakeHeaders(header0Count); var headers1 = MakeHeaders(header1Count, header0Count); - var requestProcessingTask = _http1Connection.ProcessRequestsAsync(); + var requestProcessingTask = _http1Connection.ProcessRequestsAsync(null); await _application.Output.WriteAsync(Encoding.ASCII.GetBytes("GET / HTTP/1.0\r\n")); await WaitForCondition(TimeSpan.FromSeconds(1), () => _http1Connection.RequestHeaders != null); diff --git a/test/Kestrel.Core.Tests/Http2ConnectionTests.cs b/test/Kestrel.Core.Tests/Http2ConnectionTests.cs index 69a7807ab0..3f6d9a2a9c 100644 --- a/test/Kestrel.Core.Tests/Http2ConnectionTests.cs +++ b/test/Kestrel.Core.Tests/Http2ConnectionTests.cs @@ -1987,7 +1987,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests private async Task InitializeConnectionAsync(RequestDelegate application) { - _connectionTask = _connection.ProcessAsync(new DummyApplication(application)); + _connectionTask = _connection.ProcessRequestsAsync(new DummyApplication(application)); await SendPreambleAsync().ConfigureAwait(false); await SendSettingsAsync(); diff --git a/test/Kestrel.Core.Tests/HttpConnectionTests.cs b/test/Kestrel.Core.Tests/HttpConnectionTests.cs index 3a4e43cc44..dffd274f75 100644 --- a/test/Kestrel.Core.Tests/HttpConnectionTests.cs +++ b/test/Kestrel.Core.Tests/HttpConnectionTests.cs @@ -56,8 +56,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests var mockDebugger = new Mock(); mockDebugger.SetupGet(g => g.IsAttached).Returns(true); _httpConnection.Debugger = mockDebugger.Object; - _httpConnection.CreateHttp1Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application); - _httpConnection.CreateHttp2Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application); + _httpConnection.Initialize(_httpConnectionContext.Transport, _httpConnectionContext.Application); var now = DateTimeOffset.Now; _httpConnection.Tick(now); @@ -104,8 +103,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests _httpConnectionContext.ServiceContext.Log = logger; - _httpConnection.CreateHttp1Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application); - _httpConnection.CreateHttp2Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application); + _httpConnection.Initialize(_httpConnectionContext.Transport, _httpConnectionContext.Application); _httpConnection.Http1Connection.Reset(); // Initialize timestamp @@ -132,8 +130,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests var mockLogger = new Mock(); _httpConnectionContext.ServiceContext.Log = mockLogger.Object; - _httpConnection.CreateHttp1Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application); - _httpConnection.CreateHttp2Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application); + _httpConnection.Initialize(_httpConnectionContext.Transport, _httpConnectionContext.Application); _httpConnection.Http1Connection.Reset(); // Initialize timestamp @@ -175,8 +172,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests var mockLogger = new Mock(); _httpConnectionContext.ServiceContext.Log = mockLogger.Object; - _httpConnection.CreateHttp1Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application); - _httpConnection.CreateHttp2Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application); + _httpConnection.Initialize(_httpConnectionContext.Transport, _httpConnectionContext.Application); _httpConnection.Http1Connection.Reset(); // Initialize timestamp @@ -253,8 +249,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests var mockLogger = new Mock(); _httpConnectionContext.ServiceContext.Log = mockLogger.Object; - _httpConnection.CreateHttp1Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application); - _httpConnection.CreateHttp2Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application); + _httpConnection.Initialize(_httpConnectionContext.Transport, _httpConnectionContext.Application); _httpConnection.Http1Connection.Reset(); // Initialize timestamp @@ -322,8 +317,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests var mockLogger = new Mock(); _httpConnectionContext.ServiceContext.Log = mockLogger.Object; - _httpConnection.CreateHttp1Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application); - _httpConnection.CreateHttp2Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application); + _httpConnection.Initialize(_httpConnectionContext.Transport, _httpConnectionContext.Application); _httpConnection.Http1Connection.Reset(); // Initialize timestamp @@ -385,8 +379,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests var mockLogger = new Mock(); _httpConnectionContext.ServiceContext.Log = mockLogger.Object; - _httpConnection.CreateHttp1Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application); - _httpConnection.CreateHttp2Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application); + _httpConnection.Initialize(_httpConnectionContext.Transport, _httpConnectionContext.Application); _httpConnection.Http1Connection.Reset(); var startTime = systemClock.UtcNow; @@ -427,8 +420,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests var mockLogger = new Mock(); _httpConnectionContext.ServiceContext.Log = mockLogger.Object; - _httpConnection.CreateHttp1Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application); - _httpConnection.CreateHttp2Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application); + _httpConnection.Initialize(_httpConnectionContext.Transport, _httpConnectionContext.Application); _httpConnection.Http1Connection.Reset(); _httpConnection.Http1Connection.RequestAborted.Register(() => { @@ -462,8 +454,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests var mockLogger = new Mock(); _httpConnectionContext.ServiceContext.Log = mockLogger.Object; - _httpConnection.CreateHttp1Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application); - _httpConnection.CreateHttp2Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application); + _httpConnection.Initialize(_httpConnectionContext.Transport, _httpConnectionContext.Application); _httpConnection.Http1Connection.Reset(); _httpConnection.Http1Connection.RequestAborted.Register(() => { @@ -505,8 +496,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests var mockLogger = new Mock(); _httpConnectionContext.ServiceContext.Log = mockLogger.Object; - _httpConnection.CreateHttp1Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application); - _httpConnection.CreateHttp2Connection(new DummyApplication(), _httpConnectionContext.Transport, _httpConnectionContext.Application); + _httpConnection.Initialize(_httpConnectionContext.Transport, _httpConnectionContext.Application); _httpConnection.Http1Connection.Reset(); _httpConnection.Http1Connection.RequestAborted.Register(() => { diff --git a/test/Kestrel.Core.Tests/HttpResponseHeadersTests.cs b/test/Kestrel.Core.Tests/HttpResponseHeadersTests.cs index 46c28a3633..b98a47c8e5 100644 --- a/test/Kestrel.Core.Tests/HttpResponseHeadersTests.cs +++ b/test/Kestrel.Core.Tests/HttpResponseHeadersTests.cs @@ -33,7 +33,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests TimeoutControl = null }; - var http1Connection = new Http1Connection(application: null, context: http1ConnectionContext); + var http1Connection = new Http1Connection(http1ConnectionContext); http1Connection.Reset(); diff --git a/test/Kestrel.Core.Tests/TestInput.cs b/test/Kestrel.Core.Tests/TestInput.cs index e9c2cdf701..76276f3858 100644 --- a/test/Kestrel.Core.Tests/TestInput.cs +++ b/test/Kestrel.Core.Tests/TestInput.cs @@ -34,7 +34,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests TimeoutControl = Mock.Of() }; - Http1Connection = new Http1Connection(null, Http1ConnectionContext); + Http1Connection = new Http1Connection(Http1ConnectionContext); Http1Connection.HttpResponseControl = Mock.Of(); } diff --git a/test/Kestrel.Transport.Libuv.Tests/LibuvOutputConsumerTests.cs b/test/Kestrel.Transport.Libuv.Tests/LibuvOutputConsumerTests.cs index 54dd7ed013..da69f195ab 100644 --- a/test/Kestrel.Transport.Libuv.Tests/LibuvOutputConsumerTests.cs +++ b/test/Kestrel.Transport.Libuv.Tests/LibuvOutputConsumerTests.cs @@ -708,7 +708,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.Tests var socket = new MockSocket(_mockLibuv, _libuvThread.Loop.ThreadId, transportContext.Log); var consumer = new LibuvOutputConsumer(pair.Application.Input, _libuvThread, socket, "0", transportContext.Log); - var http1Connection = new Http1Connection(null, new Http1ConnectionContext + var http1Connection = new Http1Connection(new Http1ConnectionContext { ServiceContext = serviceContext, ConnectionFeatures = new FeatureCollection(), diff --git a/test/shared/TestHttp1Connection.cs b/test/shared/TestHttp1Connection.cs index 8888a468be..d98f2e91e0 100644 --- a/test/shared/TestHttp1Connection.cs +++ b/test/shared/TestHttp1Connection.cs @@ -2,16 +2,14 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Threading.Tasks; -using Microsoft.AspNetCore.Hosting.Server; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; -using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; namespace Microsoft.AspNetCore.Testing { - public class TestHttp1Connection : Http1Connection + public class TestHttp1Connection : Http1Connection { - public TestHttp1Connection(IHttpApplication application, Http1ConnectionContext context) - : base(application, context) + public TestHttp1Connection(Http1ConnectionContext context) + : base(context) { }