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
{
public class DefaultAuthenticationManager : AuthenticationManager, IFeatureCache
public class DefaultAuthenticationManager : AuthenticationManager
{
private IFeatureCollection _features;
private int _cachedFeaturesRevision = -1;
private IHttpAuthenticationFeature _authentication;
private FeatureReferences<IHttpAuthenticationFeature> _features;
public DefaultAuthenticationManager(IFeatureCollection features)
{
_features = features;
((IFeatureCache)this).SetFeaturesRevision();
Initialize(features);
}
void IFeatureCache.CheckFeaturesRevision()
public virtual void Initialize(IFeatureCollection features)
{
if (_cachedFeaturesRevision != _features.Revision)
{
ResetFeatures();
}
_features = new FeatureReferences<IHttpAuthenticationFeature>(features);
}
void IFeatureCache.SetFeaturesRevision()
public virtual void Uninitialize()
{
_cachedFeaturesRevision = _features.Revision;
_features = default(FeatureReferences<IHttpAuthenticationFeature>);
}
public void UpdateFeatures(IFeatureCollection features)
{
_features = features;
ResetFeatures();
}
private void ResetFeatures()
{
_authentication = null;
((IFeatureCache)this).SetFeaturesRevision();
}
private IHttpAuthenticationFeature HttpAuthenticationFeature
{
get
{
return FeatureHelpers.GetOrCreateAndCache(
this,
_features,
() => new HttpAuthenticationFeature(),
ref _authentication);
}
}
private IHttpAuthenticationFeature HttpAuthenticationFeature =>
_features.Fetch(ref _features.Cache, f => new HttpAuthenticationFeature());
public override IEnumerable<AuthenticationDescription> GetAuthenticationSchemes()
{

View File

@ -10,70 +10,30 @@ using Microsoft.AspNet.Http.Features.Internal;
namespace Microsoft.AspNet.Http.Internal
{
public class DefaultConnectionInfo : ConnectionInfo, IFeatureCache
public class DefaultConnectionInfo : ConnectionInfo
{
private IFeatureCollection _features;
private int _cachedFeaturesRevision = -1;
private IHttpConnectionFeature _connection;
private ITlsConnectionFeature _tlsConnection;
private FeatureReferences<FeatureInterfaces> _features;
public DefaultConnectionInfo(IFeatureCollection features)
{
_features = features;
((IFeatureCache)this).SetFeaturesRevision();
Initialize(features);
}
void IFeatureCache.CheckFeaturesRevision()
public virtual void Initialize( IFeatureCollection features)
{
if (_cachedFeaturesRevision != _features.Revision)
{
ResetFeatures();
}
_features = new FeatureReferences<FeatureInterfaces>(features);
}
void IFeatureCache.SetFeaturesRevision()
public virtual void Uninitialize()
{
_cachedFeaturesRevision = _features.Revision;
_features = default(FeatureReferences<FeatureInterfaces>);
}
public void UpdateFeatures(IFeatureCollection features)
{
_features = features;
ResetFeatures();
}
private IHttpConnectionFeature HttpConnectionFeature =>
_features.Fetch(ref _features.Cache.Connection, f => new HttpConnectionFeature());
private void ResetFeatures()
{
_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);
}
}
private ITlsConnectionFeature TlsConnectionFeature=>
_features.Fetch(ref _features.Cache.TlsConnection, f => new TlsConnectionFeature());
public override IPAddress RemoteIpAddress
{
@ -115,5 +75,11 @@ namespace Microsoft.AspNet.Http.Internal
{
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
{
public class DefaultHttpContext : HttpContext, IFeatureCache
public class DefaultHttpContext : HttpContext
{
private readonly DefaultHttpRequest _request;
private readonly DefaultHttpResponse _response;
private FeatureReferences<FeatureInterfaces> _features;
private DefaultAuthenticationManager _authenticationManager;
private DefaultConnectionInfo _connection;
private DefaultWebSocketManager _websockets;
private IItemsFeature _items;
private IServiceProvidersFeature _serviceProviders;
private IHttpAuthenticationFeature _authentication;
private IHttpRequestLifetimeFeature _lifetime;
private ISessionFeature _session;
private IFeatureCollection _features;
private int _cachedFeaturesRevision = -1;
private HttpRequest _request;
private HttpResponse _response;
private AuthenticationManager _authenticationManager;
private ConnectionInfo _connection;
private WebSocketManager _websockets;
public DefaultHttpContext()
: this(new FeatureCollection())
{
_features.Set<IHttpRequestFeature>(new HttpRequestFeature());
_features.Set<IHttpResponseFeature>(new HttpResponseFeature());
((IFeatureCache)this).SetFeaturesRevision();
Features.Set<IHttpRequestFeature>(new HttpRequestFeature());
Features.Set<IHttpResponseFeature>(new HttpResponseFeature());
}
public DefaultHttpContext(IFeatureCollection features)
{
_features = features;
_request = new DefaultHttpRequest(this, features);
_response = new DefaultHttpResponse(this, features);
((IFeatureCache)this).SetFeaturesRevision();
Initialize(features);
}
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;
}
}
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
if (_response != null)
{
return FeatureHelpers.GetOrCreateAndCache(
this,
_features,
() => new ItemsFeature(),
ref _items);
UninitializeHttpResponse(_response);
_response = null;
}
}
IServiceProvidersFeature ServiceProvidersFeature
{
get
if (_authenticationManager != null)
{
return FeatureHelpers.GetOrCreateAndCache(
this,
_features,
() => new ServiceProvidersFeature(),
ref _serviceProviders);
UninitializeAuthenticationManager(_authenticationManager);
_authenticationManager = null;
}
}
private IHttpAuthenticationFeature HttpAuthenticationFeature
{
get
if (_connection != null)
{
return FeatureHelpers.GetOrCreateAndCache(
this,
_features,
() => new HttpAuthenticationFeature(),
ref _authentication);
UninitializeConnectionInfo(_connection);
_connection = null;
}
}
private IHttpRequestLifetimeFeature LifetimeFeature
{
get
if (_websockets != null)
{
return FeatureHelpers.GetOrCreateAndCache(
this,
_features,
() => new HttpRequestLifetimeFeature(),
ref _lifetime);
UninitializeWebSocketManager(_websockets);
_websockets = null;
}
}
private IItemsFeature ItemsFeature =>
_features.Fetch(ref _features.Cache.Items, f => new ItemsFeature());
private ISessionFeature SessionFeature
{
get { return FeatureHelpers.GetAndCache(this, _features, ref _session); }
set
{
_features.Set(value);
_session = value;
}
}
private IServiceProvidersFeature ServiceProvidersFeature =>
_features.Fetch(ref _features.Cache.ServiceProviders, f => new ServiceProvidersFeature());
private IHttpRequestIdentifierFeature RequestIdentifierFeature
{
get {
return FeatureHelpers.GetOrCreate<IHttpRequestIdentifierFeature>(
_features,
() => new HttpRequestIdentifierFeature());
}
}
private IHttpAuthenticationFeature HttpAuthenticationFeature =>
_features.Fetch(ref _features.Cache.Authentication, f => new HttpAuthenticationFeature());
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
{
get
{
if (_authenticationManager == null)
{
_authenticationManager = new DefaultAuthenticationManager(_features);
}
return _authenticationManager;
}
}
private IHttpRequestIdentifierFeature RequestIdentifierFeature =>
_features.Fetch(ref _features.Cache.RequestIdentifier, f => new HttpRequestIdentifierFeature());
public override IFeatureCollection Features => _features.Collection;
public override HttpRequest Request => _request;
public override HttpResponse Response => _response;
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
{
@ -225,7 +151,7 @@ namespace Microsoft.AspNet.Http.Internal
{
get
{
var feature = SessionFeature;
var feature = SessionFeatureOrNull;
if (feature == null)
{
throw new InvalidOperationException("Session has not been configured for this application " +
@ -235,31 +161,41 @@ namespace Microsoft.AspNet.Http.Internal
}
set
{
var feature = SessionFeature;
if (feature == null)
{
feature = new DefaultSessionFeature();
SessionFeature = feature;
}
feature.Session = value;
SessionFeature.Session = value;
}
}
public override WebSocketManager WebSockets
{
get
{
if (_websockets == null)
{
_websockets = new DefaultWebSocketManager(_features);
}
return _websockets;
}
}
public override void 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
{
public class DefaultHttpRequest : HttpRequest, IFeatureCache
public class DefaultHttpRequest : HttpRequest
{
private readonly DefaultHttpContext _context;
private IFeatureCollection _features;
private int _cachedFeaturesRevision = -1;
private HttpContext _context;
private FeatureReferences<FeatureInterfaces> _features;
private IHttpRequestFeature _request;
private IQueryFeature _query;
private IFormFeature _form;
private IRequestCookiesFeature _cookies;
public DefaultHttpRequest(HttpContext context, IFeatureCollection features)
{
Initialize(context, features);
}
public DefaultHttpRequest(DefaultHttpContext context, IFeatureCollection features)
public virtual void Initialize(HttpContext context, IFeatureCollection features)
{
_context = context;
_features = features;
((IFeatureCache)this).SetFeaturesRevision();
_features = new FeatureReferences<FeatureInterfaces>(features);
}
void IFeatureCache.CheckFeaturesRevision()
public virtual void Uninitialize()
{
if (_cachedFeaturesRevision != _features.Revision)
{
ResetFeatures();
}
_context = null;
_features = default(FeatureReferences<FeatureInterfaces>);
}
void IFeatureCache.SetFeaturesRevision()
{
_cachedFeaturesRevision = _features.Revision;
}
public override HttpContext HttpContext => _context;
public void UpdateFeatures(IFeatureCollection features)
{
_features = features;
ResetFeatures();
}
private IHttpRequestFeature HttpRequestFeature =>
_features.Fetch(ref _features.Cache.Request, f => null);
private void ResetFeatures()
{
_request = null;
_query = null;
_form = null;
_cookies = null;
private IQueryFeature QueryFeature =>
_features.Fetch(ref _features.Cache.Query, f => new QueryFeature(f));
((IFeatureCache)this).SetFeaturesRevision();
}
private IFormFeature FormFeature =>
_features.Fetch(ref _features.Cache.Form, this, f => new FormFeature(f));
private IHttpRequestFeature HttpRequestFeature
{
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; } }
private IRequestCookiesFeature RequestCookiesFeature =>
_features.Fetch(ref _features.Cache.Cookies, f => new RequestCookiesFeature(f));
public override PathString PathBase
{
@ -206,5 +151,13 @@ namespace Microsoft.AspNet.Http.Internal
{
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
{
public class DefaultHttpResponse : HttpResponse, IFeatureCache
public class DefaultHttpResponse : HttpResponse
{
private readonly DefaultHttpContext _context;
private IFeatureCollection _features;
private int _cachedFeaturesRevision = -1;
private HttpContext _context;
private FeatureReferences<FeatureInterfaces> _features;
private IHttpResponseFeature _response;
private IResponseCookiesFeature _cookies;
public DefaultHttpResponse(HttpContext context, IFeatureCollection features)
{
Initialize(context, features);
}
public DefaultHttpResponse(DefaultHttpContext context, IFeatureCollection features)
public virtual void Initialize(HttpContext context, IFeatureCollection features)
{
_context = context;
_features = features;
((IFeatureCache)this).SetFeaturesRevision();
_features = new FeatureReferences<FeatureInterfaces>(features);
}
void IFeatureCache.CheckFeaturesRevision()
public virtual void Uninitialize()
{
if (_cachedFeaturesRevision != _features.Revision)
{
ResetFeatures();
}
_context = null;
_features = default(FeatureReferences<FeatureInterfaces>);
}
void IFeatureCache.SetFeaturesRevision()
{
_cachedFeaturesRevision = _features.Revision;
}
private IHttpResponseFeature HttpResponseFeature =>
_features.Fetch(ref _features.Cache.Response, f => null);
public void UpdateFeatures(IFeatureCollection features)
{
_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);
}
}
private IResponseCookiesFeature ResponseCookiesFeature =>
_features.Fetch(ref _features.Cache.Cookies, f => new ResponseCookiesFeature(f));
public override HttpContext HttpContext { get { return _context; } }
@ -163,5 +132,11 @@ namespace Microsoft.AspNet.Http.Internal
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
{
public class DefaultWebSocketManager : WebSocketManager, IFeatureCache
public class DefaultWebSocketManager : WebSocketManager
{
private IFeatureCollection _features;
private int _cachedFeaturesRevision = -1;
private IHttpRequestFeature _request;
private IHttpWebSocketFeature _webSockets;
private FeatureReferences<FeatureInterfaces> _features;
public DefaultWebSocketManager(IFeatureCollection features)
{
_features = features;
((IFeatureCache)this).SetFeaturesRevision();
Initialize(features);
}
void IFeatureCache.CheckFeaturesRevision()
public virtual void Initialize(IFeatureCollection features)
{
if (_cachedFeaturesRevision != _features.Revision)
{
ResetFeatures();
}
_features = new FeatureReferences<FeatureInterfaces>(features);
}
void IFeatureCache.SetFeaturesRevision()
public virtual void Uninitialize()
{
_cachedFeaturesRevision = _features.Revision;
_features = default(FeatureReferences<FeatureInterfaces>);
}
public void UpdateFeatures(IFeatureCollection features)
{
_features = features;
ResetFeatures();
}
private IHttpRequestFeature HttpRequestFeature =>
_features.Fetch(ref _features.Cache.Request, f => null);
private void ResetFeatures()
{
_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); }
}
private IHttpWebSocketFeature WebSocketFeature =>
_features.Fetch(ref _features.Cache.WebSockets, f => null);
public override bool IsWebSocketRequest
{
@ -85,5 +59,11 @@ namespace Microsoft.AspNet.Http.Internal
}
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
{
public class QueryFeature : IQueryFeature, IFeatureCache
public class QueryFeature : IQueryFeature
{
private readonly IFeatureCollection _features;
private int _cachedFeaturesRevision = -1;
private IHttpRequestFeature _request;
private FeatureReferences<IHttpRequestFeature> _features;
private string _original;
private IQueryCollection _parsedValues;
@ -34,34 +31,17 @@ namespace Microsoft.AspNet.Http.Features.Internal
throw new ArgumentNullException(nameof(features));
}
_features = features;
((IFeatureCache)this).SetFeaturesRevision();
_features = new FeatureReferences<IHttpRequestFeature>(features);
}
void IFeatureCache.CheckFeaturesRevision()
{
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); }
}
private IHttpRequestFeature HttpRequestFeature =>
_features.Fetch(ref _features.Cache, f => null);
public IQueryCollection Query
{
get
{
if (_features == null)
if (_features.Collection == null)
{
if (_parsedValues == null)
{
@ -91,7 +71,7 @@ namespace Microsoft.AspNet.Http.Features.Internal
set
{
_parsedValues = value;
if (_features != null)
if (_features.Collection != null)
{
if (value == null)
{

View File

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

View File

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

View File

@ -149,93 +149,88 @@ namespace Microsoft.AspNet.Http.Internal
items["foo"] = item;
Assert.Same(item, context.Items["foo"]);
}
[Fact]
public void UpdateFeatures_ClearsCachedFeatures()
{
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<IHttpResponseFeature>(new HttpResponseFeature());
features.Set<IHttpWebSocketFeature>(new TestHttpWebSocketFeature());
TestCachedFeaturesAreSet(context.GetType(), context, features);
TestCachedFeaturesAreSet(request.GetType(), request, features);
TestCachedFeaturesAreSet(response.GetType(), response, features);
TestCachedFeaturesAreSet(authentication.GetType(), authentication, features);
TestCachedFeaturesAreSet(connection.GetType(), connection, features);
TestCachedFeaturesAreSet(websockets.GetType(), websockets, features);
// featurecollection is set. all cached interfaces are null.
var context = new DefaultHttpContext(features);
TestAllCachedFeaturesAreNull(context, features);
Assert.Equal(3, features.Count());
// 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();
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<IHttpResponseFeature>(new HttpResponseFeature());
newFeatures.Set<IHttpWebSocketFeature>(new TestHttpWebSocketFeature());
TestCachedFeaturesAreSet(context.GetType(), context, newFeatures);
TestCachedFeaturesAreSet(request.GetType(), request, newFeatures);
TestCachedFeaturesAreSet(response.GetType(), response, newFeatures);
TestCachedFeaturesAreSet(authentication.GetType(), authentication, newFeatures);
TestCachedFeaturesAreSet(connection.GetType(), connection, newFeatures);
TestCachedFeaturesAreSet(websockets.GetType(), websockets, newFeatures);
// featurecollection is set to newFeatures. all cached interfaces are null.
context.Initialize(newFeatures);
TestAllCachedFeaturesAreNull(context, newFeatures);
Assert.Equal(3, newFeatures.Count());
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)
.Where(f => f.FieldType.GetTypeInfo().IsInterface);
.Single(f =>
f.FieldType.GetTypeInfo().IsGenericType &&
f.FieldType.GetGenericTypeDefinition() == typeof(FeatureReferences<>));
foreach (var field in fields)
{
if (field.FieldType == typeof(IFeatureCollection))
{
Assert.Same(features, field.GetValue(value));
}
else
{
Assert.Null(field.GetValue(value));
}
}
var boxedExpectedStruct = features == null ?
Activator.CreateInstance(field.FieldType) :
Activator.CreateInstance(field.FieldType, features);
var boxedActualStruct = field.GetValue(value);
Assert.Equal(boxedExpectedStruct, boxedActualStruct);
}
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
.GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
.Where(p => p.PropertyType.GetTypeInfo().IsInterface);