Better context pooling (#12385)
- This change goes from pooling just the HttpContext to pooling the entire TContext. In the past this was a huge struct that got copied around and now it can be a class. Servers can provide the storage for the TContext via a new `IHostContextContainer<TContext>` interface. - Removed IDefaultHttpContextContainer since it's been superseded by IHostContextContainer - Move DefaultHttpContextFactory to Hosting to take advantage of internal methods - Also handle a null FeatureCollection and null HttpContext and throw a better exception
This commit is contained in:
parent
4ac6a4ad35
commit
65ca72c420
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
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<Context> 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);
|
||||
|
||||
if (_defaultHttpContextFactory != null)
|
||||
{
|
||||
_defaultHttpContextFactory.Dispose((DefaultHttpContext)httpContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
_httpContextFactory.Dispose(httpContext);
|
||||
_diagnostics.ContextDisposed(context);
|
||||
}
|
||||
|
||||
internal struct Context
|
||||
_diagnostics.ContextDisposed(context);
|
||||
|
||||
// Reset the context as it may be pooled
|
||||
context.Reset();
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -883,23 +883,21 @@ namespace Microsoft.AspNetCore.Hosting
|
|||
public async Task WebHost_CreatesDefaultRequestIdentifierFeature_IfNotPresent()
|
||||
{
|
||||
// Arrange
|
||||
HttpContext httpContext = null;
|
||||
var requestDelegate = new RequestDelegate(innerHttpContext =>
|
||||
var requestDelegate = new RequestDelegate(httpContext =>
|
||||
{
|
||||
httpContext = innerHttpContext;
|
||||
return Task.FromResult(0);
|
||||
// Assert
|
||||
Assert.NotNull(httpContext);
|
||||
var featuresTraceIdentifier = httpContext.Features.Get<IHttpRequestIdentifierFeature>().TraceIdentifier;
|
||||
Assert.False(string.IsNullOrWhiteSpace(httpContext.TraceIdentifier));
|
||||
Assert.Same(httpContext.TraceIdentifier, featuresTraceIdentifier);
|
||||
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
|
||||
using (var host = CreateHost(requestDelegate))
|
||||
{
|
||||
// Act
|
||||
await host.StartAsync();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(httpContext);
|
||||
var featuresTraceIdentifier = httpContext.Features.Get<IHttpRequestIdentifierFeature>().TraceIdentifier;
|
||||
Assert.False(string.IsNullOrWhiteSpace(httpContext.TraceIdentifier));
|
||||
Assert.Same(httpContext.TraceIdentifier, featuresTraceIdentifier);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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<IHttpRequestIdentifierFeature>());
|
||||
|
||||
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<IHttpRequestIdentifierFeature>());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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<ObjectDisposedException>(() => capturedContext.TraceIdentifier);
|
||||
Assert.Throws<ObjectDisposedException>(() => capturedContext.Features.Get<IHttpRequestIdentifierFeature>());
|
||||
|
||||
Assert.Throws<ObjectDisposedException>(() => capturedRequest.Scheme);
|
||||
}
|
||||
}
|
||||
|
||||
public class CountStartup
|
||||
{
|
||||
public static int ConfigureServicesCount;
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
{
|
||||
TContext HostContext { get; set; }
|
||||
}
|
||||
}
|
||||
namespace Microsoft.AspNetCore.Hosting.Server.Features
|
||||
{
|
||||
public partial interface IServerAddressesFeature
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// When implemented by a Server allows an <see cref="IHttpApplication{TContext}"/> to pool and reuse
|
||||
/// its <typeparamref name="TContext"/> between requests.
|
||||
/// </summary>
|
||||
/// <typeparam name="TContext">The <see cref="IHttpApplication{TContext}"/> Host context</typeparam>
|
||||
public interface IHostContextContainer<TContext>
|
||||
{
|
||||
TContext HostContext { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<TState, TFeature> 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<TFeature>();
|
||||
|
|
@ -108,5 +108,16 @@ namespace Microsoft.AspNetCore.Http.Features
|
|||
|
||||
public TFeature Fetch<TFeature>(ref TFeature cached, Func<IFeatureCollection, TFeature> 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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.Generic.KeyValuePair<string, Microsoft.Extensions.Primitives.StringValues>>, 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) { }
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.AspNetCore.Http
|
||||
{
|
||||
public interface IDefaultHttpContextContainer
|
||||
{
|
||||
DefaultHttpContext HttpContext { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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<TContext> : Http1Connection, IHostContextContainer<TContext>
|
||||
{
|
||||
public Http1Connection(HttpConnectionContext context) : base(context) { }
|
||||
|
||||
TContext IHostContextContainer<TContext>.HostContext { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<TContext> : Http2Stream
|
||||
internal sealed class Http2Stream<TContext> : Http2Stream, IHostContextContainer<TContext>
|
||||
{
|
||||
private readonly IHttpApplication<TContext> _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<TContext>.HostContext { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<TContext>(_context);
|
||||
_protocolSelectionState = ProtocolSelectionState.Selected;
|
||||
break;
|
||||
case HttpProtocols.Http2:
|
||||
|
|
|
|||
Loading…
Reference in New Issue