Merge branch 'lodejard/featurereferences' into dev

This commit is contained in:
Stephen Halter 2015-12-30 15:35:08 -08:00
commit 364a712cb3
15 changed files with 444 additions and 675 deletions

View File

@ -0,0 +1,54 @@
// 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;
using Microsoft.AspNet.Http.Features;
using Microsoft.AspNet.Http.Internal;
namespace SampleApp
{
public class PooledHttpContext : DefaultHttpContext
{
DefaultHttpRequest _pooledHttpRequest;
DefaultHttpResponse _pooledHttpResponse;
public PooledHttpContext(IFeatureCollection featureCollection) :
base(featureCollection)
{
}
protected override HttpRequest InitializeHttpRequest()
{
if (_pooledHttpRequest != null)
{
_pooledHttpRequest.Initialize(this, Features);
return _pooledHttpRequest;
}
return new DefaultHttpRequest(this, Features);
}
protected override void UninitializeHttpRequest(HttpRequest instance)
{
_pooledHttpRequest = instance as DefaultHttpRequest;
_pooledHttpRequest?.Uninitialize();
}
protected override HttpResponse InitializeHttpResponse()
{
if (_pooledHttpResponse != null)
{
_pooledHttpResponse.Initialize(this, Features);
return _pooledHttpResponse;
}
return new DefaultHttpResponse(this, Features);
}
protected override void UninitializeHttpResponse(HttpResponse instance)
{
_pooledHttpResponse = instance as DefaultHttpResponse;
_pooledHttpResponse?.Uninitialize();
}
}
}

View File

@ -0,0 +1,65 @@
// 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.Collections.Generic;
using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Features;
namespace SampleApp
{
public class PooledHttpContextFactory : IHttpContextFactory
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly Stack<PooledHttpContext> _pool = new Stack<PooledHttpContext>();
public PooledHttpContextFactory(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public HttpContext Create(IFeatureCollection featureCollection)
{
PooledHttpContext httpContext = null;
lock (_pool)
{
if (_pool.Count != 0)
{
httpContext = _pool.Pop();
}
}
if (httpContext == null)
{
httpContext = new PooledHttpContext(featureCollection);
}
else
{
httpContext.Initialize(featureCollection);
}
if (_httpContextAccessor != null)
{
_httpContextAccessor.HttpContext = httpContext;
}
return httpContext;
}
public void Dispose(HttpContext httpContext)
{
if (_httpContextAccessor != null)
{
_httpContextAccessor.HttpContext = null;
}
var pooled = httpContext as PooledHttpContext;
if (pooled != null)
{
pooled.Uninitialize();
lock (_pool)
{
_pool.Push(pooled);
}
}
}
}
}

View File

@ -0,0 +1,62 @@
// 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;
namespace Microsoft.AspNet.Http.Features
{
public struct FeatureReferences<TCache>
{
public FeatureReferences(IFeatureCollection collection)
{
Collection = collection;
Cache = default(TCache);
Revision = collection.Revision;
}
public IFeatureCollection Collection { get; private set; }
public int Revision { get; private set; }
// cache is a public field because the code calling Fetch must
// be able to pass ref values that "dot through" the TCache struct memory,
// if it was a Property then that getter would return a copy of the memory
// preventing the use of "ref"
public TCache Cache;
public TFeature Fetch<TFeature, TState>(
ref TFeature cached,
TState state,
Func<TState, TFeature> factory)
{
var cleared = false;
if (Revision != Collection.Revision)
{
cleared = true;
Cache = default(TCache);
Revision = Collection.Revision;
}
var feature = cached;
if (feature == null)
{
feature = Collection.Get<TFeature>();
if (feature == null)
{
feature = factory(state);
Collection.Set(feature);
if (!cleared)
{
Cache = default(TCache);
}
Revision = Collection.Revision;
}
cached = feature;
}
return feature;
}
public TFeature Fetch<TFeature>(ref TFeature cached, Func<IFeatureCollection, TFeature> factory) =>
Fetch(ref cached, Collection, factory);
}
}

View File

@ -12,56 +12,27 @@ using Microsoft.AspNet.Http.Features.Authentication.Internal;
namespace Microsoft.AspNet.Http.Authentication.Internal namespace Microsoft.AspNet.Http.Authentication.Internal
{ {
public class DefaultAuthenticationManager : AuthenticationManager, IFeatureCache public class DefaultAuthenticationManager : AuthenticationManager
{ {
private IFeatureCollection _features; private FeatureReferences<IHttpAuthenticationFeature> _features;
private int _cachedFeaturesRevision = -1;
private IHttpAuthenticationFeature _authentication;
public DefaultAuthenticationManager(IFeatureCollection features) public DefaultAuthenticationManager(IFeatureCollection features)
{ {
_features = features; Initialize(features);
((IFeatureCache)this).SetFeaturesRevision();
} }
void IFeatureCache.CheckFeaturesRevision() public virtual void Initialize(IFeatureCollection features)
{ {
if (_cachedFeaturesRevision != _features.Revision) _features = new FeatureReferences<IHttpAuthenticationFeature>(features);
{
ResetFeatures();
}
} }
void IFeatureCache.SetFeaturesRevision() public virtual void Uninitialize()
{ {
_cachedFeaturesRevision = _features.Revision; _features = default(FeatureReferences<IHttpAuthenticationFeature>);
} }
public void UpdateFeatures(IFeatureCollection features) private IHttpAuthenticationFeature HttpAuthenticationFeature =>
{ _features.Fetch(ref _features.Cache, f => new HttpAuthenticationFeature());
_features = features;
ResetFeatures();
}
private void ResetFeatures()
{
_authentication = null;
((IFeatureCache)this).SetFeaturesRevision();
}
private IHttpAuthenticationFeature HttpAuthenticationFeature
{
get
{
return FeatureHelpers.GetOrCreateAndCache(
this,
_features,
() => new HttpAuthenticationFeature(),
ref _authentication);
}
}
public override IEnumerable<AuthenticationDescription> GetAuthenticationSchemes() public override IEnumerable<AuthenticationDescription> GetAuthenticationSchemes()
{ {

View File

@ -10,70 +10,30 @@ using Microsoft.AspNet.Http.Features.Internal;
namespace Microsoft.AspNet.Http.Internal namespace Microsoft.AspNet.Http.Internal
{ {
public class DefaultConnectionInfo : ConnectionInfo, IFeatureCache public class DefaultConnectionInfo : ConnectionInfo
{ {
private IFeatureCollection _features; private FeatureReferences<FeatureInterfaces> _features;
private int _cachedFeaturesRevision = -1;
private IHttpConnectionFeature _connection;
private ITlsConnectionFeature _tlsConnection;
public DefaultConnectionInfo(IFeatureCollection features) public DefaultConnectionInfo(IFeatureCollection features)
{ {
_features = features; Initialize(features);
((IFeatureCache)this).SetFeaturesRevision();
} }
void IFeatureCache.CheckFeaturesRevision() public virtual void Initialize( IFeatureCollection features)
{ {
if (_cachedFeaturesRevision != _features.Revision) _features = new FeatureReferences<FeatureInterfaces>(features);
{
ResetFeatures();
}
} }
void IFeatureCache.SetFeaturesRevision() public virtual void Uninitialize()
{ {
_cachedFeaturesRevision = _features.Revision; _features = default(FeatureReferences<FeatureInterfaces>);
} }
public void UpdateFeatures(IFeatureCollection features) private IHttpConnectionFeature HttpConnectionFeature =>
{ _features.Fetch(ref _features.Cache.Connection, f => new HttpConnectionFeature());
_features = features;
ResetFeatures();
}
private void ResetFeatures() private ITlsConnectionFeature TlsConnectionFeature=>
{ _features.Fetch(ref _features.Cache.TlsConnection, f => new TlsConnectionFeature());
_connection = null;
_tlsConnection = null;
((IFeatureCache)this).SetFeaturesRevision();
}
private IHttpConnectionFeature HttpConnectionFeature
{
get
{
return FeatureHelpers.GetOrCreateAndCache(
this,
_features,
() => new HttpConnectionFeature(),
ref _connection);
}
}
private ITlsConnectionFeature TlsConnectionFeature
{
get
{
return FeatureHelpers.GetOrCreateAndCache(
this,
_features,
() => new TlsConnectionFeature(),
ref _tlsConnection);
}
}
public override IPAddress RemoteIpAddress public override IPAddress RemoteIpAddress
{ {
@ -115,5 +75,11 @@ namespace Microsoft.AspNet.Http.Internal
{ {
return TlsConnectionFeature.GetClientCertificateAsync(cancellationToken); return TlsConnectionFeature.GetClientCertificateAsync(cancellationToken);
} }
struct FeatureInterfaces
{
public IHttpConnectionFeature Connection;
public ITlsConnectionFeature TlsConnection;
}
} }
} }

View File

@ -14,173 +14,99 @@ using Microsoft.AspNet.Http.Features.Internal;
namespace Microsoft.AspNet.Http.Internal namespace Microsoft.AspNet.Http.Internal
{ {
public class DefaultHttpContext : HttpContext, IFeatureCache public class DefaultHttpContext : HttpContext
{ {
private readonly DefaultHttpRequest _request; private FeatureReferences<FeatureInterfaces> _features;
private readonly DefaultHttpResponse _response;
private DefaultAuthenticationManager _authenticationManager; private HttpRequest _request;
private DefaultConnectionInfo _connection; private HttpResponse _response;
private DefaultWebSocketManager _websockets; private AuthenticationManager _authenticationManager;
private ConnectionInfo _connection;
private IItemsFeature _items; private WebSocketManager _websockets;
private IServiceProvidersFeature _serviceProviders;
private IHttpAuthenticationFeature _authentication;
private IHttpRequestLifetimeFeature _lifetime;
private ISessionFeature _session;
private IFeatureCollection _features;
private int _cachedFeaturesRevision = -1;
public DefaultHttpContext() public DefaultHttpContext()
: this(new FeatureCollection()) : this(new FeatureCollection())
{ {
_features.Set<IHttpRequestFeature>(new HttpRequestFeature()); Features.Set<IHttpRequestFeature>(new HttpRequestFeature());
_features.Set<IHttpResponseFeature>(new HttpResponseFeature()); Features.Set<IHttpResponseFeature>(new HttpResponseFeature());
((IFeatureCache)this).SetFeaturesRevision();
} }
public DefaultHttpContext(IFeatureCollection features) public DefaultHttpContext(IFeatureCollection features)
{ {
_features = features; Initialize(features);
_request = new DefaultHttpRequest(this, features);
_response = new DefaultHttpResponse(this, features);
((IFeatureCache)this).SetFeaturesRevision();
} }
void IFeatureCache.CheckFeaturesRevision() public virtual void Initialize(IFeatureCollection features)
{ {
if (_cachedFeaturesRevision !=_features.Revision) _features = new FeatureReferences<FeatureInterfaces>(features);
_request = InitializeHttpRequest();
_response = InitializeHttpResponse();
}
public virtual void Uninitialize()
{
_features = default(FeatureReferences<FeatureInterfaces>);
if (_request != null)
{ {
ResetFeatures(); UninitializeHttpRequest(_request);
_request = null;
} }
} if (_response != null)
void IFeatureCache.SetFeaturesRevision()
{
_cachedFeaturesRevision = _features.Revision;
}
public void UpdateFeatures(IFeatureCollection features)
{
_features = features;
ResetFeatures();
_request.UpdateFeatures(features);
_response.UpdateFeatures(features);
_authenticationManager?.UpdateFeatures(features);
_connection?.UpdateFeatures(features);
_websockets?.UpdateFeatures(features);
}
private void ResetFeatures()
{
_items = null;
_serviceProviders = null;
_authentication = null;
_lifetime = null;
_session = null;
((IFeatureCache)this).SetFeaturesRevision();
}
IItemsFeature ItemsFeature
{
get
{ {
return FeatureHelpers.GetOrCreateAndCache( UninitializeHttpResponse(_response);
this, _response = null;
_features,
() => new ItemsFeature(),
ref _items);
} }
} if (_authenticationManager != null)
IServiceProvidersFeature ServiceProvidersFeature
{
get
{ {
return FeatureHelpers.GetOrCreateAndCache( UninitializeAuthenticationManager(_authenticationManager);
this, _authenticationManager = null;
_features,
() => new ServiceProvidersFeature(),
ref _serviceProviders);
} }
} if (_connection != null)
private IHttpAuthenticationFeature HttpAuthenticationFeature
{
get
{ {
return FeatureHelpers.GetOrCreateAndCache( UninitializeConnectionInfo(_connection);
this, _connection = null;
_features,
() => new HttpAuthenticationFeature(),
ref _authentication);
} }
} if (_websockets != null)
private IHttpRequestLifetimeFeature LifetimeFeature
{
get
{ {
return FeatureHelpers.GetOrCreateAndCache( UninitializeWebSocketManager(_websockets);
this, _websockets = null;
_features,
() => new HttpRequestLifetimeFeature(),
ref _lifetime);
} }
} }
private IItemsFeature ItemsFeature =>
_features.Fetch(ref _features.Cache.Items, f => new ItemsFeature());
private ISessionFeature SessionFeature private IServiceProvidersFeature ServiceProvidersFeature =>
{ _features.Fetch(ref _features.Cache.ServiceProviders, f => new ServiceProvidersFeature());
get { return FeatureHelpers.GetAndCache(this, _features, ref _session); }
set
{
_features.Set(value);
_session = value;
}
}
private IHttpRequestIdentifierFeature RequestIdentifierFeature private IHttpAuthenticationFeature HttpAuthenticationFeature =>
{ _features.Fetch(ref _features.Cache.Authentication, f => new HttpAuthenticationFeature());
get {
return FeatureHelpers.GetOrCreate<IHttpRequestIdentifierFeature>(
_features,
() => new HttpRequestIdentifierFeature());
}
}
public override IFeatureCollection Features { get { return _features; } } private IHttpRequestLifetimeFeature LifetimeFeature =>
_features.Fetch(ref _features.Cache.Lifetime, f => new HttpRequestLifetimeFeature());
public override HttpRequest Request { get { return _request; } } private ISessionFeature SessionFeature =>
_features.Fetch(ref _features.Cache.Session, f => new DefaultSessionFeature());
public override HttpResponse Response { get { return _response; } } private ISessionFeature SessionFeatureOrNull =>
_features.Fetch(ref _features.Cache.Session, f => null);
public override ConnectionInfo Connection
{
get
{
if (_connection == null)
{
_connection = new DefaultConnectionInfo(_features);
}
return _connection;
}
}
public override AuthenticationManager Authentication private IHttpRequestIdentifierFeature RequestIdentifierFeature =>
{ _features.Fetch(ref _features.Cache.RequestIdentifier, f => new HttpRequestIdentifierFeature());
get
{ public override IFeatureCollection Features => _features.Collection;
if (_authenticationManager == null)
{ public override HttpRequest Request => _request;
_authenticationManager = new DefaultAuthenticationManager(_features);
} public override HttpResponse Response => _response;
return _authenticationManager;
} public override ConnectionInfo Connection => _connection ?? (_connection = InitializeConnectionInfo());
}
public override AuthenticationManager Authentication => _authenticationManager ?? (_authenticationManager = InitializeAuthenticationManager());
public override WebSocketManager WebSockets => _websockets ?? (_websockets = InitializeWebSocketManager());
public override ClaimsPrincipal User public override ClaimsPrincipal User
{ {
@ -225,7 +151,7 @@ namespace Microsoft.AspNet.Http.Internal
{ {
get get
{ {
var feature = SessionFeature; var feature = SessionFeatureOrNull;
if (feature == null) if (feature == null)
{ {
throw new InvalidOperationException("Session has not been configured for this application " + throw new InvalidOperationException("Session has not been configured for this application " +
@ -235,31 +161,41 @@ namespace Microsoft.AspNet.Http.Internal
} }
set set
{ {
var feature = SessionFeature; SessionFeature.Session = value;
if (feature == null)
{
feature = new DefaultSessionFeature();
SessionFeature = feature;
}
feature.Session = value;
} }
} }
public override WebSocketManager WebSockets
{
get
{
if (_websockets == null)
{
_websockets = new DefaultWebSocketManager(_features);
}
return _websockets;
}
}
public override void Abort() public override void Abort()
{ {
LifetimeFeature.Abort(); LifetimeFeature.Abort();
} }
protected virtual HttpRequest InitializeHttpRequest() => new DefaultHttpRequest(this, Features);
protected virtual void UninitializeHttpRequest(HttpRequest instance) { }
protected virtual HttpResponse InitializeHttpResponse() => new DefaultHttpResponse(this, Features);
protected virtual void UninitializeHttpResponse(HttpResponse instance) { }
protected virtual ConnectionInfo InitializeConnectionInfo() => new DefaultConnectionInfo(Features);
protected virtual void UninitializeConnectionInfo(ConnectionInfo instance) { }
protected virtual AuthenticationManager InitializeAuthenticationManager() => new DefaultAuthenticationManager(Features);
protected virtual void UninitializeAuthenticationManager(AuthenticationManager instance) { }
protected virtual WebSocketManager InitializeWebSocketManager() => new DefaultWebSocketManager(Features);
protected virtual void UninitializeWebSocketManager(WebSocketManager instance) { }
struct FeatureInterfaces
{
public IItemsFeature Items;
public IServiceProvidersFeature ServiceProviders;
public IHttpAuthenticationFeature Authentication;
public IHttpRequestLifetimeFeature Lifetime;
public ISessionFeature Session;
public IHttpRequestIdentifierFeature RequestIdentifier;
}
} }
} }

View File

@ -11,96 +11,41 @@ using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Http.Internal namespace Microsoft.AspNet.Http.Internal
{ {
public class DefaultHttpRequest : HttpRequest, IFeatureCache public class DefaultHttpRequest : HttpRequest
{ {
private readonly DefaultHttpContext _context; private HttpContext _context;
private IFeatureCollection _features; private FeatureReferences<FeatureInterfaces> _features;
private int _cachedFeaturesRevision = -1;
private IHttpRequestFeature _request; public DefaultHttpRequest(HttpContext context, IFeatureCollection features)
private IQueryFeature _query; {
private IFormFeature _form; Initialize(context, features);
private IRequestCookiesFeature _cookies; }
public DefaultHttpRequest(DefaultHttpContext context, IFeatureCollection features) public virtual void Initialize(HttpContext context, IFeatureCollection features)
{ {
_context = context; _context = context;
_features = features; _features = new FeatureReferences<FeatureInterfaces>(features);
((IFeatureCache)this).SetFeaturesRevision();
} }
void IFeatureCache.CheckFeaturesRevision() public virtual void Uninitialize()
{ {
if (_cachedFeaturesRevision != _features.Revision) _context = null;
{ _features = default(FeatureReferences<FeatureInterfaces>);
ResetFeatures();
}
} }
void IFeatureCache.SetFeaturesRevision() public override HttpContext HttpContext => _context;
{
_cachedFeaturesRevision = _features.Revision;
}
public void UpdateFeatures(IFeatureCollection features) private IHttpRequestFeature HttpRequestFeature =>
{ _features.Fetch(ref _features.Cache.Request, f => null);
_features = features;
ResetFeatures();
}
private void ResetFeatures() private IQueryFeature QueryFeature =>
{ _features.Fetch(ref _features.Cache.Query, f => new QueryFeature(f));
_request = null;
_query = null;
_form = null;
_cookies = null;
((IFeatureCache)this).SetFeaturesRevision(); private IFormFeature FormFeature =>
} _features.Fetch(ref _features.Cache.Form, this, f => new FormFeature(f));
private IHttpRequestFeature HttpRequestFeature private IRequestCookiesFeature RequestCookiesFeature =>
{ _features.Fetch(ref _features.Cache.Cookies, f => new RequestCookiesFeature(f));
get { return FeatureHelpers.GetAndCache(this, _features, ref _request); }
}
private IQueryFeature QueryFeature
{
get
{
return FeatureHelpers.GetOrCreateAndCache(
this,
_features,
(f) => new QueryFeature(f),
ref _query);
}
}
private IFormFeature FormFeature
{
get
{
return FeatureHelpers.GetOrCreateAndCache(
this,
_features,
this,
(r) => new FormFeature(r),
ref _form);
}
}
private IRequestCookiesFeature RequestCookiesFeature
{
get
{
return FeatureHelpers.GetOrCreateAndCache(
this,
_features,
(f) => new RequestCookiesFeature(f),
ref _cookies);
}
}
public override HttpContext HttpContext { get { return _context; } }
public override PathString PathBase public override PathString PathBase
{ {
@ -206,5 +151,13 @@ namespace Microsoft.AspNet.Http.Internal
{ {
return FormFeature.ReadFormAsync(cancellationToken); return FormFeature.ReadFormAsync(cancellationToken);
} }
struct FeatureInterfaces
{
public IHttpRequestFeature Request;
public IQueryFeature Query;
public IFormFeature Form;
public IRequestCookiesFeature Cookies;
}
} }
} }

View File

@ -10,65 +10,34 @@ using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Http.Internal namespace Microsoft.AspNet.Http.Internal
{ {
public class DefaultHttpResponse : HttpResponse, IFeatureCache public class DefaultHttpResponse : HttpResponse
{ {
private readonly DefaultHttpContext _context; private HttpContext _context;
private IFeatureCollection _features; private FeatureReferences<FeatureInterfaces> _features;
private int _cachedFeaturesRevision = -1;
private IHttpResponseFeature _response; public DefaultHttpResponse(HttpContext context, IFeatureCollection features)
private IResponseCookiesFeature _cookies; {
Initialize(context, features);
}
public DefaultHttpResponse(DefaultHttpContext context, IFeatureCollection features) public virtual void Initialize(HttpContext context, IFeatureCollection features)
{ {
_context = context; _context = context;
_features = features; _features = new FeatureReferences<FeatureInterfaces>(features);
((IFeatureCache)this).SetFeaturesRevision();
} }
void IFeatureCache.CheckFeaturesRevision() public virtual void Uninitialize()
{ {
if (_cachedFeaturesRevision != _features.Revision) _context = null;
{ _features = default(FeatureReferences<FeatureInterfaces>);
ResetFeatures();
}
} }
void IFeatureCache.SetFeaturesRevision() private IHttpResponseFeature HttpResponseFeature =>
{ _features.Fetch(ref _features.Cache.Response, f => null);
_cachedFeaturesRevision = _features.Revision;
}
public void UpdateFeatures(IFeatureCollection features) private IResponseCookiesFeature ResponseCookiesFeature =>
{ _features.Fetch(ref _features.Cache.Cookies, f => new ResponseCookiesFeature(f));
_features = features;
ResetFeatures();
}
private void ResetFeatures()
{
_response = null;
_cookies = null;
((IFeatureCache)this).SetFeaturesRevision();
}
private IHttpResponseFeature HttpResponseFeature
{
get { return FeatureHelpers.GetAndCache(this, _features, ref _response); }
}
private IResponseCookiesFeature ResponseCookiesFeature
{
get
{
return FeatureHelpers.GetOrCreateAndCache(
this,
_features,
(f) => new ResponseCookiesFeature(f),
ref _cookies);
}
}
public override HttpContext HttpContext { get { return _context; } } public override HttpContext HttpContext { get { return _context; } }
@ -163,5 +132,11 @@ namespace Microsoft.AspNet.Http.Internal
Headers[HeaderNames.Location] = location; Headers[HeaderNames.Location] = location;
} }
struct FeatureInterfaces
{
public IHttpResponseFeature Response;
public IResponseCookiesFeature Cookies;
}
} }
} }

View File

@ -10,56 +10,30 @@ using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Http.Internal namespace Microsoft.AspNet.Http.Internal
{ {
public class DefaultWebSocketManager : WebSocketManager, IFeatureCache public class DefaultWebSocketManager : WebSocketManager
{ {
private IFeatureCollection _features; private FeatureReferences<FeatureInterfaces> _features;
private int _cachedFeaturesRevision = -1;
private IHttpRequestFeature _request;
private IHttpWebSocketFeature _webSockets;
public DefaultWebSocketManager(IFeatureCollection features) public DefaultWebSocketManager(IFeatureCollection features)
{ {
_features = features; Initialize(features);
((IFeatureCache)this).SetFeaturesRevision();
} }
void IFeatureCache.CheckFeaturesRevision() public virtual void Initialize(IFeatureCollection features)
{ {
if (_cachedFeaturesRevision != _features.Revision) _features = new FeatureReferences<FeatureInterfaces>(features);
{
ResetFeatures();
}
} }
void IFeatureCache.SetFeaturesRevision() public virtual void Uninitialize()
{ {
_cachedFeaturesRevision = _features.Revision; _features = default(FeatureReferences<FeatureInterfaces>);
} }
public void UpdateFeatures(IFeatureCollection features) private IHttpRequestFeature HttpRequestFeature =>
{ _features.Fetch(ref _features.Cache.Request, f => null);
_features = features;
ResetFeatures();
}
private void ResetFeatures() private IHttpWebSocketFeature WebSocketFeature =>
{ _features.Fetch(ref _features.Cache.WebSockets, f => null);
_request = null;
_webSockets = null;
((IFeatureCache)this).SetFeaturesRevision();
}
private IHttpRequestFeature HttpRequestFeature
{
get { return FeatureHelpers.GetAndCache(this, _features, ref _request); }
}
private IHttpWebSocketFeature WebSocketFeature
{
get { return FeatureHelpers.GetAndCache(this, _features, ref _webSockets); }
}
public override bool IsWebSocketRequest public override bool IsWebSocketRequest
{ {
@ -85,5 +59,11 @@ namespace Microsoft.AspNet.Http.Internal
} }
return WebSocketFeature.AcceptAsync(new WebSocketAcceptContext() { SubProtocol = subProtocol }); return WebSocketFeature.AcceptAsync(new WebSocketAcceptContext() { SubProtocol = subProtocol });
} }
struct FeatureInterfaces
{
public IHttpRequestFeature Request;
public IHttpWebSocketFeature WebSockets;
}
} }
} }

View File

@ -1,116 +0,0 @@
// 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;
namespace Microsoft.AspNet.Http.Features
{
internal static class FeatureHelpers
{
public static T GetAndCache<T>(
IFeatureCache cache,
IFeatureCollection features,
ref T cachedObject)
where T : class
{
cache.CheckFeaturesRevision();
T obj = cachedObject;
if (obj == null)
{
obj = features.Get<T>();
cachedObject = obj;
}
return obj;
}
public static T GetOrCreate<T>(
IFeatureCollection features,
Func<T> factory)
where T : class
{
T obj = features.Get<T>();
if (obj == null)
{
obj = factory();
features.Set(obj);
}
return obj;
}
public static T GetOrCreateAndCache<T>(
IFeatureCache cache,
IFeatureCollection features,
Func<T> factory,
ref T cachedObject)
where T : class
{
cache.CheckFeaturesRevision();
T obj = cachedObject;
if (obj == null)
{
obj = features.Get<T>();
if (obj == null)
{
obj = factory();
}
cachedObject = obj;
features.Set(obj);
cache.SetFeaturesRevision();
}
return obj;
}
public static T GetOrCreateAndCache<T>(
IFeatureCache cache,
IFeatureCollection features,
Func<IFeatureCollection, T> factory,
ref T cachedObject)
where T : class
{
cache.CheckFeaturesRevision();
T obj = cachedObject;
if (obj == null)
{
obj = features.Get<T>();
if (obj == null)
{
obj = factory(features);
}
cachedObject = obj;
features.Set(obj);
cache.SetFeaturesRevision();
}
return obj;
}
public static T GetOrCreateAndCache<T>(
IFeatureCache cache,
IFeatureCollection features,
HttpRequest request,
Func<HttpRequest, T> factory,
ref T cachedObject)
where T : class
{
cache.CheckFeaturesRevision();
T obj = cachedObject;
if (obj == null)
{
obj = features.Get<T>();
if (obj == null)
{
obj = factory(request);
}
cachedObject = obj;
features.Set(obj);
cache.SetFeaturesRevision();
}
return obj;
}
}
}

View File

@ -1,11 +0,0 @@
// 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.AspNet.Http.Features
{
internal interface IFeatureCache
{
void CheckFeaturesRevision();
void SetFeaturesRevision();
}
}

View File

@ -7,12 +7,9 @@ using Microsoft.AspNet.WebUtilities;
namespace Microsoft.AspNet.Http.Features.Internal namespace Microsoft.AspNet.Http.Features.Internal
{ {
public class QueryFeature : IQueryFeature, IFeatureCache public class QueryFeature : IQueryFeature
{ {
private readonly IFeatureCollection _features; private FeatureReferences<IHttpRequestFeature> _features;
private int _cachedFeaturesRevision = -1;
private IHttpRequestFeature _request;
private string _original; private string _original;
private IQueryCollection _parsedValues; private IQueryCollection _parsedValues;
@ -34,34 +31,17 @@ namespace Microsoft.AspNet.Http.Features.Internal
throw new ArgumentNullException(nameof(features)); throw new ArgumentNullException(nameof(features));
} }
_features = features; _features = new FeatureReferences<IHttpRequestFeature>(features);
((IFeatureCache)this).SetFeaturesRevision();
} }
void IFeatureCache.CheckFeaturesRevision() private IHttpRequestFeature HttpRequestFeature =>
{ _features.Fetch(ref _features.Cache, f => null);
if (_cachedFeaturesRevision != _features.Revision)
{
_request = null;
((IFeatureCache)this).SetFeaturesRevision();
}
}
void IFeatureCache.SetFeaturesRevision()
{
_cachedFeaturesRevision = _features.Revision;
}
private IHttpRequestFeature HttpRequestFeature
{
get { return FeatureHelpers.GetAndCache(this, _features, ref _request); }
}
public IQueryCollection Query public IQueryCollection Query
{ {
get get
{ {
if (_features == null) if (_features.Collection == null)
{ {
if (_parsedValues == null) if (_parsedValues == null)
{ {
@ -91,7 +71,7 @@ namespace Microsoft.AspNet.Http.Features.Internal
set set
{ {
_parsedValues = value; _parsedValues = value;
if (_features != null) if (_features.Collection != null)
{ {
if (value == null) if (value == null)
{ {

View File

@ -9,13 +9,9 @@ using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Http.Features.Internal namespace Microsoft.AspNet.Http.Features.Internal
{ {
public class RequestCookiesFeature : IRequestCookiesFeature, IFeatureCache public class RequestCookiesFeature : IRequestCookiesFeature
{ {
private readonly IFeatureCollection _features; private FeatureReferences<IHttpRequestFeature> _features;
private int _cachedFeaturesRevision = -1;
private IHttpRequestFeature _request;
private StringValues _original; private StringValues _original;
private IRequestCookieCollection _parsedValues; private IRequestCookieCollection _parsedValues;
@ -36,34 +32,17 @@ namespace Microsoft.AspNet.Http.Features.Internal
throw new ArgumentNullException(nameof(features)); throw new ArgumentNullException(nameof(features));
} }
_features = features; _features = new FeatureReferences<IHttpRequestFeature>(features);
((IFeatureCache)this).SetFeaturesRevision();
} }
void IFeatureCache.CheckFeaturesRevision() private IHttpRequestFeature HttpRequestFeature =>
{ _features.Fetch(ref _features.Cache, f => null);
if (_cachedFeaturesRevision != _features.Revision)
{
_request = null;
((IFeatureCache)this).SetFeaturesRevision();
}
}
void IFeatureCache.SetFeaturesRevision()
{
_cachedFeaturesRevision = _features.Revision;
}
private IHttpRequestFeature HttpRequestFeature
{
get { return FeatureHelpers.GetAndCache(this, _features, ref _request); }
}
public IRequestCookieCollection Cookies public IRequestCookieCollection Cookies
{ {
get get
{ {
if (_features == null) if (_features.Collection == null)
{ {
if (_parsedValues == null) if (_parsedValues == null)
{ {
@ -91,7 +70,7 @@ namespace Microsoft.AspNet.Http.Features.Internal
{ {
_parsedValues = value; _parsedValues = value;
_original = StringValues.Empty; _original = StringValues.Empty;
if (_features != null) if (_features.Collection != null)
{ {
if (_parsedValues == null || _parsedValues.Count == 0) if (_parsedValues == null || _parsedValues.Count == 0)
{ {

View File

@ -5,38 +5,18 @@ using Microsoft.AspNet.Http.Internal;
namespace Microsoft.AspNet.Http.Features.Internal namespace Microsoft.AspNet.Http.Features.Internal
{ {
public class ResponseCookiesFeature : IResponseCookiesFeature, IFeatureCache public class ResponseCookiesFeature : IResponseCookiesFeature
{ {
private readonly IFeatureCollection _features; private FeatureReferences<IHttpResponseFeature> _features;
private int _cachedFeaturesRevision = -1;
private IHttpResponseFeature _response;
private IResponseCookies _cookiesCollection; private IResponseCookies _cookiesCollection;
public ResponseCookiesFeature(IFeatureCollection features) public ResponseCookiesFeature(IFeatureCollection features)
{ {
_features = features; _features = new FeatureReferences<IHttpResponseFeature>(features);
((IFeatureCache)this).SetFeaturesRevision();
} }
void IFeatureCache.CheckFeaturesRevision() private IHttpResponseFeature HttpResponseFeature =>
{ _features.Fetch(ref _features.Cache, f => null);
if (_cachedFeaturesRevision != _features.Revision)
{
_response = null;
((IFeatureCache)this).SetFeaturesRevision();
}
}
void IFeatureCache.SetFeaturesRevision()
{
_cachedFeaturesRevision = _features.Revision;
}
private IHttpResponseFeature HttpResponseFeature
{
get { return FeatureHelpers.GetAndCache(this, _features, ref _response); }
}
public IResponseCookies Cookies public IResponseCookies Cookies
{ {

View File

@ -149,93 +149,88 @@ namespace Microsoft.AspNet.Http.Internal
items["foo"] = item; items["foo"] = item;
Assert.Same(item, context.Items["foo"]); Assert.Same(item, context.Items["foo"]);
} }
[Fact] [Fact]
public void UpdateFeatures_ClearsCachedFeatures() public void UpdateFeatures_ClearsCachedFeatures()
{ {
var features = new FeatureCollection(); var features = new FeatureCollection();
var context = new DefaultHttpContext(features);
var request = (DefaultHttpRequest)context.Request;
var response = (DefaultHttpResponse)context.Response;
var authentication = (DefaultAuthenticationManager)context.Authentication;
var connection = (DefaultConnectionInfo)context.Connection;
var websockets = (DefaultWebSocketManager)context.WebSockets;
Assert.Equal(0, features.Count());
TestCachedFeaturesAreNull(context.GetType(), context, features);
TestCachedFeaturesAreNull(request.GetType(), request, features);
TestCachedFeaturesAreNull(response.GetType(), response, features);
TestCachedFeaturesAreNull(authentication.GetType(), authentication, features);
TestCachedFeaturesAreNull(connection.GetType(), connection, features);
TestCachedFeaturesAreNull(websockets.GetType(), websockets, features);
context.Session = new TestSession();
features.Set<IHttpRequestFeature>(new HttpRequestFeature()); features.Set<IHttpRequestFeature>(new HttpRequestFeature());
features.Set<IHttpResponseFeature>(new HttpResponseFeature()); features.Set<IHttpResponseFeature>(new HttpResponseFeature());
features.Set<IHttpWebSocketFeature>(new TestHttpWebSocketFeature()); features.Set<IHttpWebSocketFeature>(new TestHttpWebSocketFeature());
TestCachedFeaturesAreSet(context.GetType(), context, features); // featurecollection is set. all cached interfaces are null.
TestCachedFeaturesAreSet(request.GetType(), request, features); var context = new DefaultHttpContext(features);
TestCachedFeaturesAreSet(response.GetType(), response, features); TestAllCachedFeaturesAreNull(context, features);
TestCachedFeaturesAreSet(authentication.GetType(), authentication, features); Assert.Equal(3, features.Count());
TestCachedFeaturesAreSet(connection.GetType(), connection, features);
TestCachedFeaturesAreSet(websockets.GetType(), websockets, features); // getting feature properties populates feature collection with defaults
TestAllCachedFeaturesAreSet(context, features);
Assert.NotEqual(3, features.Count());
// featurecollection is null. and all cached interfaces are null.
// only top level is tested because child objects are inaccessible.
context.Uninitialize();
TestCachedFeaturesAreNull(context, null);
Assert.NotEqual(0, features.Count());
var newFeatures = new FeatureCollection(); var newFeatures = new FeatureCollection();
context.UpdateFeatures(newFeatures);
Assert.Equal(0, newFeatures.Count());
TestCachedFeaturesAreNull(context.GetType(), context, newFeatures);
TestCachedFeaturesAreNull(request.GetType(), request, newFeatures);
TestCachedFeaturesAreNull(response.GetType(), response, newFeatures);
TestCachedFeaturesAreNull(authentication.GetType(), authentication, newFeatures);
TestCachedFeaturesAreNull(connection.GetType(), connection, newFeatures);
TestCachedFeaturesAreNull(websockets.GetType(), websockets, newFeatures);
context.Session = new TestSession();
newFeatures.Set<IHttpRequestFeature>(new HttpRequestFeature()); newFeatures.Set<IHttpRequestFeature>(new HttpRequestFeature());
newFeatures.Set<IHttpResponseFeature>(new HttpResponseFeature()); newFeatures.Set<IHttpResponseFeature>(new HttpResponseFeature());
newFeatures.Set<IHttpWebSocketFeature>(new TestHttpWebSocketFeature()); newFeatures.Set<IHttpWebSocketFeature>(new TestHttpWebSocketFeature());
TestCachedFeaturesAreSet(context.GetType(), context, newFeatures); // featurecollection is set to newFeatures. all cached interfaces are null.
TestCachedFeaturesAreSet(request.GetType(), request, newFeatures); context.Initialize(newFeatures);
TestCachedFeaturesAreSet(response.GetType(), response, newFeatures); TestAllCachedFeaturesAreNull(context, newFeatures);
TestCachedFeaturesAreSet(authentication.GetType(), authentication, newFeatures); Assert.Equal(3, newFeatures.Count());
TestCachedFeaturesAreSet(connection.GetType(), connection, newFeatures);
TestCachedFeaturesAreSet(websockets.GetType(), websockets, newFeatures);
Assert.NotEqual(0, newFeatures.Count()); // getting feature properties populates new feature collection with defaults
TestAllCachedFeaturesAreSet(context, newFeatures);
Assert.NotEqual(3, newFeatures.Count());
} }
void TestCachedFeaturesAreNull(Type type, object value, IFeatureCollection features) void TestAllCachedFeaturesAreNull(HttpContext context, IFeatureCollection features)
{ {
TestCachedFeaturesAreNull(context, features);
TestCachedFeaturesAreNull(context.Request, features);
TestCachedFeaturesAreNull(context.Response, features);
TestCachedFeaturesAreNull(context.Authentication, features);
TestCachedFeaturesAreNull(context.Connection, features);
TestCachedFeaturesAreNull(context.WebSockets, features);
}
var fields = type void TestCachedFeaturesAreNull(object value, IFeatureCollection features)
{
var type = value.GetType();
var field = type
.GetFields(BindingFlags.NonPublic | BindingFlags.Instance) .GetFields(BindingFlags.NonPublic | BindingFlags.Instance)
.Where(f => f.FieldType.GetTypeInfo().IsInterface); .Single(f =>
f.FieldType.GetTypeInfo().IsGenericType &&
f.FieldType.GetGenericTypeDefinition() == typeof(FeatureReferences<>));
foreach (var field in fields) var boxedExpectedStruct = features == null ?
{ Activator.CreateInstance(field.FieldType) :
if (field.FieldType == typeof(IFeatureCollection)) Activator.CreateInstance(field.FieldType, features);
{
Assert.Same(features, field.GetValue(value)); var boxedActualStruct = field.GetValue(value);
}
else Assert.Equal(boxedExpectedStruct, boxedActualStruct);
{
Assert.Null(field.GetValue(value));
}
}
} }
void TestCachedFeaturesAreSet(Type type, object value, IFeatureCollection features) void TestAllCachedFeaturesAreSet(HttpContext context, IFeatureCollection features)
{ {
TestCachedFeaturesAreSet(context, features);
TestCachedFeaturesAreSet(context.Request, features);
TestCachedFeaturesAreSet(context.Response, features);
TestCachedFeaturesAreSet(context.Authentication, features);
TestCachedFeaturesAreSet(context.Connection, features);
TestCachedFeaturesAreSet(context.WebSockets, features);
}
void TestCachedFeaturesAreSet(object value, IFeatureCollection features)
{
var type = value.GetType();
var properties = type var properties = type
.GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance) .GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
.Where(p => p.PropertyType.GetTypeInfo().IsInterface); .Where(p => p.PropertyType.GetTypeInfo().IsInterface);