Skip to content

Commit 179c445

Browse files
committed
Leave DefaultHttpContext alone and added ReusableHttpContext and friends
- ReusableHttpContext and friends mimic the functionality of DefaultHttpContext but is sealed and has no overridable methods.
1 parent 9ed47f4 commit 179c445

File tree

9 files changed

+674
-86
lines changed

9 files changed

+674
-86
lines changed

src/Http/Http/src/DefaultHttpContext.cs

Lines changed: 3 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -51,92 +51,13 @@ public DefaultHttpContext(IFeatureCollection features)
5151
public virtual void Initialize(IFeatureCollection features)
5252
{
5353
_features = new FeatureReferences<FeatureInterfaces>(features);
54-
55-
if (GetType() != typeof(DefaultHttpContext))
56-
{
57-
_request = InitializeHttpRequest();
58-
_response = InitializeHttpResponse();
59-
}
60-
else
61-
{
62-
if (_request is DefaultHttpRequest defaultHttpRequest)
63-
{
64-
defaultHttpRequest.Initialize(this);
65-
}
66-
else
67-
{
68-
_request = new DefaultHttpRequest(this);
69-
}
70-
71-
if (_response is DefaultHttpResponse defaultHttpResponse)
72-
{
73-
defaultHttpResponse.Initialize(this);
74-
}
75-
else
76-
{
77-
_response = new DefaultHttpResponse(this);
78-
}
79-
80-
// Only set the ConnectionInfo if it was already allocated
81-
if (_connection is DefaultConnectionInfo defaultConnectionInfo)
82-
{
83-
defaultConnectionInfo.Initialize(features);
84-
}
85-
}
54+
_request = InitializeHttpRequest();
55+
_response = InitializeHttpResponse();
8656
}
8757

8858
public virtual void Uninitialize()
8959
{
90-
_features = default;
91-
92-
if (GetType() != typeof(DefaultHttpContext))
93-
{
94-
// Avoid breaing backwards compatibility with the type check
95-
UninitializeDerivedType();
96-
}
97-
else
98-
{
99-
UninitializeForReuse();
100-
}
101-
}
102-
103-
private void UninitializeForReuse()
104-
{
105-
if (_request is DefaultHttpRequest defaultHttpRequest)
106-
{
107-
defaultHttpRequest.Uninitialize();
108-
}
109-
110-
if (_response is DefaultHttpResponse defaultHttpResponse)
111-
{
112-
defaultHttpResponse.Uninitialize();
113-
}
114-
115-
// Even though this field is lazy, we will reuse it if it was created to avoid
116-
// future allocations
117-
if (_connection is DefaultConnectionInfo defaultConnectionInfo)
118-
{
119-
defaultConnectionInfo.Uninitialize();
120-
}
121-
122-
// These fields are lazily initialized so we null them out
123-
124-
if (_authenticationManager != null)
125-
{
126-
#pragma warning disable CS0618 // Type or member is obsolete
127-
_authenticationManager = null;
128-
#pragma warning restore CS0618 // Type or member is obsolete
129-
}
130-
131-
// It's unlikely that this would be reused
132-
if (_websockets != null)
133-
{
134-
_websockets = null;
135-
}
136-
}
137-
138-
private void UninitializeDerivedType()
139-
{
60+
_features = default(FeatureReferences<FeatureInterfaces>);
14061
if (_request != null)
14162
{
14263
UninitializeHttpRequest(_request);

src/Http/Http/src/Internal/DefaultHttpRequest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public virtual void Initialize(HttpContext context)
3737
public virtual void Uninitialize()
3838
{
3939
_context = null;
40-
_features = default;
40+
_features = default(FeatureReferences<FeatureInterfaces>);
4141
}
4242

4343
public override HttpContext HttpContext => _context;

src/Http/Http/src/Internal/DefaultHttpResponse.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public virtual void Initialize(HttpContext context)
3232
public virtual void Uninitialize()
3333
{
3434
_context = null;
35-
_features = default;
35+
_features = default(FeatureReferences<FeatureInterfaces>);
3636
}
3737

3838
private IHttpResponseFeature HttpResponseFeature =>
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Net;
4+
using System.Security.Cryptography.X509Certificates;
5+
using System.Text;
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
using Microsoft.AspNetCore.Http.Features;
9+
10+
namespace Microsoft.AspNetCore.Http.Internal
11+
{
12+
public sealed class ReusableConnectionInfo : ConnectionInfo
13+
{
14+
// Lambdas hoisted to static readonly fields to improve inlining https://github.com/dotnet/roslyn/issues/13624
15+
private readonly static Func<IFeatureCollection, IHttpConnectionFeature> _newHttpConnectionFeature = f => new HttpConnectionFeature();
16+
private readonly static Func<IFeatureCollection, ITlsConnectionFeature> _newTlsConnectionFeature = f => new TlsConnectionFeature();
17+
18+
private FeatureReferences<FeatureInterfaces> _features;
19+
20+
public ReusableConnectionInfo(IFeatureCollection features)
21+
{
22+
Initialize(features);
23+
}
24+
25+
public void Initialize(IFeatureCollection features)
26+
{
27+
_features = new FeatureReferences<FeatureInterfaces>(features);
28+
}
29+
30+
public void Uninitialize()
31+
{
32+
_features = default(FeatureReferences<FeatureInterfaces>);
33+
}
34+
35+
private IHttpConnectionFeature HttpConnectionFeature =>
36+
_features.Fetch(ref _features.Cache.Connection, _newHttpConnectionFeature);
37+
38+
private ITlsConnectionFeature TlsConnectionFeature =>
39+
_features.Fetch(ref _features.Cache.TlsConnection, _newTlsConnectionFeature);
40+
41+
/// <inheritdoc />
42+
public override string Id
43+
{
44+
get { return HttpConnectionFeature.ConnectionId; }
45+
set { HttpConnectionFeature.ConnectionId = value; }
46+
}
47+
48+
public override IPAddress RemoteIpAddress
49+
{
50+
get { return HttpConnectionFeature.RemoteIpAddress; }
51+
set { HttpConnectionFeature.RemoteIpAddress = value; }
52+
}
53+
54+
public override int RemotePort
55+
{
56+
get { return HttpConnectionFeature.RemotePort; }
57+
set { HttpConnectionFeature.RemotePort = value; }
58+
}
59+
60+
public override IPAddress LocalIpAddress
61+
{
62+
get { return HttpConnectionFeature.LocalIpAddress; }
63+
set { HttpConnectionFeature.LocalIpAddress = value; }
64+
}
65+
66+
public override int LocalPort
67+
{
68+
get { return HttpConnectionFeature.LocalPort; }
69+
set { HttpConnectionFeature.LocalPort = value; }
70+
}
71+
72+
public override X509Certificate2 ClientCertificate
73+
{
74+
get { return TlsConnectionFeature.ClientCertificate; }
75+
set { TlsConnectionFeature.ClientCertificate = value; }
76+
}
77+
78+
public override Task<X509Certificate2> GetClientCertificateAsync(CancellationToken cancellationToken = default)
79+
{
80+
return TlsConnectionFeature.GetClientCertificateAsync(cancellationToken);
81+
}
82+
83+
struct FeatureInterfaces
84+
{
85+
public IHttpConnectionFeature Connection;
86+
public ITlsConnectionFeature TlsConnection;
87+
}
88+
}
89+
}
Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Security.Claims;
4+
using System.Text;
5+
using System.Threading;
6+
using Microsoft.AspNetCore.Http.Authentication;
7+
using Microsoft.AspNetCore.Http.Features;
8+
using Microsoft.AspNetCore.Http.Features.Authentication;
9+
10+
namespace Microsoft.AspNetCore.Http.Internal
11+
{
12+
public sealed class ReusableHttpContext : HttpContext
13+
{
14+
// Lambdas hoisted to static readonly fields to improve inlining https://github.com/dotnet/roslyn/issues/13624
15+
private readonly static Func<IFeatureCollection, IItemsFeature> _newItemsFeature = f => new ItemsFeature();
16+
private readonly static Func<IFeatureCollection, IServiceProvidersFeature> _newServiceProvidersFeature = f => new ServiceProvidersFeature();
17+
private readonly static Func<IFeatureCollection, IHttpAuthenticationFeature> _newHttpAuthenticationFeature = f => new HttpAuthenticationFeature();
18+
private readonly static Func<IFeatureCollection, IHttpRequestLifetimeFeature> _newHttpRequestLifetimeFeature = f => new HttpRequestLifetimeFeature();
19+
private readonly static Func<IFeatureCollection, ISessionFeature> _newSessionFeature = f => new DefaultSessionFeature();
20+
private readonly static Func<IFeatureCollection, ISessionFeature> _nullSessionFeature = f => null;
21+
private readonly static Func<IFeatureCollection, IHttpRequestIdentifierFeature> _newHttpRequestIdentifierFeature = f => new HttpRequestIdentifierFeature();
22+
23+
private FeatureReferences<FeatureInterfaces> _features;
24+
25+
private ReusableHttpRequest _request;
26+
private ReusableHttpResponse _response;
27+
28+
private ReusableConnectionInfo _connection;
29+
private ReusableWebSocketManager _websockets;
30+
31+
public ReusableHttpContext(IFeatureCollection features)
32+
{
33+
Initialize(features);
34+
}
35+
36+
public void Initialize(IFeatureCollection features)
37+
{
38+
_features = new FeatureReferences<FeatureInterfaces>(features);
39+
40+
if (_request is null)
41+
{
42+
_request = new ReusableHttpRequest(this);
43+
}
44+
else
45+
{
46+
_request.Initialize(this);
47+
}
48+
49+
if (_response is null)
50+
{
51+
_response = new ReusableHttpResponse(this);
52+
}
53+
else
54+
{
55+
_response.Initialize(this);
56+
}
57+
58+
// Only set the ConnectionInfo if it was already allocated
59+
if (_connection != null)
60+
{
61+
_connection.Initialize(features);
62+
}
63+
64+
if (_websockets != null)
65+
{
66+
_websockets.Initialize(features);
67+
}
68+
}
69+
70+
public void Uninitialize()
71+
{
72+
_features = default;
73+
74+
_request?.Uninitialize();
75+
_response?.Uninitialize();
76+
_connection?.Uninitialize();
77+
_websockets?.Uninitialize();
78+
}
79+
80+
private IItemsFeature ItemsFeature =>
81+
_features.Fetch(ref _features.Cache.Items, _newItemsFeature);
82+
83+
private IServiceProvidersFeature ServiceProvidersFeature =>
84+
_features.Fetch(ref _features.Cache.ServiceProviders, _newServiceProvidersFeature);
85+
86+
private IHttpAuthenticationFeature HttpAuthenticationFeature =>
87+
_features.Fetch(ref _features.Cache.Authentication, _newHttpAuthenticationFeature);
88+
89+
private IHttpRequestLifetimeFeature LifetimeFeature =>
90+
_features.Fetch(ref _features.Cache.Lifetime, _newHttpRequestLifetimeFeature);
91+
92+
private ISessionFeature SessionFeature =>
93+
_features.Fetch(ref _features.Cache.Session, _newSessionFeature);
94+
95+
private ISessionFeature SessionFeatureOrNull =>
96+
_features.Fetch(ref _features.Cache.Session, _nullSessionFeature);
97+
98+
99+
private IHttpRequestIdentifierFeature RequestIdentifierFeature =>
100+
_features.Fetch(ref _features.Cache.RequestIdentifier, _newHttpRequestIdentifierFeature);
101+
102+
public override IFeatureCollection Features => _features.Collection;
103+
104+
public override HttpRequest Request => _request;
105+
106+
public override HttpResponse Response => _response;
107+
108+
public override ConnectionInfo Connection => _connection ?? (_connection = new ReusableConnectionInfo(_features.Collection));
109+
110+
/// <summary>
111+
/// This is obsolete and will be removed in a future version.
112+
/// The recommended alternative is to use Microsoft.AspNetCore.Authentication.AuthenticationHttpContextExtensions.
113+
/// See https://go.microsoft.com/fwlink/?linkid=845470.
114+
/// </summary>
115+
[Obsolete("This is obsolete and will be removed in a future version. The recommended alternative is to use Microsoft.AspNetCore.Authentication.AuthenticationHttpContextExtensions. See https://go.microsoft.com/fwlink/?linkid=845470.")]
116+
public override AuthenticationManager Authentication => throw new NotSupportedException();
117+
118+
public override WebSocketManager WebSockets => _websockets ?? (_websockets = new ReusableWebSocketManager(_features.Collection));
119+
120+
121+
public override ClaimsPrincipal User
122+
{
123+
get
124+
{
125+
var user = HttpAuthenticationFeature.User;
126+
if (user == null)
127+
{
128+
user = new ClaimsPrincipal(new ClaimsIdentity());
129+
HttpAuthenticationFeature.User = user;
130+
}
131+
return user;
132+
}
133+
set { HttpAuthenticationFeature.User = value; }
134+
}
135+
136+
public override IDictionary<object, object> Items
137+
{
138+
get { return ItemsFeature.Items; }
139+
set { ItemsFeature.Items = value; }
140+
}
141+
142+
public override IServiceProvider RequestServices
143+
{
144+
get { return ServiceProvidersFeature.RequestServices; }
145+
set { ServiceProvidersFeature.RequestServices = value; }
146+
}
147+
148+
public override CancellationToken RequestAborted
149+
{
150+
get { return LifetimeFeature.RequestAborted; }
151+
set { LifetimeFeature.RequestAborted = value; }
152+
}
153+
154+
public override string TraceIdentifier
155+
{
156+
get { return RequestIdentifierFeature.TraceIdentifier; }
157+
set { RequestIdentifierFeature.TraceIdentifier = value; }
158+
}
159+
160+
public override ISession Session
161+
{
162+
get
163+
{
164+
var feature = SessionFeatureOrNull;
165+
if (feature == null)
166+
{
167+
throw new InvalidOperationException("Session has not been configured for this application " +
168+
"or request.");
169+
}
170+
return feature.Session;
171+
}
172+
set
173+
{
174+
SessionFeature.Session = value;
175+
}
176+
}
177+
178+
179+
180+
public override void Abort()
181+
{
182+
LifetimeFeature.Abort();
183+
}
184+
185+
struct FeatureInterfaces
186+
{
187+
public IItemsFeature Items;
188+
public IServiceProvidersFeature ServiceProviders;
189+
public IHttpAuthenticationFeature Authentication;
190+
public IHttpRequestLifetimeFeature Lifetime;
191+
public ISessionFeature Session;
192+
public IHttpRequestIdentifierFeature RequestIdentifier;
193+
}
194+
}
195+
}

0 commit comments

Comments
 (0)