using System.Security.Claims; using Microsoft.AspNetCore.Http.Features; namespace Microsoft.AspNetCore.Http; /// <summary> /// Encapsulates all HTTP-specific information about an individual HTTP request. /// </summary> public abstract class HttpContext { /// <summary> /// Gets the collection of HTTP features provided by the server and middleware available on this request. /// </summary> public abstract IFeatureCollection Features { get; } /// <summary> /// Gets the <see cref="HttpRequest"/> object for this request. /// </summary> public abstract HttpRequest Request { get; } /// <summary> /// Gets the <see cref="HttpResponse"/> object for this request. /// </summary> public abstract HttpResponse Response { get; } /// <summary> /// Gets information about the underlying connection for this request. /// </summary> public abstract ConnectionInfo Connection { get; } /// <summary> /// Gets an object that manages the establishment of WebSocket connections for this request. /// </summary> public abstract WebSocketManager WebSockets { get; } /// <summary> /// Gets or sets the user for this request. /// </summary> public abstract ClaimsPrincipal User { get; set; } /// <summary> /// Gets or sets a key/value collection that can be used to share data within the scope of this request. /// </summary> public abstract IDictionary<object, object?> Items { get; set; } /// <summary> /// Gets or sets the <see cref="IServiceProvider"/> that provides access to the request's service container. /// </summary> public abstract IServiceProvider RequestServices { get; set; } /// <summary> /// Notifies when the connection underlying this request is aborted and thus request operations should be /// cancelled. /// </summary> public abstract CancellationToken RequestAborted { get; set; } /// <summary> /// Gets or sets a unique identifier to represent this request in trace logs. /// </summary> public abstract string TraceIdentifier { get; set; } /// <summary> /// Gets or sets the object used to manage user session data for this request. /// </summary> public abstract ISession Session { get; set; } /// <summary> /// Aborts the connection underlying this request. /// </summary> public abstract void Abort(); }
using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Security.Claims; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http.Features.Authentication; using Microsoft.Extensions.DependencyInjection; namespace Microsoft.AspNetCore.Http; /// <summary> /// Represents an implementation of the HTTP Context class. /// </summary> public sealed class DefaultHttpContext : HttpContext { // The initial size of the feature collection when using the default constructor; based on number of common features // https://github.com/dotnet/aspnetcore/issues/31249 private const int DefaultFeatureCollectionSize = 10; // Lambdas hoisted to static readonly fields to improve inlining https://github.com/dotnet/roslyn/issues/13624 private static readonly Func<IFeatureCollection, IItemsFeature> _newItemsFeature = f => new ItemsFeature(); private static readonly Func<DefaultHttpContext, IServiceProvidersFeature> _newServiceProvidersFeature = context => new RequestServicesFeature(context, context.ServiceScopeFactory); private static readonly Func<IFeatureCollection, IHttpAuthenticationFeature> _newHttpAuthenticationFeature = f => new HttpAuthenticationFeature(); private static readonly Func<IFeatureCollection, IHttpRequestLifetimeFeature> _newHttpRequestLifetimeFeature = f => new HttpRequestLifetimeFeature(); private static readonly Func<IFeatureCollection, ISessionFeature> _newSessionFeature = f => new DefaultSessionFeature(); private static readonly Func<IFeatureCollection, ISessionFeature?> _nullSessionFeature = f => null; private static readonly Func<IFeatureCollection, IHttpRequestIdentifierFeature> _newHttpRequestIdentifierFeature = f => new HttpRequestIdentifierFeature(); private FeatureReferences<FeatureInterfaces> _features; private readonly DefaultHttpRequest _request; private readonly DefaultHttpResponse _response; private DefaultConnectionInfo? _connection; private DefaultWebSocketManager? _websockets; // This is field exists to make analyzing memory dumps easier. // https://github.com/dotnet/aspnetcore/issues/29709 internal bool _active; /// <summary> /// Initializes a new instance of the <see cref="DefaultHttpContext"/> class. /// </summary> public DefaultHttpContext() : this(new FeatureCollection(DefaultFeatureCollectionSize)) { Features.Set<IHttpRequestFeature>(new HttpRequestFeature()); Features.Set<IHttpResponseFeature>(new HttpResponseFeature()); Features.Set<IHttpResponseBodyFeature>(new StreamResponseBodyFeature(Stream.Null)); } /// <summary> /// Initializes a new instance of the <see cref="DefaultHttpContext"/> class with provided features. /// </summary> /// <param name="features">Initial set of features for the <see cref="DefaultHttpContext"/>.</param> public DefaultHttpContext(IFeatureCollection features) { _features.Initalize(features); _request = new DefaultHttpRequest(this); _response = new DefaultHttpResponse(this); } /// <summary> /// Reinitialize the current instant of the class with features passed in. /// </summary> /// <remarks> /// This method allows the consumer to re-use the <see cref="DefaultHttpContext" /> for another request, rather than having to allocate a new instance. /// </remarks> /// <param name="features">The new set of features for the <see cref="DefaultHttpContext" />.</param> public void Initialize(IFeatureCollection features) { var revision = features.Revision; _features.Initalize(features, revision); _request.Initialize(revision); _response.Initialize(revision); _connection?.Initialize(features, revision); _websockets?.Initialize(features, revision); _active = true; } /// <summary> /// Uninitialize all the features in the <see cref="DefaultHttpContext" />. /// </summary> public void Uninitialize() { _features = default; _request.Uninitialize(); _response.Uninitialize(); _connection?.Uninitialize(); _websockets?.Uninitialize(); _active = false; } /// <summary> /// Gets or set the <see cref="FormOptions" /> for this instance. /// </summary> /// <returns> /// <see cref="FormOptions"/> /// </returns> public FormOptions FormOptions { get; set; } = default!; /// <summary> /// Gets or sets the <see cref="IServiceScopeFactory" /> for this instance. /// </summary> /// <returns> /// <see cref="IServiceScopeFactory"/> /// </returns> public IServiceScopeFactory ServiceScopeFactory { get; set; } = default!; private IItemsFeature ItemsFeature => _features.Fetch(ref _features.Cache.Items, _newItemsFeature)!; private IServiceProvidersFeature ServiceProvidersFeature => _features.Fetch(ref _features.Cache.ServiceProviders, this, _newServiceProvidersFeature)!; private IHttpAuthenticationFeature HttpAuthenticationFeature => _features.Fetch(ref _features.Cache.Authentication, _newHttpAuthenticationFeature)!; private IHttpRequestLifetimeFeature LifetimeFeature => _features.Fetch(ref _features.Cache.Lifetime, _newHttpRequestLifetimeFeature)!; private ISessionFeature SessionFeature => _features.Fetch(ref _features.Cache.Session, _newSessionFeature)!; private ISessionFeature? SessionFeatureOrNull => _features.Fetch(ref _features.Cache.Session, _nullSessionFeature); private IHttpRequestIdentifierFeature RequestIdentifierFeature => _features.Fetch(ref _features.Cache.RequestIdentifier, _newHttpRequestIdentifierFeature)!; /// <inheritdoc/> public override IFeatureCollection Features => _features.Collection ?? ContextDisposed(); /// <inheritdoc/> public override HttpRequest Request => _request; /// <inheritdoc/> public override HttpResponse Response => _response; /// <inheritdoc/> public override ConnectionInfo Connection => _connection ?? (_connection = new DefaultConnectionInfo(Features)); /// <inheritdoc/> public override WebSocketManager WebSockets => _websockets ?? (_websockets = new DefaultWebSocketManager(Features)); /// <inheritdoc/> public override ClaimsPrincipal User { get { var user = HttpAuthenticationFeature.User; if (user == null) { user = new ClaimsPrincipal(new ClaimsIdentity()); HttpAuthenticationFeature.User = user; } return user; } set { HttpAuthenticationFeature.User = value; } } /// <inheritdoc/> public override IDictionary<object, object?> Items { get { return ItemsFeature.Items; } set { ItemsFeature.Items = value; } } /// <inheritdoc/> public override IServiceProvider RequestServices { get { return ServiceProvidersFeature.RequestServices; } set { ServiceProvidersFeature.RequestServices = value; } } /// <inheritdoc/> public override CancellationToken RequestAborted { get { return LifetimeFeature.RequestAborted; } set { LifetimeFeature.RequestAborted = value; } } /// <inheritdoc/> public override string TraceIdentifier { get { return RequestIdentifierFeature.TraceIdentifier; } set { RequestIdentifierFeature.TraceIdentifier = value; } } /// <inheritdoc/> public override ISession Session { get { var feature = SessionFeatureOrNull; if (feature == null) { throw new InvalidOperationException("Session has not been configured for this application " + "or request."); } return feature.Session; } set { SessionFeature.Session = value; } } // This property exists because of backwards compatibility. // We send an anonymous object with an HttpContext property // via DiagnosticListener in various events throughout the pipeline. Instead // we just send the HttpContext to avoid extra allocations /// <summary> /// This API is used by ASP.NET Core's infrastructure and should not be used by application code. /// </summary> [EditorBrowsable(EditorBrowsableState.Never)] public HttpContext HttpContext => this; /// <inheritdoc/> public override void Abort() { LifetimeFeature.Abort(); } private static IFeatureCollection ContextDisposed() { ThrowContextDisposed(); return null; } [DoesNotReturn] private static void ThrowContextDisposed() { throw new ObjectDisposedException(nameof(HttpContext), $"Request has finished and {nameof(HttpContext)} disposed."); } struct FeatureInterfaces { public IItemsFeature? Items; public IServiceProvidersFeature? ServiceProviders; public IHttpAuthenticationFeature? Authentication; public IHttpRequestLifetimeFeature? Lifetime; public ISessionFeature? Session; public IHttpRequestIdentifierFeature? RequestIdentifier; } }