Merge pull request #420 from benaadams/Features-perf

Remove FeatureReference indirection
This commit is contained in:
David Fowler 2015-10-15 04:03:23 -07:00
commit d565659de7
11 changed files with 362 additions and 60 deletions

View File

@ -12,25 +12,44 @@ using Microsoft.AspNet.Http.Features.Authentication.Internal;
namespace Microsoft.AspNet.Http.Authentication.Internal namespace Microsoft.AspNet.Http.Authentication.Internal
{ {
public class DefaultAuthenticationManager : AuthenticationManager public class DefaultAuthenticationManager : AuthenticationManager, IFeatureCache
{ {
private readonly IFeatureCollection _features; private readonly IFeatureCollection _features;
private FeatureReference<IHttpAuthenticationFeature> _authentication = FeatureReference<IHttpAuthenticationFeature>.Default; private int _cachedFeaturesRevision = -1;
private FeatureReference<IHttpResponseFeature> _response = FeatureReference<IHttpResponseFeature>.Default;
private IHttpAuthenticationFeature _authentication;
private IHttpResponseFeature _response;
public DefaultAuthenticationManager(IFeatureCollection features) public DefaultAuthenticationManager(IFeatureCollection features)
{ {
_features = features; _features = features;
} }
void IFeatureCache.CheckFeaturesRevision()
{
if (_cachedFeaturesRevision != _features.Revision)
{
_authentication = null;
_response = null;
_cachedFeaturesRevision = _features.Revision;
}
}
private IHttpAuthenticationFeature HttpAuthenticationFeature 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 private IHttpResponseFeature HttpResponseFeature
{ {
get { return _response.Fetch(_features); } get { return FeatureHelpers.GetAndCache(this, _features, ref _response); }
} }
public override IEnumerable<AuthenticationDescription> GetAuthenticationSchemes() public override IEnumerable<AuthenticationDescription> GetAuthenticationSchemes()

View File

@ -10,26 +10,51 @@ using Microsoft.AspNet.Http.Features.Internal;
namespace Microsoft.AspNet.Http.Internal namespace Microsoft.AspNet.Http.Internal
{ {
public class DefaultConnectionInfo : ConnectionInfo public class DefaultConnectionInfo : ConnectionInfo, IFeatureCache
{ {
private readonly IFeatureCollection _features; private readonly IFeatureCollection _features;
private int _cachedFeaturesRevision = -1;
private FeatureReference<IHttpConnectionFeature> _connection = FeatureReference<IHttpConnectionFeature>.Default; private IHttpConnectionFeature _connection;
private FeatureReference<ITlsConnectionFeature> _tlsConnection = FeatureReference<ITlsConnectionFeature>.Default; private ITlsConnectionFeature _tlsConnection;
public DefaultConnectionInfo(IFeatureCollection features) public DefaultConnectionInfo(IFeatureCollection features)
{ {
_features = features; _features = features;
} }
void IFeatureCache.CheckFeaturesRevision()
{
if (_cachedFeaturesRevision != _features.Revision)
{
_connection = null;
_tlsConnection = null;
_cachedFeaturesRevision = _features.Revision;
}
}
private IHttpConnectionFeature HttpConnectionFeature 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 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 public override IPAddress RemoteIpAddress

View File

@ -14,20 +14,22 @@ using Microsoft.AspNet.Http.Features.Internal;
namespace Microsoft.AspNet.Http.Internal namespace Microsoft.AspNet.Http.Internal
{ {
public class DefaultHttpContext : HttpContext public class DefaultHttpContext : HttpContext, IFeatureCache
{ {
private readonly HttpRequest _request; private readonly HttpRequest _request;
private readonly HttpResponse _response; private readonly HttpResponse _response;
private readonly ConnectionInfo _connection; private readonly ConnectionInfo _connection;
private readonly AuthenticationManager _authenticationManager; private readonly AuthenticationManager _authenticationManager;
private FeatureReference<IItemsFeature> _items; private IItemsFeature _items;
private FeatureReference<IServiceProvidersFeature> _serviceProviders; private IServiceProvidersFeature _serviceProviders;
private FeatureReference<IHttpAuthenticationFeature> _authentication; private IHttpAuthenticationFeature _authentication;
private FeatureReference<IHttpRequestLifetimeFeature> _lifetime; private IHttpRequestLifetimeFeature _lifetime;
private FeatureReference<ISessionFeature> _session; private ISessionFeature _session;
private WebSocketManager _websockets; private WebSocketManager _websockets;
private IFeatureCollection _features; private IFeatureCollection _features;
private int _cachedFeaturesRevision = -1;
public DefaultHttpContext() public DefaultHttpContext()
: this(new FeatureCollection()) : this(new FeatureCollection())
@ -43,37 +45,77 @@ namespace Microsoft.AspNet.Http.Internal
_response = new DefaultHttpResponse(this, features); _response = new DefaultHttpResponse(this, features);
_connection = new DefaultConnectionInfo(features); _connection = new DefaultConnectionInfo(features);
_authenticationManager = new DefaultAuthenticationManager(features); _authenticationManager = new DefaultAuthenticationManager(features);
}
_items = FeatureReference<IItemsFeature>.Default; void IFeatureCache.CheckFeaturesRevision()
_serviceProviders = FeatureReference<IServiceProvidersFeature>.Default; {
_authentication = FeatureReference<IHttpAuthenticationFeature>.Default; if (_cachedFeaturesRevision !=_features.Revision)
_lifetime = FeatureReference<IHttpRequestLifetimeFeature>.Default; {
_session = FeatureReference<ISessionFeature>.Default; _items = null;
_serviceProviders = null;
_authentication = null;
_lifetime = null;
_session = null;
_cachedFeaturesRevision = _features.Revision;
}
} }
IItemsFeature ItemsFeature IItemsFeature ItemsFeature
{ {
get { return _items.Fetch(_features) ?? _items.Update(_features, new ItemsFeature()); } get
{
return FeatureHelpers.GetOrCreateAndCache(
this,
_features,
() => new ItemsFeature(),
ref _items);
}
} }
IServiceProvidersFeature ServiceProvidersFeature 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 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 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 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; } } public override IFeatureCollection Features { get { return _features; } }
@ -143,7 +185,7 @@ namespace Microsoft.AspNet.Http.Internal
if (feature == null) if (feature == null)
{ {
feature = new DefaultSessionFeature(); feature = new DefaultSessionFeature();
_session.Update(_features, feature); SessionFeature = feature;
} }
feature.Session = value; feature.Session = value;
} }

View File

@ -11,15 +11,16 @@ using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Http.Internal namespace Microsoft.AspNet.Http.Internal
{ {
public class DefaultHttpRequest : HttpRequest public class DefaultHttpRequest : HttpRequest, IFeatureCache
{ {
private readonly DefaultHttpContext _context; private readonly DefaultHttpContext _context;
private readonly IFeatureCollection _features; private readonly IFeatureCollection _features;
private int _cachedFeaturesRevision = -1;
private FeatureReference<IHttpRequestFeature> _request = FeatureReference<IHttpRequestFeature>.Default; private IHttpRequestFeature _request;
private FeatureReference<IQueryFeature> _query = FeatureReference<IQueryFeature>.Default; private IQueryFeature _query;
private FeatureReference<IFormFeature> _form = FeatureReference<IFormFeature>.Default; private IFormFeature _form;
private FeatureReference<IRequestCookiesFeature> _cookies = FeatureReference<IRequestCookiesFeature>.Default; private IRequestCookiesFeature _cookies;
public DefaultHttpRequest(DefaultHttpContext context, IFeatureCollection features) public DefaultHttpRequest(DefaultHttpContext context, IFeatureCollection features)
{ {
@ -27,24 +28,58 @@ namespace Microsoft.AspNet.Http.Internal
_features = features; _features = features;
} }
void IFeatureCache.CheckFeaturesRevision()
{
if (_cachedFeaturesRevision != _features.Revision)
{
_request = null;
_query = null;
_form = null;
_cookies = null;
_cachedFeaturesRevision = _features.Revision;
}
}
private IHttpRequestFeature HttpRequestFeature private IHttpRequestFeature HttpRequestFeature
{ {
get { return _request.Fetch(_features); } get { return FeatureHelpers.GetAndCache(this, _features, ref _request); }
} }
private IQueryFeature QueryFeature 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 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 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; } } public override HttpContext HttpContext { get { return _context; } }

View File

@ -10,12 +10,14 @@ using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Http.Internal namespace Microsoft.AspNet.Http.Internal
{ {
public class DefaultHttpResponse : HttpResponse public class DefaultHttpResponse : HttpResponse, IFeatureCache
{ {
private readonly DefaultHttpContext _context; private readonly DefaultHttpContext _context;
private readonly IFeatureCollection _features; private readonly IFeatureCollection _features;
private FeatureReference<IHttpResponseFeature> _response = FeatureReference<IHttpResponseFeature>.Default; private int _cachedFeaturesRevision = -1;
private FeatureReference<IResponseCookiesFeature> _cookies = FeatureReference<IResponseCookiesFeature>.Default;
private IHttpResponseFeature _response;
private IResponseCookiesFeature _cookies;
public DefaultHttpResponse(DefaultHttpContext context, IFeatureCollection features) public DefaultHttpResponse(DefaultHttpContext context, IFeatureCollection features)
{ {
@ -23,14 +25,31 @@ namespace Microsoft.AspNet.Http.Internal
_features = features; _features = features;
} }
void IFeatureCache.CheckFeaturesRevision()
{
if (_cachedFeaturesRevision != _features.Revision)
{
_response = null;
_cookies = null;
_cachedFeaturesRevision = _features.Revision;
}
}
private IHttpResponseFeature HttpResponseFeature private IHttpResponseFeature HttpResponseFeature
{ {
get { return _response.Fetch(_features); } get { return FeatureHelpers.GetAndCache(this, _features, ref _response); }
} }
private IResponseCookiesFeature ResponseCookiesFeature 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; } } public override HttpContext HttpContext { get { return _context; } }

View File

@ -10,25 +10,37 @@ using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Http.Internal namespace Microsoft.AspNet.Http.Internal
{ {
public class DefaultWebSocketManager : WebSocketManager public class DefaultWebSocketManager : WebSocketManager, IFeatureCache
{ {
private IFeatureCollection _features; private IFeatureCollection _features;
private FeatureReference<IHttpRequestFeature> _request = FeatureReference<IHttpRequestFeature>.Default; private int _cachedFeaturesRevision = -1;
private FeatureReference<IHttpWebSocketFeature> _webSockets = FeatureReference<IHttpWebSocketFeature>.Default;
private IHttpRequestFeature _request;
private IHttpWebSocketFeature _webSockets;
public DefaultWebSocketManager(IFeatureCollection features) public DefaultWebSocketManager(IFeatureCollection features)
{ {
_features = features; _features = features;
} }
void IFeatureCache.CheckFeaturesRevision()
{
if (_cachedFeaturesRevision != _features.Revision)
{
_request = null;
_webSockets = null;
_cachedFeaturesRevision = _features.Revision;
}
}
private IHttpRequestFeature HttpRequestFeature private IHttpRequestFeature HttpRequestFeature
{ {
get { return _request.Fetch(_features); } get { return FeatureHelpers.GetAndCache(this, _features, ref _request); }
} }
private IHttpWebSocketFeature WebSocketFeature private IHttpWebSocketFeature WebSocketFeature
{ {
get { return _webSockets.Fetch(_features); } get { return FeatureHelpers.GetAndCache(this, _features, ref _webSockets); }
} }
public override bool IsWebSocketRequest public override bool IsWebSocketRequest

View File

@ -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<T>(
IFeatureCache cache,
IFeatureCollection features,
ref T cachedObject)
{
cache.CheckFeaturesRevision();
T obj = cachedObject;
if (obj == null)
{
obj = features.Get<T>();
cachedObject = obj;
}
return obj;
}
public static T GetOrCreateAndCache<T>(
IFeatureCache cache,
IFeatureCollection features,
Func<T> factory,
ref T cachedObject)
{
cache.CheckFeaturesRevision();
T obj = cachedObject;
if (obj == null)
{
obj = features.Get<T>();
if (obj == null)
{
obj = factory();
cachedObject = obj;
features.Set(obj);
}
}
return obj;
}
public static T GetOrCreateAndCache<T>(
IFeatureCache cache,
IFeatureCollection features,
Func<IFeatureCollection, T> factory,
ref T cachedObject)
{
cache.CheckFeaturesRevision();
T obj = cachedObject;
if (obj == null)
{
obj = features.Get<T>();
if (obj == null)
{
obj = factory(features);
cachedObject = obj;
features.Set(obj);
}
}
return obj;
}
public static T GetOrCreateAndCache<T>(
IFeatureCache cache,
IFeatureCollection features,
HttpRequest request,
Func<HttpRequest, T> factory,
ref T cachedObject)
{
cache.CheckFeaturesRevision();
T obj = cachedObject;
if (obj == null)
{
obj = features.Get<T>();
if (obj == null)
{
obj = factory(request);
cachedObject = obj;
features.Set(obj);
}
}
return obj;
}
}
}

View File

@ -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();
}
}

View File

@ -9,10 +9,12 @@ using Microsoft.Extensions.Primitives;
namespace Microsoft.AspNet.Http.Features.Internal namespace Microsoft.AspNet.Http.Features.Internal
{ {
public class QueryFeature : IQueryFeature public class QueryFeature : IQueryFeature, IFeatureCache
{ {
private readonly IFeatureCollection _features; private readonly IFeatureCollection _features;
private FeatureReference<IHttpRequestFeature> _request = FeatureReference<IHttpRequestFeature>.Default; private int _cachedFeaturesRevision = -1;
private IHttpRequestFeature _request;
private string _original; private string _original;
private IReadableStringCollection _parsedValues; private IReadableStringCollection _parsedValues;
@ -46,6 +48,20 @@ namespace Microsoft.AspNet.Http.Features.Internal
_features = 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 public IReadableStringCollection Query
{ {
get get
@ -55,7 +71,7 @@ namespace Microsoft.AspNet.Http.Features.Internal
return _parsedValues ?? ReadableStringCollection.Empty; return _parsedValues ?? ReadableStringCollection.Empty;
} }
var current = _request.Fetch(_features).QueryString; var current = HttpRequestFeature.QueryString;
if (_parsedValues == null || !string.Equals(_original, current, StringComparison.Ordinal)) if (_parsedValues == null || !string.Equals(_original, current, StringComparison.Ordinal))
{ {
_original = current; _original = current;
@ -71,12 +87,12 @@ namespace Microsoft.AspNet.Http.Features.Internal
if (value == null) if (value == null)
{ {
_original = string.Empty; _original = string.Empty;
_request.Fetch(_features).QueryString = string.Empty; HttpRequestFeature.QueryString = string.Empty;
} }
else else
{ {
_original = QueryString.Create(_parsedValues).ToString(); _original = QueryString.Create(_parsedValues).ToString();
_request.Fetch(_features).QueryString = _original; HttpRequestFeature.QueryString = _original;
} }
} }
} }

View File

@ -10,10 +10,12 @@ using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Http.Features.Internal namespace Microsoft.AspNet.Http.Features.Internal
{ {
public class RequestCookiesFeature : IRequestCookiesFeature public class RequestCookiesFeature : IRequestCookiesFeature, IFeatureCache
{ {
private readonly IFeatureCollection _features; private readonly IFeatureCollection _features;
private readonly FeatureReference<IHttpRequestFeature> _request = FeatureReference<IHttpRequestFeature>.Default; private int _cachedFeaturesRevision = -1;
private IHttpRequestFeature _request;
private StringValues _original; private StringValues _original;
private IReadableStringCollection _parsedValues; private IReadableStringCollection _parsedValues;
@ -43,6 +45,20 @@ namespace Microsoft.AspNet.Http.Features.Internal
_features = 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 public IReadableStringCollection Cookies
{ {
get get
@ -52,7 +68,7 @@ namespace Microsoft.AspNet.Http.Features.Internal
return _parsedValues ?? ReadableStringCollection.Empty; return _parsedValues ?? ReadableStringCollection.Empty;
} }
var headers = _request.Fetch(_features).Headers; var headers = HttpRequestFeature.Headers;
StringValues current; StringValues current;
if (!headers.TryGetValue(HeaderNames.Cookie, out current)) if (!headers.TryGetValue(HeaderNames.Cookie, out current))
{ {
@ -81,7 +97,7 @@ namespace Microsoft.AspNet.Http.Features.Internal
{ {
if (_parsedValues == null || _parsedValues.Count == 0) if (_parsedValues == null || _parsedValues.Count == 0)
{ {
_request.Fetch(_features).Headers.Remove(HeaderNames.Cookie); HttpRequestFeature.Headers.Remove(HeaderNames.Cookie);
} }
else else
{ {
@ -94,7 +110,7 @@ namespace Microsoft.AspNet.Http.Features.Internal
} }
} }
_original = headers.ToArray(); _original = headers.ToArray();
_request.Fetch(_features).Headers[HeaderNames.Cookie] = _original; HttpRequestFeature.Headers[HeaderNames.Cookie] = _original;
} }
} }
} }

View File

@ -5,10 +5,12 @@ using Microsoft.AspNet.Http.Internal;
namespace Microsoft.AspNet.Http.Features.Internal namespace Microsoft.AspNet.Http.Features.Internal
{ {
public class ResponseCookiesFeature : IResponseCookiesFeature public class ResponseCookiesFeature : IResponseCookiesFeature, IFeatureCache
{ {
private readonly IFeatureCollection _features; private readonly IFeatureCollection _features;
private readonly FeatureReference<IHttpResponseFeature> _request = FeatureReference<IHttpResponseFeature>.Default; private int _cachedFeaturesRevision = -1;
private IHttpResponseFeature _response;
private IResponseCookies _cookiesCollection; private IResponseCookies _cookiesCollection;
public ResponseCookiesFeature(IFeatureCollection features) public ResponseCookiesFeature(IFeatureCollection features)
@ -16,16 +18,29 @@ namespace Microsoft.AspNet.Http.Features.Internal
_features = 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 public IResponseCookies Cookies
{ {
get get
{ {
if (_cookiesCollection == null) if (_cookiesCollection == null)
{ {
var headers = _request.Fetch(_features).Headers; var headers = HttpResponseFeature.Headers;
_cookiesCollection = new ResponseCookies(new HeaderDictionary(headers)); _cookiesCollection = new ResponseCookies(new HeaderDictionary(headers));
} }
return _cookiesCollection; return _cookiesCollection;
} }
} }