Allow feature cache to be updated+invalidated

This commit is contained in:
Ben Adams 2015-12-06 23:18:41 +00:00 committed by Chris R
parent c41be75796
commit 67c5ec29b3
12 changed files with 330 additions and 47 deletions

View File

@ -14,27 +14,43 @@ namespace Microsoft.AspNet.Http.Authentication.Internal
{
public class DefaultAuthenticationManager : AuthenticationManager, IFeatureCache
{
private readonly IFeatureCollection _features;
private IFeatureCollection _features;
private int _cachedFeaturesRevision = -1;
private IHttpAuthenticationFeature _authentication;
private IHttpResponseFeature _response;
public DefaultAuthenticationManager(IFeatureCollection features)
{
_features = features;
((IFeatureCache)this).SetFeaturesRevision();
}
void IFeatureCache.CheckFeaturesRevision()
{
if (_cachedFeaturesRevision != _features.Revision)
{
_authentication = null;
_response = null;
_cachedFeaturesRevision = _features.Revision;
ResetFeatures();
}
}
void IFeatureCache.SetFeaturesRevision()
{
_cachedFeaturesRevision = _features.Revision;
}
public void UpdateFeatures(IFeatureCollection features)
{
_features = features;
ResetFeatures();
}
private void ResetFeatures()
{
_authentication = null;
((IFeatureCache)this).SetFeaturesRevision();
}
private IHttpAuthenticationFeature HttpAuthenticationFeature
{
get
@ -47,11 +63,6 @@ namespace Microsoft.AspNet.Http.Authentication.Internal
}
}
private IHttpResponseFeature HttpResponseFeature
{
get { return FeatureHelpers.GetAndCache(this, _features, ref _response); }
}
public override IEnumerable<AuthenticationDescription> GetAuthenticationSchemes()
{
var handler = HttpAuthenticationFeature.Handler;

View File

@ -12,7 +12,7 @@ namespace Microsoft.AspNet.Http.Internal
{
public class DefaultConnectionInfo : ConnectionInfo, IFeatureCache
{
private readonly IFeatureCollection _features;
private IFeatureCollection _features;
private int _cachedFeaturesRevision = -1;
private IHttpConnectionFeature _connection;
@ -21,18 +21,36 @@ namespace Microsoft.AspNet.Http.Internal
public DefaultConnectionInfo(IFeatureCollection features)
{
_features = features;
((IFeatureCache)this).SetFeaturesRevision();
}
void IFeatureCache.CheckFeaturesRevision()
{
if (_cachedFeaturesRevision != _features.Revision)
{
_connection = null;
_tlsConnection = null;
_cachedFeaturesRevision = _features.Revision;
ResetFeatures();
}
}
void IFeatureCache.SetFeaturesRevision()
{
_cachedFeaturesRevision = _features.Revision;
}
public void UpdateFeatures(IFeatureCollection features)
{
_features = features;
ResetFeatures();
}
private void ResetFeatures()
{
_connection = null;
_tlsConnection = null;
((IFeatureCache)this).SetFeaturesRevision();
}
private IHttpConnectionFeature HttpConnectionFeature
{
get

View File

@ -16,17 +16,18 @@ namespace Microsoft.AspNet.Http.Internal
{
public class DefaultHttpContext : HttpContext, IFeatureCache
{
private readonly HttpRequest _request;
private readonly HttpResponse _response;
private ConnectionInfo _connection;
private AuthenticationManager _authenticationManager;
private readonly DefaultHttpRequest _request;
private readonly DefaultHttpResponse _response;
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 WebSocketManager _websockets;
private IFeatureCollection _features;
private int _cachedFeaturesRevision = -1;
@ -36,6 +37,7 @@ namespace Microsoft.AspNet.Http.Internal
{
_features.Set<IHttpRequestFeature>(new HttpRequestFeature());
_features.Set<IHttpResponseFeature>(new HttpResponseFeature());
((IFeatureCache)this).SetFeaturesRevision();
}
public DefaultHttpContext(IFeatureCollection features)
@ -43,21 +45,46 @@ namespace Microsoft.AspNet.Http.Internal
_features = features;
_request = new DefaultHttpRequest(this, features);
_response = new DefaultHttpResponse(this, features);
((IFeatureCache)this).SetFeaturesRevision();
}
void IFeatureCache.CheckFeaturesRevision()
{
if (_cachedFeaturesRevision !=_features.Revision)
{
_items = null;
_serviceProviders = null;
_authentication = null;
_lifetime = null;
_session = null;
_cachedFeaturesRevision = _features.Revision;
ResetFeatures();
}
}
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

View File

@ -14,7 +14,7 @@ namespace Microsoft.AspNet.Http.Internal
public class DefaultHttpRequest : HttpRequest, IFeatureCache
{
private readonly DefaultHttpContext _context;
private readonly IFeatureCollection _features;
private IFeatureCollection _features;
private int _cachedFeaturesRevision = -1;
private IHttpRequestFeature _request;
@ -26,20 +26,38 @@ namespace Microsoft.AspNet.Http.Internal
{
_context = context;
_features = features;
((IFeatureCache)this).SetFeaturesRevision();
}
void IFeatureCache.CheckFeaturesRevision()
{
if (_cachedFeaturesRevision != _features.Revision)
{
_request = null;
_query = null;
_form = null;
_cookies = null;
_cachedFeaturesRevision = _features.Revision;
ResetFeatures();
}
}
void IFeatureCache.SetFeaturesRevision()
{
_cachedFeaturesRevision = _features.Revision;
}
public void UpdateFeatures(IFeatureCollection features)
{
_features = features;
ResetFeatures();
}
private void ResetFeatures()
{
_request = null;
_query = null;
_form = null;
_cookies = null;
((IFeatureCache)this).SetFeaturesRevision();
}
private IHttpRequestFeature HttpRequestFeature
{
get { return FeatureHelpers.GetAndCache(this, _features, ref _request); }

View File

@ -13,7 +13,7 @@ namespace Microsoft.AspNet.Http.Internal
public class DefaultHttpResponse : HttpResponse, IFeatureCache
{
private readonly DefaultHttpContext _context;
private readonly IFeatureCollection _features;
private IFeatureCollection _features;
private int _cachedFeaturesRevision = -1;
private IHttpResponseFeature _response;
@ -23,18 +23,36 @@ namespace Microsoft.AspNet.Http.Internal
{
_context = context;
_features = features;
((IFeatureCache)this).SetFeaturesRevision();
}
void IFeatureCache.CheckFeaturesRevision()
{
if (_cachedFeaturesRevision != _features.Revision)
{
_response = null;
_cookies = null;
_cachedFeaturesRevision = _features.Revision;
ResetFeatures();
}
}
void IFeatureCache.SetFeaturesRevision()
{
_cachedFeaturesRevision = _features.Revision;
}
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); }

View File

@ -21,18 +21,36 @@ namespace Microsoft.AspNet.Http.Internal
public DefaultWebSocketManager(IFeatureCollection features)
{
_features = features;
((IFeatureCache)this).SetFeaturesRevision();
}
void IFeatureCache.CheckFeaturesRevision()
{
if (_cachedFeaturesRevision != _features.Revision)
{
_request = null;
_webSockets = null;
_cachedFeaturesRevision = _features.Revision;
ResetFeatures();
}
}
void IFeatureCache.SetFeaturesRevision()
{
_cachedFeaturesRevision = _features.Revision;
}
public void UpdateFeatures(IFeatureCollection features)
{
_features = features;
ResetFeatures();
}
private void ResetFeatures()
{
_request = null;
_webSockets = null;
((IFeatureCache)this).SetFeaturesRevision();
}
private IHttpRequestFeature HttpRequestFeature
{
get { return FeatureHelpers.GetAndCache(this, _features, ref _request); }

View File

@ -56,9 +56,10 @@ namespace Microsoft.AspNet.Http.Features
if (obj == null)
{
obj = factory();
cachedObject = obj;
features.Set(obj);
}
cachedObject = obj;
features.Set(obj);
cache.SetFeaturesRevision();
}
return obj;
}
@ -79,9 +80,10 @@ namespace Microsoft.AspNet.Http.Features
if (obj == null)
{
obj = factory(features);
cachedObject = obj;
features.Set(obj);
}
cachedObject = obj;
features.Set(obj);
cache.SetFeaturesRevision();
}
return obj;
}
@ -103,9 +105,10 @@ namespace Microsoft.AspNet.Http.Features
if (obj == null)
{
obj = factory(request);
cachedObject = obj;
features.Set(obj);
}
cachedObject = obj;
features.Set(obj);
cache.SetFeaturesRevision();
}
return obj;
}

View File

@ -6,5 +6,6 @@ namespace Microsoft.AspNet.Http.Features
internal interface IFeatureCache
{
void CheckFeaturesRevision();
void SetFeaturesRevision();
}
}

View File

@ -35,6 +35,7 @@ namespace Microsoft.AspNet.Http.Features.Internal
}
_features = features;
((IFeatureCache)this).SetFeaturesRevision();
}
void IFeatureCache.CheckFeaturesRevision()
@ -42,10 +43,15 @@ namespace Microsoft.AspNet.Http.Features.Internal
if (_cachedFeaturesRevision != _features.Revision)
{
_request = null;
_cachedFeaturesRevision = _features.Revision;
((IFeatureCache)this).SetFeaturesRevision();
}
}
void IFeatureCache.SetFeaturesRevision()
{
_cachedFeaturesRevision = _features.Revision;
}
private IHttpRequestFeature HttpRequestFeature
{
get { return FeatureHelpers.GetAndCache(this, _features, ref _request); }

View File

@ -37,6 +37,7 @@ namespace Microsoft.AspNet.Http.Features.Internal
}
_features = features;
((IFeatureCache)this).SetFeaturesRevision();
}
void IFeatureCache.CheckFeaturesRevision()
@ -44,10 +45,15 @@ namespace Microsoft.AspNet.Http.Features.Internal
if (_cachedFeaturesRevision != _features.Revision)
{
_request = null;
_cachedFeaturesRevision = _features.Revision;
((IFeatureCache)this).SetFeaturesRevision();
}
}
void IFeatureCache.SetFeaturesRevision()
{
_cachedFeaturesRevision = _features.Revision;
}
private IHttpRequestFeature HttpRequestFeature
{
get { return FeatureHelpers.GetAndCache(this, _features, ref _request); }

View File

@ -16,6 +16,7 @@ namespace Microsoft.AspNet.Http.Features.Internal
public ResponseCookiesFeature(IFeatureCollection features)
{
_features = features;
((IFeatureCache)this).SetFeaturesRevision();
}
void IFeatureCache.CheckFeaturesRevision()
@ -23,10 +24,15 @@ namespace Microsoft.AspNet.Http.Features.Internal
if (_cachedFeaturesRevision != _features.Revision)
{
_response = null;
_cachedFeaturesRevision = _features.Revision;
((IFeatureCache)this).SetFeaturesRevision();
}
}
void IFeatureCache.SetFeaturesRevision()
{
_cachedFeaturesRevision = _features.Revision;
}
private IHttpResponseFeature HttpResponseFeature
{
get { return FeatureHelpers.GetAndCache(this, _features, ref _response); }

View File

@ -4,8 +4,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.WebSockets;
using System.Reflection;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNet.Http.Authentication.Internal;
using Microsoft.AspNet.Http.Features;
using Microsoft.AspNet.Http.Features.Internal;
using Xunit;
@ -146,6 +149,138 @@ 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);
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);
Assert.NotEqual(0, newFeatures.Count());
}
void TestCachedFeaturesAreNull(Type type, object value, IFeatureCollection features)
{
var fields = type
.GetFields(BindingFlags.NonPublic | BindingFlags.Instance)
.Where(f => f.FieldType.GetTypeInfo().IsInterface);
foreach (var field in fields)
{
if (field.FieldType == typeof(IFeatureCollection))
{
Assert.Same(features, field.GetValue(value));
}
else
{
Assert.Null(field.GetValue(value));
}
}
}
void TestCachedFeaturesAreSet(Type type, object value, IFeatureCollection features)
{
var properties = type
.GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
.Where(p => p.PropertyType.GetTypeInfo().IsInterface);
TestFeatureProperties(value, features, properties);
var fields = type
.GetFields(BindingFlags.NonPublic | BindingFlags.Instance)
.Where(f => f.FieldType.GetTypeInfo().IsInterface);
foreach (var field in fields)
{
if (field.FieldType == typeof(IFeatureCollection))
{
Assert.Same(features, field.GetValue(value));
}
else
{
var v = field.GetValue(value);
Assert.Same(features[field.FieldType], v);
Assert.NotNull(v);
}
}
}
private static void TestFeatureProperties(object value, IFeatureCollection features, IEnumerable<PropertyInfo> properties)
{
foreach (var property in properties)
{
if (property.PropertyType == typeof(IFeatureCollection))
{
Assert.Same(features, property.GetValue(value));
}
else
{
if (property.Name.Contains("Feature"))
{
var v = property.GetValue(value);
Assert.Same(features[property.PropertyType], v);
Assert.NotNull(v);
}
}
}
}
private HttpContext CreateContext()
{
@ -195,5 +330,21 @@ namespace Microsoft.AspNet.Http.Internal
{
public ISession Session { get; set; }
}
private class TestHttpWebSocketFeature : IHttpWebSocketFeature
{
public bool IsWebSocketRequest
{
get
{
throw new NotImplementedException();
}
}
public Task<WebSocket> AcceptAsync(WebSocketAcceptContext context)
{
throw new NotImplementedException();
}
}
}
}