diff --git a/src/Hosting/Hosting/ref/Microsoft.AspNetCore.Hosting.netcoreapp3.0.cs b/src/Hosting/Hosting/ref/Microsoft.AspNetCore.Hosting.netcoreapp3.0.cs index 0ecc1a7c3b..5a17de6ee4 100644 --- a/src/Hosting/Hosting/ref/Microsoft.AspNetCore.Hosting.netcoreapp3.0.cs +++ b/src/Hosting/Hosting/ref/Microsoft.AspNetCore.Hosting.netcoreapp3.0.cs @@ -85,6 +85,15 @@ namespace Microsoft.AspNetCore.Hosting.StaticWebAssets public static void UseStaticWebAssets(Microsoft.AspNetCore.Hosting.IWebHostEnvironment environment, Microsoft.Extensions.Configuration.IConfiguration configuration) { } } } +namespace Microsoft.AspNetCore.Http +{ + public partial class DefaultHttpContextFactory : Microsoft.AspNetCore.Http.IHttpContextFactory + { + public DefaultHttpContextFactory(System.IServiceProvider serviceProvider) { } + public Microsoft.AspNetCore.Http.HttpContext Create(Microsoft.AspNetCore.Http.Features.IFeatureCollection featureCollection) { throw null; } + public void Dispose(Microsoft.AspNetCore.Http.HttpContext httpContext) { } + } +} namespace Microsoft.Extensions.Hosting { public static partial class GenericHostWebHostBuilderExtensions diff --git a/src/Http/Http/src/DefaultHttpContextFactory.cs b/src/Hosting/Hosting/src/Http/DefaultHttpContextFactory.cs similarity index 65% rename from src/Http/Http/src/DefaultHttpContextFactory.cs rename to src/Hosting/Hosting/src/Http/DefaultHttpContextFactory.cs index fccec7d739..08ecd9b057 100644 --- a/src/Http/Http/src/DefaultHttpContextFactory.cs +++ b/src/Hosting/Hosting/src/Http/DefaultHttpContextFactory.cs @@ -2,6 +2,8 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; using Microsoft.AspNetCore.Http.Features; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; @@ -26,12 +28,30 @@ namespace Microsoft.AspNetCore.Http public HttpContext Create(IFeatureCollection featureCollection) { - if (featureCollection == null) + if (featureCollection is null) { throw new ArgumentNullException(nameof(featureCollection)); } - var httpContext = CreateHttpContext(featureCollection); + var httpContext = new DefaultHttpContext(featureCollection); + Initialize(httpContext); + return httpContext; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void Initialize(DefaultHttpContext httpContext, IFeatureCollection featureCollection) + { + Debug.Assert(featureCollection != null); + Debug.Assert(httpContext != null); + + httpContext.Initialize(featureCollection); + + Initialize(httpContext); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private DefaultHttpContext Initialize(DefaultHttpContext httpContext) + { if (_httpContextAccessor != null) { _httpContextAccessor.HttpContext = httpContext; @@ -43,16 +63,6 @@ namespace Microsoft.AspNetCore.Http return httpContext; } - private static DefaultHttpContext CreateHttpContext(IFeatureCollection featureCollection) - { - if (featureCollection is IDefaultHttpContextContainer container) - { - return container.HttpContext; - } - - return new DefaultHttpContext(featureCollection); - } - public void Dispose(HttpContext httpContext) { if (_httpContextAccessor != null) @@ -60,5 +70,15 @@ namespace Microsoft.AspNetCore.Http _httpContextAccessor.HttpContext = null; } } + + internal void Dispose(DefaultHttpContext httpContext) + { + if (_httpContextAccessor != null) + { + _httpContextAccessor.HttpContext = null; + } + + httpContext.Uninitialize(); + } } } diff --git a/src/Hosting/Hosting/src/Internal/HostingApplication.cs b/src/Hosting/Hosting/src/Internal/HostingApplication.cs index 48e846fe10..c64426c0db 100644 --- a/src/Hosting/Hosting/src/Internal/HostingApplication.cs +++ b/src/Hosting/Hosting/src/Internal/HostingApplication.cs @@ -5,6 +5,7 @@ using System; using System.Diagnostics; using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting.Server; +using Microsoft.AspNetCore.Hosting.Server.Abstractions; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; using Microsoft.Extensions.Logging; @@ -15,6 +16,7 @@ namespace Microsoft.AspNetCore.Hosting { private readonly RequestDelegate _application; private readonly IHttpContextFactory _httpContextFactory; + private readonly DefaultHttpContextFactory _defaultHttpContextFactory; private HostingApplicationDiagnostics _diagnostics; public HostingApplication( @@ -25,19 +27,58 @@ namespace Microsoft.AspNetCore.Hosting { _application = application; _diagnostics = new HostingApplicationDiagnostics(logger, diagnosticSource); - _httpContextFactory = httpContextFactory; + if (httpContextFactory is DefaultHttpContextFactory factory) + { + _defaultHttpContextFactory = factory; + } + else + { + _httpContextFactory = httpContextFactory; + } } // Set up the request public Context CreateContext(IFeatureCollection contextFeatures) { - var context = new Context(); - var httpContext = _httpContextFactory.Create(contextFeatures); + Context hostContext; + if (contextFeatures is IHostContextContainer container) + { + hostContext = container.HostContext; + if (hostContext is null) + { + hostContext = new Context(); + container.HostContext = hostContext; + } + } + else + { + // Server doesn't support pooling, so create a new Context + hostContext = new Context(); + } - _diagnostics.BeginRequest(httpContext, ref context); + HttpContext httpContext; + if (_defaultHttpContextFactory != null) + { + var defaultHttpContext = (DefaultHttpContext)hostContext.HttpContext; + if (defaultHttpContext is null) + { + httpContext = _defaultHttpContextFactory.Create(contextFeatures); + hostContext.HttpContext = httpContext; + } + else + { + _defaultHttpContextFactory.Initialize(defaultHttpContext, contextFeatures); + httpContext = defaultHttpContext; + } + } + else + { + httpContext = _httpContextFactory.Create(contextFeatures); + hostContext.HttpContext = httpContext; + } - context.HttpContext = httpContext; - return context; + _diagnostics.BeginRequest(httpContext, hostContext); + return hostContext; } // Execute the request @@ -51,18 +92,44 @@ namespace Microsoft.AspNetCore.Hosting { var httpContext = context.HttpContext; _diagnostics.RequestEnd(httpContext, exception, context); - _httpContextFactory.Dispose(httpContext); + + if (_defaultHttpContextFactory != null) + { + _defaultHttpContextFactory.Dispose((DefaultHttpContext)httpContext); + } + else + { + _httpContextFactory.Dispose(httpContext); + } + _diagnostics.ContextDisposed(context); + + // Reset the context as it may be pooled + context.Reset(); } - internal struct Context + + internal class Context { public HttpContext HttpContext { get; set; } public IDisposable Scope { get; set; } - public long StartTimestamp { get; set; } - public bool EventLogEnabled { get; set; } public Activity Activity { get; set; } + + public long StartTimestamp { get; set; } internal bool HasDiagnosticListener { get; set; } + public bool EventLogEnabled { get; set; } + + public void Reset() + { + // Not resetting HttpContext here as we pool it on the Context + + Scope = null; + Activity = null; + + StartTimestamp = 0; + HasDiagnosticListener = false; + EventLogEnabled = false; + } } } } diff --git a/src/Hosting/Hosting/src/Internal/HostingApplicationDiagnostics.cs b/src/Hosting/Hosting/src/Internal/HostingApplicationDiagnostics.cs index e499e81e20..65730d67ff 100644 --- a/src/Hosting/Hosting/src/Internal/HostingApplicationDiagnostics.cs +++ b/src/Hosting/Hosting/src/Internal/HostingApplicationDiagnostics.cs @@ -33,7 +33,7 @@ namespace Microsoft.AspNetCore.Hosting } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void BeginRequest(HttpContext httpContext, ref HostingApplication.Context context) + public void BeginRequest(HttpContext httpContext, HostingApplication.Context context) { long startTimestamp = 0; diff --git a/src/Http/Http/test/DefaultHttpContextFactoryTests.cs b/src/Hosting/Hosting/test/Http/DefaultHttpContextFactoryTests.cs similarity index 97% rename from src/Http/Http/test/DefaultHttpContextFactoryTests.cs rename to src/Hosting/Hosting/test/Http/DefaultHttpContextFactoryTests.cs index 8fa2ad68d9..761b122630 100644 --- a/src/Http/Http/test/DefaultHttpContextFactoryTests.cs +++ b/src/Hosting/Hosting/test/Http/DefaultHttpContextFactoryTests.cs @@ -1,11 +1,8 @@ // 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.IO; using Microsoft.AspNetCore.Http.Features; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; using Xunit; namespace Microsoft.AspNetCore.Http diff --git a/src/Hosting/Hosting/test/WebHostTests.cs b/src/Hosting/Hosting/test/WebHostTests.cs index cdc53386da..7b0665341a 100644 --- a/src/Hosting/Hosting/test/WebHostTests.cs +++ b/src/Hosting/Hosting/test/WebHostTests.cs @@ -883,23 +883,21 @@ namespace Microsoft.AspNetCore.Hosting public async Task WebHost_CreatesDefaultRequestIdentifierFeature_IfNotPresent() { // Arrange - HttpContext httpContext = null; - var requestDelegate = new RequestDelegate(innerHttpContext => - { - httpContext = innerHttpContext; - return Task.FromResult(0); - }); - - using (var host = CreateHost(requestDelegate)) + var requestDelegate = new RequestDelegate(httpContext => { - // Act - await host.StartAsync(); - // Assert Assert.NotNull(httpContext); var featuresTraceIdentifier = httpContext.Features.Get().TraceIdentifier; Assert.False(string.IsNullOrWhiteSpace(httpContext.TraceIdentifier)); Assert.Same(httpContext.TraceIdentifier, featuresTraceIdentifier); + + return Task.CompletedTask; + }); + + using (var host = CreateHost(requestDelegate)) + { + // Act + await host.StartAsync(); } } @@ -907,13 +905,15 @@ namespace Microsoft.AspNetCore.Hosting public async Task WebHost_DoesNot_CreateDefaultRequestIdentifierFeature_IfPresent() { // Arrange - HttpContext httpContext = null; - var requestDelegate = new RequestDelegate(innerHttpContext => - { - httpContext = innerHttpContext; - return Task.FromResult(0); - }); var requestIdentifierFeature = new StubHttpRequestIdentifierFeature(); + var requestDelegate = new RequestDelegate(httpContext => + { + // Assert + Assert.NotNull(httpContext); + Assert.Same(requestIdentifierFeature, httpContext.Features.Get()); + + return Task.CompletedTask; + }); using (var host = CreateHost(requestDelegate)) { @@ -926,10 +926,6 @@ namespace Microsoft.AspNetCore.Hosting }; // Act await host.StartAsync(); - - // Assert - Assert.NotNull(httpContext); - Assert.Same(requestIdentifierFeature, httpContext.Features.Get()); } } @@ -949,6 +945,36 @@ namespace Microsoft.AspNetCore.Hosting } } + [Fact] + public async Task WebHost_HttpContextUseAfterRequestEnd_Fails() + { + // Arrange + HttpContext capturedContext = null; + HttpRequest capturedRequest = null; + var requestDelegate = new RequestDelegate(httpContext => + { + capturedContext = httpContext; + capturedRequest = httpContext.Request; + + return Task.CompletedTask; + }); + + using (var host = CreateHost(requestDelegate)) + { + // Act + await host.StartAsync(); + + // Assert + Assert.NotNull(capturedContext); + Assert.NotNull(capturedRequest); + + Assert.Throws(() => capturedContext.TraceIdentifier); + Assert.Throws(() => capturedContext.Features.Get()); + + Assert.Throws(() => capturedRequest.Scheme); + } + } + public class CountStartup { public static int ConfigureServicesCount; diff --git a/src/Hosting/Server.Abstractions/ref/Microsoft.AspNetCore.Hosting.Server.Abstractions.netcoreapp3.0.cs b/src/Hosting/Server.Abstractions/ref/Microsoft.AspNetCore.Hosting.Server.Abstractions.netcoreapp3.0.cs index a775c4229e..184bb7353c 100644 --- a/src/Hosting/Server.Abstractions/ref/Microsoft.AspNetCore.Hosting.Server.Abstractions.netcoreapp3.0.cs +++ b/src/Hosting/Server.Abstractions/ref/Microsoft.AspNetCore.Hosting.Server.Abstractions.netcoreapp3.0.cs @@ -27,6 +27,13 @@ namespace Microsoft.AspNetCore.Hosting.Server public bool IsEnabled { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } } } +namespace Microsoft.AspNetCore.Hosting.Server.Abstractions +{ + public partial interface IHostContextContainer + { + TContext HostContext { get; set; } + } +} namespace Microsoft.AspNetCore.Hosting.Server.Features { public partial interface IServerAddressesFeature diff --git a/src/Hosting/Server.Abstractions/src/IHostContextContainer.cs b/src/Hosting/Server.Abstractions/src/IHostContextContainer.cs new file mode 100644 index 0000000000..603a323912 --- /dev/null +++ b/src/Hosting/Server.Abstractions/src/IHostContextContainer.cs @@ -0,0 +1,15 @@ +// 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. + +namespace Microsoft.AspNetCore.Hosting.Server.Abstractions +{ + /// + /// When implemented by a Server allows an to pool and reuse + /// its between requests. + /// + /// The Host context + public interface IHostContextContainer + { + TContext HostContext { get; set; } + } +} diff --git a/src/Hosting/TestHost/test/WebSocketClientTests.cs b/src/Hosting/TestHost/test/WebSocketClientTests.cs index 0b7571e211..40c3b23465 100644 --- a/src/Hosting/TestHost/test/WebSocketClientTests.cs +++ b/src/Hosting/TestHost/test/WebSocketClientTests.cs @@ -2,11 +2,9 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; using Xunit; namespace Microsoft.AspNetCore.TestHost.Tests @@ -19,7 +17,9 @@ namespace Microsoft.AspNetCore.TestHost.Tests [InlineData("http://localhost:81/connect", "localhost:81")] public async Task ConnectAsync_ShouldSetRequestProperties(string requestUri, string expectedHost) { - HttpRequest capturedRequest = null; + string capturedScheme = null; + string capturedHost = null; + string capturedPath = null; using (var testServer = new TestServer(new WebHostBuilder() .Configure(app => @@ -28,7 +28,9 @@ namespace Microsoft.AspNetCore.TestHost.Tests { if (ctx.Request.Path.StartsWithSegments("/connect")) { - capturedRequest = ctx.Request; + capturedScheme = ctx.Request.Scheme; + capturedHost = ctx.Request.Host.Value; + capturedPath = ctx.Request.Path; } return Task.FromResult(0); }); @@ -40,7 +42,7 @@ namespace Microsoft.AspNetCore.TestHost.Tests { await client.ConnectAsync( uri: new Uri(requestUri), - cancellationToken: default(CancellationToken)); + cancellationToken: default); } catch { @@ -48,9 +50,9 @@ namespace Microsoft.AspNetCore.TestHost.Tests } } - Assert.Equal("http", capturedRequest.Scheme); - Assert.Equal(expectedHost, capturedRequest.Host.Value); - Assert.Equal("/connect", capturedRequest.Path); + Assert.Equal("http", capturedScheme); + Assert.Equal(expectedHost, capturedHost); + Assert.Equal("/connect", capturedPath); } } } diff --git a/src/Http/Http.Features/src/FeatureReferences.cs b/src/Http/Http.Features/src/FeatureReferences.cs index 54b0472918..f19938bfe9 100644 --- a/src/Http/Http.Features/src/FeatureReferences.cs +++ b/src/Http/Http.Features/src/FeatureReferences.cs @@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.Http.Features public FeatureReferences(IFeatureCollection collection) { Collection = collection; - Cache = default(TCache); + Cache = default; Revision = collection.Revision; } @@ -65,7 +65,7 @@ namespace Microsoft.AspNetCore.Http.Features Func factory) where TFeature : class { var flush = false; - var revision = Collection.Revision; + var revision = Collection?.Revision ?? ContextDisposed(); if (Revision != revision) { // Clear cached value to force call to UpdateCached @@ -83,7 +83,7 @@ namespace Microsoft.AspNetCore.Http.Features if (flush) { // Collection detected as changed, clear cache - Cache = default(TCache); + Cache = default; } cached = Collection.Get(); @@ -108,5 +108,16 @@ namespace Microsoft.AspNetCore.Http.Features public TFeature Fetch(ref TFeature cached, Func factory) where TFeature : class => Fetch(ref cached, Collection, factory); + + private static int ContextDisposed() + { + ThrowContextDisposed(); + return 0; + } + + private static void ThrowContextDisposed() + { + throw new ObjectDisposedException(nameof(Collection), nameof(IFeatureCollection) + " has been disposed."); + } } } diff --git a/src/Http/Http/ref/Microsoft.AspNetCore.Http.netcoreapp3.0.cs b/src/Http/Http/ref/Microsoft.AspNetCore.Http.netcoreapp3.0.cs index 3848153a9f..2ef99c6b37 100644 --- a/src/Http/Http/ref/Microsoft.AspNetCore.Http.netcoreapp3.0.cs +++ b/src/Http/Http/ref/Microsoft.AspNetCore.Http.netcoreapp3.0.cs @@ -54,12 +54,6 @@ namespace Microsoft.AspNetCore.Http public void Initialize(Microsoft.AspNetCore.Http.Features.IFeatureCollection features) { } public void Uninitialize() { } } - public partial class DefaultHttpContextFactory : Microsoft.AspNetCore.Http.IHttpContextFactory - { - public DefaultHttpContextFactory(System.IServiceProvider serviceProvider) { } - public Microsoft.AspNetCore.Http.HttpContext Create(Microsoft.AspNetCore.Http.Features.IFeatureCollection featureCollection) { throw null; } - public void Dispose(Microsoft.AspNetCore.Http.HttpContext httpContext) { } - } public partial class FormCollection : Microsoft.AspNetCore.Http.IFormCollection, System.Collections.Generic.IEnumerable>, System.Collections.IEnumerable { public static readonly Microsoft.AspNetCore.Http.FormCollection Empty; @@ -164,10 +158,6 @@ namespace Microsoft.AspNetCore.Http public static void EnableBuffering(this Microsoft.AspNetCore.Http.HttpRequest request, int bufferThreshold, long bufferLimit) { } public static void EnableBuffering(this Microsoft.AspNetCore.Http.HttpRequest request, long bufferLimit) { } } - public partial interface IDefaultHttpContextContainer - { - Microsoft.AspNetCore.Http.DefaultHttpContext HttpContext { get; } - } public partial class MiddlewareFactory : Microsoft.AspNetCore.Http.IMiddlewareFactory { public MiddlewareFactory(System.IServiceProvider serviceProvider) { } diff --git a/src/Http/Http/src/DefaultHttpContext.cs b/src/Http/Http/src/DefaultHttpContext.cs index 60e0a185a6..5a9a6641c7 100644 --- a/src/Http/Http/src/DefaultHttpContext.cs +++ b/src/Http/Http/src/DefaultHttpContext.cs @@ -90,7 +90,7 @@ namespace Microsoft.AspNetCore.Http private IHttpRequestIdentifierFeature RequestIdentifierFeature => _features.Fetch(ref _features.Cache.RequestIdentifier, _newHttpRequestIdentifierFeature); - public override IFeatureCollection Features => _features.Collection; + public override IFeatureCollection Features => _features.Collection ?? ContextDisposed(); public override HttpRequest Request => _request; @@ -169,6 +169,17 @@ namespace Microsoft.AspNetCore.Http LifetimeFeature.Abort(); } + private static IFeatureCollection ContextDisposed() + { + ThrowContextDisposed(); + return null; + } + + private static void ThrowContextDisposed() + { + throw new ObjectDisposedException(nameof(HttpContext), $"Request has finished and {nameof(HttpContext)} disposed."); + } + struct FeatureInterfaces { public IItemsFeature Items; diff --git a/src/Http/Http/src/HttpContextFactory.cs b/src/Http/Http/src/HttpContextFactory.cs index 3c2796e9dd..3121c1704b 100644 --- a/src/Http/Http/src/HttpContextFactory.cs +++ b/src/Http/Http/src/HttpContextFactory.cs @@ -54,7 +54,7 @@ namespace Microsoft.AspNetCore.Http throw new ArgumentNullException(nameof(featureCollection)); } - var httpContext = CreateHttpContext(featureCollection); + var httpContext = new DefaultHttpContext(featureCollection); if (_httpContextAccessor != null) { _httpContextAccessor.HttpContext = httpContext; @@ -66,16 +66,6 @@ namespace Microsoft.AspNetCore.Http return httpContext; } - private static DefaultHttpContext CreateHttpContext(IFeatureCollection featureCollection) - { - if (featureCollection is IDefaultHttpContextContainer container) - { - return container.HttpContext; - } - - return new DefaultHttpContext(featureCollection); - } - public void Dispose(HttpContext httpContext) { if (_httpContextAccessor != null) diff --git a/src/Http/Http/src/IDefaultHttpContextContainer.cs b/src/Http/Http/src/IDefaultHttpContextContainer.cs deleted file mode 100644 index 63addddb67..0000000000 --- a/src/Http/Http/src/IDefaultHttpContextContainer.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Microsoft.AspNetCore.Http -{ - public interface IDefaultHttpContextContainer - { - DefaultHttpContext HttpContext { get; } - } -} diff --git a/src/Servers/Kestrel/Core/src/Internal/Http/Http1ConnectionOfT.cs b/src/Servers/Kestrel/Core/src/Internal/Http/Http1ConnectionOfT.cs new file mode 100644 index 0000000000..7c33962764 --- /dev/null +++ b/src/Servers/Kestrel/Core/src/Internal/Http/Http1ConnectionOfT.cs @@ -0,0 +1,14 @@ +// 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.AspNetCore.Hosting.Server.Abstractions; + +namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http +{ + internal sealed class Http1Connection : Http1Connection, IHostContextContainer + { + public Http1Connection(HttpConnectionContext context) : base(context) { } + + TContext IHostContextContainer.HostContext { get; set; } + } +} diff --git a/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.cs b/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.cs index b329fc8b20..80424d9568 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.cs @@ -25,7 +25,7 @@ using Microsoft.Net.Http.Headers; namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http { - internal abstract partial class HttpProtocol : IDefaultHttpContextContainer, IHttpResponseControl + internal abstract partial class HttpProtocol : IHttpResponseControl { private static readonly byte[] _bytesConnectionClose = Encoding.ASCII.GetBytes("\r\nConnection: close"); private static readonly byte[] _bytesConnectionKeepAlive = Encoding.ASCII.GetBytes("\r\nConnection: keep-alive"); @@ -64,7 +64,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http private long _responseBytesWritten; private readonly HttpConnectionContext _context; - private DefaultHttpContext _httpContext; private RouteValueDictionary _routeValues; private Endpoint _endpoint; @@ -296,23 +295,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http protected HttpResponseHeaders HttpResponseHeaders { get; } = new HttpResponseHeaders(); - DefaultHttpContext IDefaultHttpContextContainer.HttpContext - { - get - { - if (_httpContext is null) - { - _httpContext = new DefaultHttpContext(this); - } - else - { - _httpContext.Initialize(this); - } - - return _httpContext; - } - } - public void InitializeBodyControl(MessageBody messageBody) { if (_bodyControl == null) @@ -409,8 +391,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http _responseBytesWritten = 0; - _httpContext?.Uninitialize(); - OnReset(); } diff --git a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2StreamOfT.cs b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2StreamOfT.cs index ee6bbd3d88..e602618976 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2StreamOfT.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2StreamOfT.cs @@ -1,11 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Text; +// 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.AspNetCore.Hosting.Server; +using Microsoft.AspNetCore.Hosting.Server.Abstractions; namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 { - internal sealed class Http2Stream : Http2Stream + internal sealed class Http2Stream : Http2Stream, IHostContextContainer { private readonly IHttpApplication _application; @@ -19,5 +21,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 // REVIEW: Should we store this in a field for easy debugging? _ = ProcessRequestsAsync(_application); } + + // Pooled Host context + TContext IHostContextContainer.HostContext { get; set; } } } diff --git a/src/Servers/Kestrel/Core/src/Internal/HttpConnection.cs b/src/Servers/Kestrel/Core/src/Internal/HttpConnection.cs index b558a585e1..891bc995df 100644 --- a/src/Servers/Kestrel/Core/src/Internal/HttpConnection.cs +++ b/src/Servers/Kestrel/Core/src/Internal/HttpConnection.cs @@ -55,7 +55,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal { case HttpProtocols.Http1: // _http1Connection must be initialized before adding the connection to the connection manager - requestProcessor = _http1Connection = new Http1Connection(_context); + requestProcessor = _http1Connection = new Http1Connection(_context); _protocolSelectionState = ProtocolSelectionState.Selected; break; case HttpProtocols.Http2: