diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs index 03c2a619b9..40c5c16331 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Connection.cs @@ -185,7 +185,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http private Frame CreateFrame() { - return new Frame(this, _remoteEndPoint, _localEndPoint, _filterContext?.PrepareRequest); + return FrameFactory(this, _remoteEndPoint, _localEndPoint, _filterContext?.PrepareRequest); } void IConnectionControl.Pause() diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs index 13b16b5634..98dee5304c 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Frame.cs @@ -19,7 +19,7 @@ using Microsoft.Extensions.Primitives; namespace Microsoft.AspNet.Server.Kestrel.Http { - public partial class Frame : FrameContext, IFrameControl + public abstract partial class Frame : FrameContext, IFrameControl { private static readonly Encoding _ascii = Encoding.ASCII; private static readonly ArraySegment _endChunkBytes = CreateAsciiByteArraySegment("\r\n"); @@ -41,7 +41,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http private readonly object _onStartingSync = new Object(); private readonly object _onCompletedSync = new Object(); - private readonly FrameRequestHeaders _requestHeaders = new FrameRequestHeaders(); + protected readonly FrameRequestHeaders _requestHeaders = new FrameRequestHeaders(); private readonly FrameResponseHeaders _responseHeaders = new FrameResponseHeaders(); private List, object>> _onStarting; @@ -50,18 +50,18 @@ namespace Microsoft.AspNet.Server.Kestrel.Http private bool _requestProcessingStarted; private Task _requestProcessingTask; - private volatile bool _requestProcessingStopping; // volatile, see: https://msdn.microsoft.com/en-us/library/x13ttww7.aspx - private volatile bool _requestAborted; - private CancellationTokenSource _abortedCts; - private CancellationToken? _manuallySetRequestAbortToken; + protected volatile bool _requestProcessingStopping; // volatile, see: https://msdn.microsoft.com/en-us/library/x13ttww7.aspx + protected volatile bool _requestAborted; + protected CancellationTokenSource _abortedCts; + protected CancellationToken? _manuallySetRequestAbortToken; - private FrameRequestStream _requestBody; - private FrameResponseStream _responseBody; + internal FrameRequestStream _requestBody; + internal FrameResponseStream _responseBody; - private bool _responseStarted; - private bool _keepAlive; + protected bool _responseStarted; + protected bool _keepAlive; private bool _autoChunk; - private Exception _applicationException; + protected Exception _applicationException; private HttpVersionType _httpVersion; @@ -306,119 +306,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http /// The resulting Task from this loop is preserved in a field which is used when the server needs /// to drain and close all currently active connections. /// - public async Task RequestProcessingAsync() - { - try - { - var terminated = false; - while (!terminated && !_requestProcessingStopping) - { - while (!terminated && !_requestProcessingStopping && !TakeStartLine(SocketInput)) - { - terminated = SocketInput.RemoteIntakeFin; - if (!terminated) - { - await SocketInput; - } - } - - while (!terminated && !_requestProcessingStopping && !TakeMessageHeaders(SocketInput, _requestHeaders)) - { - terminated = SocketInput.RemoteIntakeFin; - if (!terminated) - { - await SocketInput; - } - } - - if (!terminated && !_requestProcessingStopping) - { - var messageBody = MessageBody.For(HttpVersion, _requestHeaders, this); - _keepAlive = messageBody.RequestKeepAlive; - _requestBody = new FrameRequestStream(messageBody); - RequestBody = _requestBody; - _responseBody = new FrameResponseStream(this); - ResponseBody = _responseBody; - DuplexStream = new FrameDuplexStream(RequestBody, ResponseBody); - - _abortedCts = null; - _manuallySetRequestAbortToken = null; - - var httpContext = HttpContextFactory.Create(this); - try - { - await Application.Invoke(httpContext).ConfigureAwait(false); - } - catch (Exception ex) - { - ReportApplicationError(ex); - } - finally - { - // Trigger OnStarting if it hasn't been called yet and the app hasn't - // already failed. If an OnStarting callback throws we can go through - // our normal error handling in ProduceEnd. - // https://github.com/aspnet/KestrelHttpServer/issues/43 - if (!_responseStarted && _applicationException == null) - { - await FireOnStarting(); - } - - await FireOnCompleted(); - - HttpContextFactory.Dispose(httpContext); - - // If _requestAbort is set, the connection has already been closed. - if (!_requestAborted) - { - await ProduceEnd(); - - if (_keepAlive) - { - // Finish reading the request body in case the app did not. - await messageBody.Consume(); - } - } - - _requestBody.StopAcceptingReads(); - _responseBody.StopAcceptingWrites(); - } - - terminated = !_keepAlive; - } - - Reset(); - } - } - catch (Exception ex) - { - Log.LogWarning("Connection processing ended abnormally", ex); - } - finally - { - try - { - _abortedCts = null; - - // If _requestAborted is set, the connection has already been closed. - if (!_requestAborted) - { - // Inform client no more data will ever arrive - ConnectionControl.End(ProduceEndType.SocketShutdownSend); - - // Wait for client to either disconnect or send unexpected data - await SocketInput; - - // Dispose socket - ConnectionControl.End(ProduceEndType.SocketDisconnect); - } - } - catch (Exception ex) - { - Log.LogWarning("Connection shutdown abnormally", ex); - } - } - } + public abstract Task RequestProcessingAsync(); public void OnStarting(Func callback, object state) { @@ -444,7 +332,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - private async Task FireOnStarting() + protected async Task FireOnStarting() { List, object>> onStarting = null; lock (_onStartingSync) @@ -468,7 +356,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - private async Task FireOnCompleted() + protected async Task FireOnCompleted() { List, object>> onCompleted = null; lock (_onCompletedSync) @@ -633,7 +521,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http return CreateResponseHeader(statusBytes, appCompleted, immediate); } - private async Task ProduceEnd() + protected async Task ProduceEnd() { if (_applicationException != null) { @@ -740,7 +628,7 @@ namespace Microsoft.AspNet.Server.Kestrel.Http } } - private bool TakeStartLine(SocketInput input) + protected bool TakeStartLine(SocketInput input) { var scan = input.ConsumingStart(); var consumed = scan; @@ -977,9 +865,16 @@ namespace Microsoft.AspNet.Server.Kestrel.Http statusCode != 304; } - private void ReportApplicationError(Exception ex) + protected void ReportApplicationError(Exception ex) { - _applicationException = ex; + if (_applicationException == null) + { + _applicationException = ex; + } + else + { + _applicationException = new AggregateException(_applicationException, ex); + } Log.ApplicationError(ex); } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/FrameOfT.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameOfT.cs new file mode 100644 index 0000000000..941c75fe8e --- /dev/null +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/FrameOfT.cs @@ -0,0 +1,153 @@ +// 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.Net; +using System.Threading.Tasks; +using Microsoft.AspNet.Hosting.Server; +using Microsoft.AspNet.Http.Features; +using Microsoft.Extensions.Logging; + +namespace Microsoft.AspNet.Server.Kestrel.Http +{ + public class Frame : Frame + { + private readonly IHttpApplication _application; + + public Frame(IHttpApplication application, + ConnectionContext context) + : this(application, context, remoteEndPoint: null, localEndPoint: null, prepareRequest: null) + { + } + + public Frame(IHttpApplication application, + ConnectionContext context, + IPEndPoint remoteEndPoint, + IPEndPoint localEndPoint, + Action prepareRequest) + : base(context, remoteEndPoint, localEndPoint, prepareRequest) + { + _application = application; + } + + /// + /// Primary loop which consumes socket input, parses it for protocol framing, and invokes the + /// application delegate for as long as the socket is intended to remain open. + /// The resulting Task from this loop is preserved in a field which is used when the server needs + /// to drain and close all currently active connections. + /// + public override async Task RequestProcessingAsync() + { + try + { + var terminated = false; + while (!terminated && !_requestProcessingStopping) + { + while (!terminated && !_requestProcessingStopping && !TakeStartLine(SocketInput)) + { + terminated = SocketInput.RemoteIntakeFin; + if (!terminated) + { + await SocketInput; + } + } + + while (!terminated && !_requestProcessingStopping && !TakeMessageHeaders(SocketInput, _requestHeaders)) + { + terminated = SocketInput.RemoteIntakeFin; + if (!terminated) + { + await SocketInput; + } + } + + if (!terminated && !_requestProcessingStopping) + { + var messageBody = MessageBody.For(HttpVersion, _requestHeaders, this); + _keepAlive = messageBody.RequestKeepAlive; + _requestBody = new FrameRequestStream(messageBody); + RequestBody = _requestBody; + _responseBody = new FrameResponseStream(this); + ResponseBody = _responseBody; + DuplexStream = new FrameDuplexStream(RequestBody, ResponseBody); + + _abortedCts = null; + _manuallySetRequestAbortToken = null; + + var context = _application.CreateContext(this); + try + { + await _application.ProcessRequestAsync(context).ConfigureAwait(false); + } + catch (Exception ex) + { + ReportApplicationError(ex); + } + finally + { + // Trigger OnStarting if it hasn't been called yet and the app hasn't + // already failed. If an OnStarting callback throws we can go through + // our normal error handling in ProduceEnd. + // https://github.com/aspnet/KestrelHttpServer/issues/43 + if (!_responseStarted && _applicationException == null) + { + await FireOnStarting(); + } + + await FireOnCompleted(); + + _application.DisposeContext(context, _applicationException); + + // If _requestAbort is set, the connection has already been closed. + if (!_requestAborted) + { + await ProduceEnd(); + + if (_keepAlive) + { + // Finish reading the request body in case the app did not. + await messageBody.Consume(); + } + } + + _requestBody.StopAcceptingReads(); + _responseBody.StopAcceptingWrites(); + } + + terminated = !_keepAlive; + } + + Reset(); + } + } + catch (Exception ex) + { + Log.LogWarning("Connection processing ended abnormally", ex); + } + finally + { + try + { + _abortedCts = null; + + // If _requestAborted is set, the connection has already been closed. + if (!_requestAborted) + { + // Inform client no more data will ever arrive + ConnectionControl.End(ProduceEndType.SocketShutdownSend); + + // Wait for client to either disconnect or send unexpected data + await SocketInput; + + // Dispose socket + ConnectionControl.End(ProduceEndType.SocketDisconnect); + } + } + catch (Exception ex) + { + Log.LogWarning("Connection shutdown abnormally", ex); + } + } + } + } +} diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/Listener.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/Listener.cs index d1a80b7043..6721756266 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/Listener.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/Listener.cs @@ -23,12 +23,10 @@ namespace Microsoft.AspNet.Server.Kestrel.Http public Task StartAsync( ServerAddress address, - KestrelThread thread, - RequestDelegate application) + KestrelThread thread) { ServerAddress = address; Thread = thread; - Application = application; var tcs = new TaskCompletionSource(this); diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerContext.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerContext.cs index 5817200410..fecd0afa44 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerContext.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerContext.cs @@ -24,7 +24,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Http { ServerAddress = listenerContext.ServerAddress; Thread = listenerContext.Thread; - Application = listenerContext.Application; Memory2 = listenerContext.Memory2; Log = listenerContext.Log; } @@ -33,8 +32,6 @@ namespace Microsoft.AspNet.Server.Kestrel.Http public KestrelThread Thread { get; set; } - public RequestDelegate Application { get; set; } - public MemoryPool2 Memory2 { get; set; } } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerPrimary.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerPrimary.cs index c5b6321d4a..d3c344ccec 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerPrimary.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerPrimary.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using Microsoft.AspNet.Http; using Microsoft.AspNet.Server.Kestrel.Infrastructure; using Microsoft.AspNet.Server.Kestrel.Networking; using Microsoft.Extensions.Logging; @@ -34,12 +33,11 @@ namespace Microsoft.AspNet.Server.Kestrel.Http public async Task StartAsync( string pipeName, ServerAddress address, - KestrelThread thread, - RequestDelegate application) + KestrelThread thread) { _pipeName = pipeName; - await StartAsync(address, thread, application).ConfigureAwait(false); + await StartAsync(address, thread).ConfigureAwait(false); await Thread.PostAsync(_this => _this.PostCallback(), this).ConfigureAwait(false); diff --git a/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerSecondary.cs b/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerSecondary.cs index 0bc8fae781..1c6ca4aea8 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerSecondary.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/Http/ListenerSecondary.cs @@ -32,15 +32,13 @@ namespace Microsoft.AspNet.Server.Kestrel.Http public Task StartAsync( string pipeName, ServerAddress address, - KestrelThread thread, - RequestDelegate application) + KestrelThread thread) { _pipeName = pipeName; _buf = thread.Loop.Libuv.buf_init(_ptr, 4); ServerAddress = address; Thread = thread; - Application = application; DispatchPipe = new UvPipeHandle(Log); diff --git a/src/Microsoft.AspNet.Server.Kestrel/KestrelEngine.cs b/src/Microsoft.AspNet.Server.Kestrel/KestrelEngine.cs index b0150cad1d..d64f4316e9 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/KestrelEngine.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/KestrelEngine.cs @@ -48,7 +48,7 @@ namespace Microsoft.AspNet.Server.Kestrel Threads.Clear(); } - public IDisposable CreateServer(ServerAddress address, RequestDelegate application) + public IDisposable CreateServer(ServerAddress address) { var listeners = new List(); @@ -69,7 +69,7 @@ namespace Microsoft.AspNet.Server.Kestrel (Listener) new PipeListener(this) : new TcpListener(this); listeners.Add(listener); - listener.StartAsync(address, thread, application).Wait(); + listener.StartAsync(address, thread).Wait(); } else if (first) { @@ -78,7 +78,7 @@ namespace Microsoft.AspNet.Server.Kestrel : new TcpListenerPrimary(this); listeners.Add(listener); - listener.StartAsync(pipeName, address, thread, application).Wait(); + listener.StartAsync(pipeName, address, thread).Wait(); } else { @@ -86,7 +86,7 @@ namespace Microsoft.AspNet.Server.Kestrel ? (ListenerSecondary) new PipeListenerSecondary(this) : new TcpListenerSecondary(this); listeners.Add(listener); - listener.StartAsync(pipeName, address, thread, application).Wait(); + listener.StartAsync(pipeName, address, thread).Wait(); } first = false; diff --git a/src/Microsoft.AspNet.Server.Kestrel/KestrelServer.cs b/src/Microsoft.AspNet.Server.Kestrel/KestrelServer.cs index 346808ebc4..f1e3504d22 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/KestrelServer.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/KestrelServer.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using Microsoft.AspNet.Hosting; using Microsoft.AspNet.Hosting.Server; -using Microsoft.AspNet.Http; using Microsoft.AspNet.Http.Features; using Microsoft.AspNet.Server.Kestrel.Http; using Microsoft.Extensions.Logging; @@ -17,9 +16,8 @@ namespace Microsoft.AspNet.Server.Kestrel private Stack _disposables; private readonly IApplicationLifetime _applicationLifetime; private readonly ILogger _logger; - private readonly IHttpContextFactory _httpContextFactory; - public KestrelServer(IFeatureCollection features, IApplicationLifetime applicationLifetime, ILogger logger, IHttpContextFactory httpContextFactory) + public KestrelServer(IFeatureCollection features, IApplicationLifetime applicationLifetime, ILogger logger) { if (features == null) { @@ -36,20 +34,14 @@ namespace Microsoft.AspNet.Server.Kestrel throw new ArgumentNullException(nameof(logger)); } - if (httpContextFactory == null) - { - throw new ArgumentNullException(nameof(httpContextFactory)); - } - _applicationLifetime = applicationLifetime; _logger = logger; Features = features; - _httpContextFactory = httpContextFactory; } public IFeatureCollection Features { get; } - public void Start(RequestDelegate requestDelegate) + public void Start(IHttpApplication application) { if (_disposables != null) { @@ -64,9 +56,12 @@ namespace Microsoft.AspNet.Server.Kestrel var dateHeaderValueManager = new DateHeaderValueManager(); var engine = new KestrelEngine(new ServiceContext { + FrameFactory = (context, remoteEP, localEP, prepareRequest) => + { + return new Frame(application, context, remoteEP, localEP, prepareRequest); + }, AppLifetime = _applicationLifetime, Log = new KestrelTrace(_logger), - HttpContextFactory = _httpContextFactory, DateHeaderValueManager = dateHeaderValueManager, ConnectionFilter = information.ConnectionFilter, NoDelay = information.NoDelay @@ -119,8 +114,7 @@ namespace Microsoft.AspNet.Server.Kestrel { atLeastOneListener = true; _disposables.Push(engine.CreateServer( - parsedAddress, - requestDelegate)); + parsedAddress)); } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/ServerFactory.cs b/src/Microsoft.AspNet.Server.Kestrel/ServerFactory.cs index 158e089fee..ccde5797ce 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/ServerFactory.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/ServerFactory.cs @@ -3,7 +3,6 @@ using Microsoft.AspNet.Hosting; using Microsoft.AspNet.Hosting.Server; -using Microsoft.AspNet.Http; using Microsoft.AspNet.Http.Features; using Microsoft.AspNet.Server.Features; using Microsoft.Extensions.Configuration; @@ -18,13 +17,11 @@ namespace Microsoft.AspNet.Server.Kestrel { private readonly IApplicationLifetime _appLifetime; private readonly ILoggerFactory _loggerFactory; - private readonly IHttpContextFactory _httpContextFactory; - public ServerFactory(IApplicationLifetime appLifetime, ILoggerFactory loggerFactory, IHttpContextFactory httpContextFactory) + public ServerFactory(IApplicationLifetime appLifetime, ILoggerFactory loggerFactory) { _appLifetime = appLifetime; _loggerFactory = loggerFactory; - _httpContextFactory = httpContextFactory; } public IServer CreateServer(IConfiguration configuration) @@ -34,7 +31,7 @@ namespace Microsoft.AspNet.Server.Kestrel var serverFeatures = new FeatureCollection(); serverFeatures.Set(information); serverFeatures.Set(information); - return new KestrelServer(serverFeatures, _appLifetime, _loggerFactory.CreateLogger("Microsoft.AspNet.Server.Kestrel"), _httpContextFactory); + return new KestrelServer(serverFeatures, _appLifetime, _loggerFactory.CreateLogger("Microsoft.AspNet.Server.Kestrel")); } } } diff --git a/src/Microsoft.AspNet.Server.Kestrel/ServiceContext.cs b/src/Microsoft.AspNet.Server.Kestrel/ServiceContext.cs index 095f37f856..62d5d5d758 100644 --- a/src/Microsoft.AspNet.Server.Kestrel/ServiceContext.cs +++ b/src/Microsoft.AspNet.Server.Kestrel/ServiceContext.cs @@ -1,8 +1,10 @@ // 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.Net; using Microsoft.AspNet.Hosting; -using Microsoft.AspNet.Http; +using Microsoft.AspNet.Http.Features; using Microsoft.AspNet.Server.Kestrel.Filter; using Microsoft.AspNet.Server.Kestrel.Http; using Microsoft.AspNet.Server.Kestrel.Infrastructure; @@ -19,7 +21,7 @@ namespace Microsoft.AspNet.Server.Kestrel { AppLifetime = context.AppLifetime; Log = context.Log; - HttpContextFactory = context.HttpContextFactory; + FrameFactory = context.FrameFactory; DateHeaderValueManager = context.DateHeaderValueManager; ConnectionFilter = context.ConnectionFilter; NoDelay = context.NoDelay; @@ -29,7 +31,7 @@ namespace Microsoft.AspNet.Server.Kestrel public IKestrelTrace Log { get; set; } - public IHttpContextFactory HttpContextFactory { get; set; } + public Func, Frame> FrameFactory { get; set; } public DateHeaderValueManager DateHeaderValueManager { get; set; } diff --git a/test/Microsoft.AspNet.Server.KestrelTests/DummyApplication.cs b/test/Microsoft.AspNet.Server.KestrelTests/DummyApplication.cs new file mode 100644 index 0000000000..540b9d2f47 --- /dev/null +++ b/test/Microsoft.AspNet.Server.KestrelTests/DummyApplication.cs @@ -0,0 +1,37 @@ +// 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.AspNet.Hosting.Server; +using Microsoft.AspNet.Http; +using Microsoft.AspNet.Http.Internal; +using Microsoft.AspNet.Http.Features; + +namespace Microsoft.AspNet.Server.KestrelTests +{ + public class DummyApplication : IHttpApplication + { + private readonly RequestDelegate _requestDelegate; + + public DummyApplication(RequestDelegate requestDelegate) + { + _requestDelegate = requestDelegate; + } + + public HttpContext CreateContext(IFeatureCollection contextFeatures) + { + return new DefaultHttpContext(contextFeatures); + } + + public void DisposeContext(HttpContext context, Exception exception) + { + + } + + public async Task ProcessRequestAsync(HttpContext context) + { + await _requestDelegate(context); + } + } +} diff --git a/test/Microsoft.AspNet.Server.KestrelTests/EngineTests.cs b/test/Microsoft.AspNet.Server.KestrelTests/EngineTests.cs index afc30af8a9..b0a8c10b51 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/EngineTests.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/EngineTests.cs @@ -12,6 +12,7 @@ using Microsoft.AspNet.Http; using Microsoft.AspNet.Http.Features; using Microsoft.AspNet.Server.Kestrel; using Microsoft.AspNet.Server.Kestrel.Filter; +using Microsoft.AspNet.Server.Kestrel.Http; using Microsoft.AspNet.Testing.xunit; using Microsoft.Extensions.Logging; using Xunit; @@ -91,12 +92,13 @@ namespace Microsoft.AspNet.Server.KestrelTests [ConditionalTheory] [MemberData(nameof(ConnectionFilterData))] [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")] - public void ListenerCanCreateAndDispose(ServiceContext testContext) + public void ListenerCanCreateAndDispose(TestServiceContext testContext) { + testContext.App = App; var engine = new KestrelEngine(testContext); engine.Start(1); var address = ServerAddress.FromUrl("http://localhost:54321/"); - var started = engine.CreateServer(address, App); + var started = engine.CreateServer(address); started.Dispose(); engine.Dispose(); } @@ -104,12 +106,13 @@ namespace Microsoft.AspNet.Server.KestrelTests [ConditionalTheory] [MemberData(nameof(ConnectionFilterData))] [FrameworkSkipCondition(RuntimeFrameworks.Mono, SkipReason = "Test hangs after execution on Mono.")] - public void ConnectionCanReadAndWrite(ServiceContext testContext) + public void ConnectionCanReadAndWrite(TestServiceContext testContext) { + testContext.App = App; var engine = new KestrelEngine(testContext); engine.Start(1); var address = ServerAddress.FromUrl("http://localhost:54321/"); - var started = engine.CreateServer(address, App); + var started = engine.CreateServer(address); Console.WriteLine("Started"); var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); diff --git a/test/Microsoft.AspNet.Server.KestrelTests/FrameFacts.cs b/test/Microsoft.AspNet.Server.KestrelTests/FrameFacts.cs index 9e3419797f..da1bc9058e 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/FrameFacts.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/FrameFacts.cs @@ -19,7 +19,7 @@ namespace Microsoft.AspNet.Server.KestrelTests DateHeaderValueManager = new DateHeaderValueManager(), ServerAddress = ServerAddress.FromUrl("http://localhost:5000") }; - var frame = new Frame(connectionContext); + var frame = new Frame(application: null, context: connectionContext); frame.Scheme = "https"; // Act diff --git a/test/Microsoft.AspNet.Server.KestrelTests/FrameResponseHeadersTests.cs b/test/Microsoft.AspNet.Server.KestrelTests/FrameResponseHeadersTests.cs index 8082e62ed7..1526c53b26 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/FrameResponseHeadersTests.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/FrameResponseHeadersTests.cs @@ -20,7 +20,7 @@ namespace Microsoft.AspNet.Server.KestrelTests DateHeaderValueManager = new DateHeaderValueManager(), ServerAddress = ServerAddress.FromUrl("http://localhost:5000") }; - var frame = new Frame(connectionContext); + var frame = new Frame(application: null, context: connectionContext); IDictionary headers = frame.ResponseHeaders; Assert.Equal(2, headers.Count); @@ -48,7 +48,7 @@ namespace Microsoft.AspNet.Server.KestrelTests DateHeaderValueManager = new DateHeaderValueManager(), ServerAddress = ServerAddress.FromUrl("http://localhost:5000") }; - var frame = new Frame(connectionContext); + var frame = new Frame(application: null, context: connectionContext); Assert.True(frame.ResponseHeaders.Count > 0); diff --git a/test/Microsoft.AspNet.Server.KestrelTests/TestServer.cs b/test/Microsoft.AspNet.Server.KestrelTests/TestServer.cs index 70f9fd1cb7..42efcb3793 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/TestServer.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/TestServer.cs @@ -4,6 +4,7 @@ using System; using Microsoft.AspNet.Http; using Microsoft.AspNet.Server.Kestrel; +using Microsoft.AspNet.Server.Kestrel.Http; namespace Microsoft.AspNet.Server.KestrelTests { @@ -32,11 +33,14 @@ namespace Microsoft.AspNet.Server.KestrelTests public void Create(RequestDelegate app, ServiceContext context, string serverAddress) { + context.FrameFactory = (connectionContext, remoteEP, localEP, prepareRequest) => + { + return new Frame(new DummyApplication(app), connectionContext, remoteEP, localEP, prepareRequest); + }; _engine = new KestrelEngine(context); _engine.Start(1); _server = _engine.CreateServer( - ServerAddress.FromUrl(serverAddress), - app); + ServerAddress.FromUrl(serverAddress)); } public void Dispose() diff --git a/test/Microsoft.AspNet.Server.KestrelTests/TestServiceContext.cs b/test/Microsoft.AspNet.Server.KestrelTests/TestServiceContext.cs index 3badc340b7..9686915699 100644 --- a/test/Microsoft.AspNet.Server.KestrelTests/TestServiceContext.cs +++ b/test/Microsoft.AspNet.Server.KestrelTests/TestServiceContext.cs @@ -1,19 +1,37 @@ // 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 Microsoft.AspNet.Http.Internal; +using Microsoft.AspNet.Http; using Microsoft.AspNet.Server.Kestrel; +using Microsoft.AspNet.Server.Kestrel.Http; namespace Microsoft.AspNet.Server.KestrelTests { public class TestServiceContext : ServiceContext { + private RequestDelegate _app; + public TestServiceContext() { AppLifetime = new LifetimeNotImplemented(); Log = new TestKestrelTrace(); - HttpContextFactory = new HttpContextFactory(new HttpContextAccessor()); DateHeaderValueManager = new TestDateHeaderValueManager(); } + + public RequestDelegate App + { + get + { + return _app; + } + set + { + _app = value; + FrameFactory = (connectionContext, remoteEP, localEP, prepareRequest) => + { + return new Frame(new DummyApplication(_app), connectionContext, remoteEP, localEP, prepareRequest); + }; + } + } } }