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
{
public class DefaultAuthenticationManager : AuthenticationManager
public class DefaultAuthenticationManager : AuthenticationManager, IFeatureCache
{
private readonly IFeatureCollection _features;
private FeatureReference<IHttpAuthenticationFeature> _authentication = FeatureReference<IHttpAuthenticationFeature>.Default;
private FeatureReference<IHttpResponseFeature> _response = FeatureReference<IHttpResponseFeature>.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<AuthenticationDescription> GetAuthenticationSchemes()

View File

@ -10,26 +10,51 @@ using Microsoft.AspNet.Http.Features.Internal;
namespace Microsoft.AspNet.Http.Internal
{
public class DefaultConnectionInfo : ConnectionInfo
public class DefaultConnectionInfo : ConnectionInfo, IFeatureCache
{
private readonly IFeatureCollection _features;
private int _cachedFeaturesRevision = -1;
private FeatureReference<IHttpConnectionFeature> _connection = FeatureReference<IHttpConnectionFeature>.Default;
private FeatureReference<ITlsConnectionFeature> _tlsConnection = FeatureReference<ITlsConnectionFeature>.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

View File

@ -14,20 +14,22 @@ using Microsoft.AspNet.Http.Features.Internal;
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<IItemsFeature> _items;
private FeatureReference<IServiceProvidersFeature> _serviceProviders;
private FeatureReference<IHttpAuthenticationFeature> _authentication;
private FeatureReference<IHttpRequestLifetimeFeature> _lifetime;
private FeatureReference<ISessionFeature> _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 @@ namespace Microsoft.AspNet.Http.Internal
_response = new DefaultHttpResponse(this, features);
_connection = new DefaultConnectionInfo(features);
_authenticationManager = new DefaultAuthenticationManager(features);
}
_items = FeatureReference<IItemsFeature>.Default;
_serviceProviders = FeatureReference<IServiceProvidersFeature>.Default;
_authentication = FeatureReference<IHttpAuthenticationFeature>.Default;
_lifetime = FeatureReference<IHttpRequestLifetimeFeature>.Default;
_session = FeatureReference<ISessionFeature>.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 @@ namespace Microsoft.AspNet.Http.Internal
if (feature == null)
{
feature = new DefaultSessionFeature();
_session.Update(_features, feature);
SessionFeature = feature;
}
feature.Session = value;
}

View File

@ -11,15 +11,16 @@ using Microsoft.Net.Http.Headers;
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<IHttpRequestFeature> _request = FeatureReference<IHttpRequestFeature>.Default;
private FeatureReference<IQueryFeature> _query = FeatureReference<IQueryFeature>.Default;
private FeatureReference<IFormFeature> _form = FeatureReference<IFormFeature>.Default;
private FeatureReference<IRequestCookiesFeature> _cookies = FeatureReference<IRequestCookiesFeature>.Default;
private IHttpRequestFeature _request;
private IQueryFeature _query;
private IFormFeature _form;
private IRequestCookiesFeature _cookies;
public DefaultHttpRequest(DefaultHttpContext context, IFeatureCollection features)
{
@ -27,24 +28,58 @@ namespace Microsoft.AspNet.Http.Internal
_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; } }

View File

@ -10,12 +10,14 @@ using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Http.Internal
{
public class DefaultHttpResponse : HttpResponse
public class DefaultHttpResponse : HttpResponse, IFeatureCache
{
private readonly DefaultHttpContext _context;
private readonly IFeatureCollection _features;
private FeatureReference<IHttpResponseFeature> _response = FeatureReference<IHttpResponseFeature>.Default;
private FeatureReference<IResponseCookiesFeature> _cookies = FeatureReference<IResponseCookiesFeature>.Default;
private int _cachedFeaturesRevision = -1;
private IHttpResponseFeature _response;
private IResponseCookiesFeature _cookies;
public DefaultHttpResponse(DefaultHttpContext context, IFeatureCollection features)
{
@ -23,14 +25,31 @@ namespace Microsoft.AspNet.Http.Internal
_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; } }

View File

@ -10,25 +10,37 @@ using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Http.Internal
{
public class DefaultWebSocketManager : WebSocketManager
public class DefaultWebSocketManager : WebSocketManager, IFeatureCache
{
private IFeatureCollection _features;
private FeatureReference<IHttpRequestFeature> _request = FeatureReference<IHttpRequestFeature>.Default;
private FeatureReference<IHttpWebSocketFeature> _webSockets = FeatureReference<IHttpWebSocketFeature>.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

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
{
public class QueryFeature : IQueryFeature
public class QueryFeature : IQueryFeature, IFeatureCache
{
private readonly IFeatureCollection _features;
private FeatureReference<IHttpRequestFeature> _request = FeatureReference<IHttpRequestFeature>.Default;
private int _cachedFeaturesRevision = -1;
private IHttpRequestFeature _request;
private string _original;
private IReadableStringCollection _parsedValues;
@ -46,6 +48,20 @@ namespace Microsoft.AspNet.Http.Features.Internal
_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 @@ namespace Microsoft.AspNet.Http.Features.Internal
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 @@ namespace Microsoft.AspNet.Http.Features.Internal
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;
}
}
}

View File

@ -10,10 +10,12 @@ using Microsoft.Net.Http.Headers;
namespace Microsoft.AspNet.Http.Features.Internal
{
public class RequestCookiesFeature : IRequestCookiesFeature
public class RequestCookiesFeature : IRequestCookiesFeature, IFeatureCache
{
private readonly IFeatureCollection _features;
private readonly FeatureReference<IHttpRequestFeature> _request = FeatureReference<IHttpRequestFeature>.Default;
private int _cachedFeaturesRevision = -1;
private IHttpRequestFeature _request;
private StringValues _original;
private IReadableStringCollection _parsedValues;
@ -43,6 +45,20 @@ namespace Microsoft.AspNet.Http.Features.Internal
_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 @@ namespace Microsoft.AspNet.Http.Features.Internal
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 @@ namespace Microsoft.AspNet.Http.Features.Internal
{
if (_parsedValues == null || _parsedValues.Count == 0)
{
_request.Fetch(_features).Headers.Remove(HeaderNames.Cookie);
HttpRequestFeature.Headers.Remove(HeaderNames.Cookie);
}
else
{
@ -94,7 +110,7 @@ namespace Microsoft.AspNet.Http.Features.Internal
}
}
_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
{
public class ResponseCookiesFeature : IResponseCookiesFeature
public class ResponseCookiesFeature : IResponseCookiesFeature, IFeatureCache
{
private readonly IFeatureCollection _features;
private readonly FeatureReference<IHttpResponseFeature> _request = FeatureReference<IHttpResponseFeature>.Default;
private int _cachedFeaturesRevision = -1;
private IHttpResponseFeature _response;
private IResponseCookies _cookiesCollection;
public ResponseCookiesFeature(IFeatureCollection features)
@ -16,16 +18,29 @@ namespace Microsoft.AspNet.Http.Features.Internal
_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;
}
}