diff --git a/src/Microsoft.AspNet.Http/Authentication/DefaultAuthenticationManager.cs b/src/Microsoft.AspNet.Http/Authentication/DefaultAuthenticationManager.cs index 4dcb8191..e138f98f 100644 --- a/src/Microsoft.AspNet.Http/Authentication/DefaultAuthenticationManager.cs +++ b/src/Microsoft.AspNet.Http/Authentication/DefaultAuthenticationManager.cs @@ -12,25 +12,44 @@ namespace Microsoft.AspNet.Http.Authentication.Internal { - public class DefaultAuthenticationManager : AuthenticationManager + public class DefaultAuthenticationManager : AuthenticationManager, IFeatureCache { private readonly IFeatureCollection _features; - private FeatureReference _authentication = FeatureReference.Default; - private FeatureReference _response = FeatureReference.Default; + private int _cachedFeaturesRevision = -1; + + private IHttpAuthenticationFeature _authentication; + private IHttpResponseFeature _response; public DefaultAuthenticationManager(IFeatureCollection features) { _features = features; } + void IFeatureCache.CheckFeaturesRevision() + { + if (_cachedFeaturesRevision != _features.Revision) + { + _authentication = null; + _response = null; + _cachedFeaturesRevision = _features.Revision; + } + } + private IHttpAuthenticationFeature HttpAuthenticationFeature { - get { return _authentication.Fetch(_features) ?? _authentication.Update(_features, new HttpAuthenticationFeature()); } + get + { + return FeatureHelpers.GetOrCreateAndCache( + this, + _features, + () => new HttpAuthenticationFeature(), + ref _authentication); + } } private IHttpResponseFeature HttpResponseFeature { - get { return _response.Fetch(_features); } + get { return FeatureHelpers.GetAndCache(this, _features, ref _response); } } public override IEnumerable GetAuthenticationSchemes() diff --git a/src/Microsoft.AspNet.Http/DefaultConnectionInfo.cs b/src/Microsoft.AspNet.Http/DefaultConnectionInfo.cs index 30a7ace3..c0fd4234 100644 --- a/src/Microsoft.AspNet.Http/DefaultConnectionInfo.cs +++ b/src/Microsoft.AspNet.Http/DefaultConnectionInfo.cs @@ -10,26 +10,51 @@ namespace Microsoft.AspNet.Http.Internal { - public class DefaultConnectionInfo : ConnectionInfo + public class DefaultConnectionInfo : ConnectionInfo, IFeatureCache { private readonly IFeatureCollection _features; + private int _cachedFeaturesRevision = -1; - private FeatureReference _connection = FeatureReference.Default; - private FeatureReference _tlsConnection = FeatureReference.Default; + private IHttpConnectionFeature _connection; + private ITlsConnectionFeature _tlsConnection; public DefaultConnectionInfo(IFeatureCollection features) { _features = features; } + void IFeatureCache.CheckFeaturesRevision() + { + if (_cachedFeaturesRevision != _features.Revision) + { + _connection = null; + _tlsConnection = null; + _cachedFeaturesRevision = _features.Revision; + } + } + private IHttpConnectionFeature HttpConnectionFeature { - get { return _connection.Fetch(_features) ?? _connection.Update(_features, new HttpConnectionFeature()); } + get + { + return FeatureHelpers.GetOrCreateAndCache( + this, + _features, + () => new HttpConnectionFeature(), + ref _connection); + } } private ITlsConnectionFeature TlsConnectionFeature { - get { return _tlsConnection.Fetch(_features) ?? _tlsConnection.Update(_features, new TlsConnectionFeature()); } + get + { + return FeatureHelpers.GetOrCreateAndCache( + this, + _features, + () => new TlsConnectionFeature(), + ref _tlsConnection); + } } public override IPAddress RemoteIpAddress diff --git a/src/Microsoft.AspNet.Http/DefaultHttpContext.cs b/src/Microsoft.AspNet.Http/DefaultHttpContext.cs index db0a4d27..089511e2 100644 --- a/src/Microsoft.AspNet.Http/DefaultHttpContext.cs +++ b/src/Microsoft.AspNet.Http/DefaultHttpContext.cs @@ -14,20 +14,22 @@ namespace Microsoft.AspNet.Http.Internal { - public class DefaultHttpContext : HttpContext + public class DefaultHttpContext : HttpContext, IFeatureCache { private readonly HttpRequest _request; private readonly HttpResponse _response; private readonly ConnectionInfo _connection; private readonly AuthenticationManager _authenticationManager; - private FeatureReference _items; - private FeatureReference _serviceProviders; - private FeatureReference _authentication; - private FeatureReference _lifetime; - private FeatureReference _session; + 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; public DefaultHttpContext() : this(new FeatureCollection()) @@ -43,37 +45,77 @@ public DefaultHttpContext(IFeatureCollection features) _response = new DefaultHttpResponse(this, features); _connection = new DefaultConnectionInfo(features); _authenticationManager = new DefaultAuthenticationManager(features); + } - _items = FeatureReference.Default; - _serviceProviders = FeatureReference.Default; - _authentication = FeatureReference.Default; - _lifetime = FeatureReference.Default; - _session = FeatureReference.Default; + void IFeatureCache.CheckFeaturesRevision() + { + if (_cachedFeaturesRevision !=_features.Revision) + { + _items = null; + _serviceProviders = null; + _authentication = null; + _lifetime = null; + _session = null; + _cachedFeaturesRevision = _features.Revision; + } } IItemsFeature ItemsFeature { - get { return _items.Fetch(_features) ?? _items.Update(_features, new ItemsFeature()); } + get + { + return FeatureHelpers.GetOrCreateAndCache( + this, + _features, + () => new ItemsFeature(), + ref _items); + } } IServiceProvidersFeature ServiceProvidersFeature { - get { return _serviceProviders.Fetch(_features) ?? _serviceProviders.Update(_features, new ServiceProvidersFeature()); } + get + { + return FeatureHelpers.GetOrCreateAndCache( + this, + _features, + () => new ServiceProvidersFeature(), + ref _serviceProviders); + } } private IHttpAuthenticationFeature HttpAuthenticationFeature { - get { return _authentication.Fetch(_features) ?? _authentication.Update(_features, new HttpAuthenticationFeature()); } + get + { + return FeatureHelpers.GetOrCreateAndCache( + this, + _features, + () => new HttpAuthenticationFeature(), + ref _authentication); + } } private IHttpRequestLifetimeFeature LifetimeFeature { - get { return _lifetime.Fetch(_features) ?? _lifetime.Update(_features, new HttpRequestLifetimeFeature()); } + get + { + return FeatureHelpers.GetOrCreateAndCache( + this, + _features, + () => new HttpRequestLifetimeFeature(), + ref _lifetime); + } } private ISessionFeature SessionFeature { - get { return _session.Fetch(_features); } + get { return FeatureHelpers.GetAndCache(this, _features, ref _session); } + set + { + _features.Set(value); + _session = value; + } } public override IFeatureCollection Features { get { return _features; } } @@ -143,7 +185,7 @@ public override ISession Session if (feature == null) { feature = new DefaultSessionFeature(); - _session.Update(_features, feature); + SessionFeature = feature; } feature.Session = value; } diff --git a/src/Microsoft.AspNet.Http/DefaultHttpRequest.cs b/src/Microsoft.AspNet.Http/DefaultHttpRequest.cs index 479ead6e..2bd87ba8 100644 --- a/src/Microsoft.AspNet.Http/DefaultHttpRequest.cs +++ b/src/Microsoft.AspNet.Http/DefaultHttpRequest.cs @@ -11,15 +11,16 @@ namespace Microsoft.AspNet.Http.Internal { - public class DefaultHttpRequest : HttpRequest + public class DefaultHttpRequest : HttpRequest, IFeatureCache { private readonly DefaultHttpContext _context; private readonly IFeatureCollection _features; + private int _cachedFeaturesRevision = -1; - private FeatureReference _request = FeatureReference.Default; - private FeatureReference _query = FeatureReference.Default; - private FeatureReference _form = FeatureReference.Default; - private FeatureReference _cookies = FeatureReference.Default; + private IHttpRequestFeature _request; + private IQueryFeature _query; + private IFormFeature _form; + private IRequestCookiesFeature _cookies; public DefaultHttpRequest(DefaultHttpContext context, IFeatureCollection features) { @@ -27,24 +28,58 @@ public DefaultHttpRequest(DefaultHttpContext context, IFeatureCollection feature _features = features; } + void IFeatureCache.CheckFeaturesRevision() + { + if (_cachedFeaturesRevision != _features.Revision) + { + _request = null; + _query = null; + _form = null; + _cookies = null; + _cachedFeaturesRevision = _features.Revision; + } + } + private IHttpRequestFeature HttpRequestFeature { - get { return _request.Fetch(_features); } + get { return FeatureHelpers.GetAndCache(this, _features, ref _request); } } private IQueryFeature QueryFeature { - get { return _query.Fetch(_features) ?? _query.Update(_features, new QueryFeature(_features)); } + get + { + return FeatureHelpers.GetOrCreateAndCache( + this, + _features, + (f) => new QueryFeature(f), + ref _query); + } } private IFormFeature FormFeature { - get { return _form.Fetch(_features) ?? _form.Update(_features, new FormFeature(this)); } + get + { + return FeatureHelpers.GetOrCreateAndCache( + this, + _features, + this, + (r) => new FormFeature(r), + ref _form); + } } private IRequestCookiesFeature RequestCookiesFeature { - get { return _cookies.Fetch(_features) ?? _cookies.Update(_features, new RequestCookiesFeature(_features)); } + get + { + return FeatureHelpers.GetOrCreateAndCache( + this, + _features, + (f) => new RequestCookiesFeature(f), + ref _cookies); + } } public override HttpContext HttpContext { get { return _context; } } diff --git a/src/Microsoft.AspNet.Http/DefaultHttpResponse.cs b/src/Microsoft.AspNet.Http/DefaultHttpResponse.cs index e5f09f7c..0d728fd3 100644 --- a/src/Microsoft.AspNet.Http/DefaultHttpResponse.cs +++ b/src/Microsoft.AspNet.Http/DefaultHttpResponse.cs @@ -10,12 +10,14 @@ namespace Microsoft.AspNet.Http.Internal { - public class DefaultHttpResponse : HttpResponse + public class DefaultHttpResponse : HttpResponse, IFeatureCache { private readonly DefaultHttpContext _context; private readonly IFeatureCollection _features; - private FeatureReference _response = FeatureReference.Default; - private FeatureReference _cookies = FeatureReference.Default; + private int _cachedFeaturesRevision = -1; + + private IHttpResponseFeature _response; + private IResponseCookiesFeature _cookies; public DefaultHttpResponse(DefaultHttpContext context, IFeatureCollection features) { @@ -23,14 +25,31 @@ public DefaultHttpResponse(DefaultHttpContext context, IFeatureCollection featur _features = features; } + void IFeatureCache.CheckFeaturesRevision() + { + if (_cachedFeaturesRevision != _features.Revision) + { + _response = null; + _cookies = null; + _cachedFeaturesRevision = _features.Revision; + } + } + private IHttpResponseFeature HttpResponseFeature { - get { return _response.Fetch(_features); } + get { return FeatureHelpers.GetAndCache(this, _features, ref _response); } } private IResponseCookiesFeature ResponseCookiesFeature { - get { return _cookies.Fetch(_features) ?? _cookies.Update(_features, new ResponseCookiesFeature(_features)); } + get + { + return FeatureHelpers.GetOrCreateAndCache( + this, + _features, + (f) => new ResponseCookiesFeature(f), + ref _cookies); + } } public override HttpContext HttpContext { get { return _context; } } diff --git a/src/Microsoft.AspNet.Http/DefaultWebSocketManager.cs b/src/Microsoft.AspNet.Http/DefaultWebSocketManager.cs index 9493859f..454b0fa6 100644 --- a/src/Microsoft.AspNet.Http/DefaultWebSocketManager.cs +++ b/src/Microsoft.AspNet.Http/DefaultWebSocketManager.cs @@ -10,25 +10,37 @@ namespace Microsoft.AspNet.Http.Internal { - public class DefaultWebSocketManager : WebSocketManager + public class DefaultWebSocketManager : WebSocketManager, IFeatureCache { private IFeatureCollection _features; - private FeatureReference _request = FeatureReference.Default; - private FeatureReference _webSockets = FeatureReference.Default; + private int _cachedFeaturesRevision = -1; + + private IHttpRequestFeature _request; + private IHttpWebSocketFeature _webSockets; public DefaultWebSocketManager(IFeatureCollection features) { _features = features; } + void IFeatureCache.CheckFeaturesRevision() + { + if (_cachedFeaturesRevision != _features.Revision) + { + _request = null; + _webSockets = null; + _cachedFeaturesRevision = _features.Revision; + } + } + private IHttpRequestFeature HttpRequestFeature { - get { return _request.Fetch(_features); } + get { return FeatureHelpers.GetAndCache(this, _features, ref _request); } } private IHttpWebSocketFeature WebSocketFeature { - get { return _webSockets.Fetch(_features); } + get { return FeatureHelpers.GetAndCache(this, _features, ref _webSockets); } } public override bool IsWebSocketRequest diff --git a/src/Microsoft.AspNet.Http/Features/FeatureHelpers.cs b/src/Microsoft.AspNet.Http/Features/FeatureHelpers.cs new file mode 100644 index 00000000..142e0e2a --- /dev/null +++ b/src/Microsoft.AspNet.Http/Features/FeatureHelpers.cs @@ -0,0 +1,93 @@ +// 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 sealed class FeatureHelpers + { + public static T GetAndCache( + IFeatureCache cache, + IFeatureCollection features, + ref T cachedObject) + { + cache.CheckFeaturesRevision(); + + T obj = cachedObject; + if (obj == null) + { + obj = features.Get(); + cachedObject = obj; + } + return obj; + } + + public static T GetOrCreateAndCache( + IFeatureCache cache, + IFeatureCollection features, + Func factory, + ref T cachedObject) + { + cache.CheckFeaturesRevision(); + + T obj = cachedObject; + if (obj == null) + { + obj = features.Get(); + if (obj == null) + { + obj = factory(); + cachedObject = obj; + features.Set(obj); + } + } + return obj; + } + + public static T GetOrCreateAndCache( + IFeatureCache cache, + IFeatureCollection features, + Func factory, + ref T cachedObject) + { + cache.CheckFeaturesRevision(); + + T obj = cachedObject; + if (obj == null) + { + obj = features.Get(); + if (obj == null) + { + obj = factory(features); + cachedObject = obj; + features.Set(obj); + } + } + return obj; + } + + public static T GetOrCreateAndCache( + IFeatureCache cache, + IFeatureCollection features, + HttpRequest request, + Func factory, + ref T cachedObject) + { + cache.CheckFeaturesRevision(); + + T obj = cachedObject; + if (obj == null) + { + obj = features.Get(); + if (obj == null) + { + obj = factory(request); + cachedObject = obj; + features.Set(obj); + } + } + return obj; + } + } +} diff --git a/src/Microsoft.AspNet.Http/Features/IFeatureCache.cs b/src/Microsoft.AspNet.Http/Features/IFeatureCache.cs new file mode 100644 index 00000000..80db1ee3 --- /dev/null +++ b/src/Microsoft.AspNet.Http/Features/IFeatureCache.cs @@ -0,0 +1,10 @@ +// 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(); + } +} diff --git a/src/Microsoft.AspNet.Http/Features/QueryFeature.cs b/src/Microsoft.AspNet.Http/Features/QueryFeature.cs index 5dd7ca91..4a275ec6 100644 --- a/src/Microsoft.AspNet.Http/Features/QueryFeature.cs +++ b/src/Microsoft.AspNet.Http/Features/QueryFeature.cs @@ -9,10 +9,12 @@ namespace Microsoft.AspNet.Http.Features.Internal { - public class QueryFeature : IQueryFeature + public class QueryFeature : IQueryFeature, IFeatureCache { private readonly IFeatureCollection _features; - private FeatureReference _request = FeatureReference.Default; + private int _cachedFeaturesRevision = -1; + + private IHttpRequestFeature _request; private string _original; private IReadableStringCollection _parsedValues; @@ -46,6 +48,20 @@ public QueryFeature(IFeatureCollection features) _features = features; } + void IFeatureCache.CheckFeaturesRevision() + { + if (_cachedFeaturesRevision != _features.Revision) + { + _request = null; + _cachedFeaturesRevision = _features.Revision; + } + } + + private IHttpRequestFeature HttpRequestFeature + { + get { return FeatureHelpers.GetAndCache(this, _features, ref _request); } + } + public IReadableStringCollection Query { get @@ -55,7 +71,7 @@ public IReadableStringCollection Query return _parsedValues ?? ReadableStringCollection.Empty; } - var current = _request.Fetch(_features).QueryString; + var current = HttpRequestFeature.QueryString; if (_parsedValues == null || !string.Equals(_original, current, StringComparison.Ordinal)) { _original = current; @@ -71,12 +87,12 @@ public IReadableStringCollection Query if (value == null) { _original = string.Empty; - _request.Fetch(_features).QueryString = string.Empty; + HttpRequestFeature.QueryString = string.Empty; } else { _original = QueryString.Create(_parsedValues).ToString(); - _request.Fetch(_features).QueryString = _original; + HttpRequestFeature.QueryString = _original; } } } diff --git a/src/Microsoft.AspNet.Http/Features/RequestCookiesFeature.cs b/src/Microsoft.AspNet.Http/Features/RequestCookiesFeature.cs index 0eb44ef1..2a10f15a 100644 --- a/src/Microsoft.AspNet.Http/Features/RequestCookiesFeature.cs +++ b/src/Microsoft.AspNet.Http/Features/RequestCookiesFeature.cs @@ -10,10 +10,12 @@ namespace Microsoft.AspNet.Http.Features.Internal { - public class RequestCookiesFeature : IRequestCookiesFeature + public class RequestCookiesFeature : IRequestCookiesFeature, IFeatureCache { private readonly IFeatureCollection _features; - private readonly FeatureReference _request = FeatureReference.Default; + private int _cachedFeaturesRevision = -1; + + private IHttpRequestFeature _request; private StringValues _original; private IReadableStringCollection _parsedValues; @@ -43,6 +45,20 @@ public RequestCookiesFeature(IFeatureCollection features) _features = features; } + void IFeatureCache.CheckFeaturesRevision() + { + if (_cachedFeaturesRevision != _features.Revision) + { + _request = null; + _cachedFeaturesRevision = _features.Revision; + } + } + + private IHttpRequestFeature HttpRequestFeature + { + get { return FeatureHelpers.GetAndCache(this, _features, ref _request); } + } + public IReadableStringCollection Cookies { get @@ -52,7 +68,7 @@ public IReadableStringCollection Cookies return _parsedValues ?? ReadableStringCollection.Empty; } - var headers = _request.Fetch(_features).Headers; + var headers = HttpRequestFeature.Headers; StringValues current; if (!headers.TryGetValue(HeaderNames.Cookie, out current)) { @@ -81,7 +97,7 @@ public IReadableStringCollection Cookies { if (_parsedValues == null || _parsedValues.Count == 0) { - _request.Fetch(_features).Headers.Remove(HeaderNames.Cookie); + HttpRequestFeature.Headers.Remove(HeaderNames.Cookie); } else { @@ -94,7 +110,7 @@ public IReadableStringCollection Cookies } } _original = headers.ToArray(); - _request.Fetch(_features).Headers[HeaderNames.Cookie] = _original; + HttpRequestFeature.Headers[HeaderNames.Cookie] = _original; } } } diff --git a/src/Microsoft.AspNet.Http/Features/ResponseCookiesFeature.cs b/src/Microsoft.AspNet.Http/Features/ResponseCookiesFeature.cs index 96504f1f..d56ba114 100644 --- a/src/Microsoft.AspNet.Http/Features/ResponseCookiesFeature.cs +++ b/src/Microsoft.AspNet.Http/Features/ResponseCookiesFeature.cs @@ -5,10 +5,12 @@ namespace Microsoft.AspNet.Http.Features.Internal { - public class ResponseCookiesFeature : IResponseCookiesFeature + public class ResponseCookiesFeature : IResponseCookiesFeature, IFeatureCache { private readonly IFeatureCollection _features; - private readonly FeatureReference _request = FeatureReference.Default; + private int _cachedFeaturesRevision = -1; + + private IHttpResponseFeature _response; private IResponseCookies _cookiesCollection; public ResponseCookiesFeature(IFeatureCollection features) @@ -16,16 +18,29 @@ public ResponseCookiesFeature(IFeatureCollection features) _features = features; } + void IFeatureCache.CheckFeaturesRevision() + { + if (_cachedFeaturesRevision != _features.Revision) + { + _response = null; + _cachedFeaturesRevision = _features.Revision; + } + } + + private IHttpResponseFeature HttpResponseFeature + { + get { return FeatureHelpers.GetAndCache(this, _features, ref _response); } + } + public IResponseCookies Cookies { get { if (_cookiesCollection == null) { - var headers = _request.Fetch(_features).Headers; + var headers = HttpResponseFeature.Headers; _cookiesCollection = new ResponseCookies(new HeaderDictionary(headers)); } - return _cookiesCollection; } }