Faster IFeatureCollection.Get<TFeature> (#2290)
This commit is contained in:
parent
d996f6b7fc
commit
c0f88ebdc1
|
|
@ -4,6 +4,9 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.IO.Pipelines;
|
using System.IO.Pipelines;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using BenchmarkDotNet.Attributes;
|
using BenchmarkDotNet.Attributes;
|
||||||
using Microsoft.AspNetCore.Http.Features;
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||||
|
|
@ -14,65 +17,63 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
|
||||||
{
|
{
|
||||||
public class HttpProtocolFeatureCollection
|
public class HttpProtocolFeatureCollection
|
||||||
{
|
{
|
||||||
private readonly Http1Connection _http1Connection;
|
private readonly IFeatureCollection _collection;
|
||||||
private IFeatureCollection _collection;
|
|
||||||
|
|
||||||
[Benchmark(Baseline = true)]
|
[Benchmark]
|
||||||
public IHttpRequestFeature GetFirstViaFastFeature()
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
public IHttpRequestFeature GetViaTypeOf_First()
|
||||||
{
|
{
|
||||||
return (IHttpRequestFeature)GetFastFeature(typeof(IHttpRequestFeature));
|
return (IHttpRequestFeature)_collection[typeof(IHttpRequestFeature)];
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public IHttpRequestFeature GetFirstViaType()
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
{
|
public IHttpRequestFeature GetViaGeneric_First()
|
||||||
return (IHttpRequestFeature)Get(typeof(IHttpRequestFeature));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public IHttpRequestFeature GetFirstViaExtension()
|
|
||||||
{
|
|
||||||
return _collection.GetType<IHttpRequestFeature>();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public IHttpRequestFeature GetFirstViaGeneric()
|
|
||||||
{
|
{
|
||||||
return _collection.Get<IHttpRequestFeature>();
|
return _collection.Get<IHttpRequestFeature>();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public IHttpSendFileFeature GetLastViaFastFeature()
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
public IHttpSendFileFeature GetViaTypeOf_Last()
|
||||||
{
|
{
|
||||||
return (IHttpSendFileFeature)GetFastFeature(typeof(IHttpSendFileFeature));
|
return (IHttpSendFileFeature)_collection[typeof(IHttpSendFileFeature)];
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public IHttpSendFileFeature GetLastViaType()
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
{
|
public IHttpSendFileFeature GetViaGeneric_Last()
|
||||||
return (IHttpSendFileFeature)Get(typeof(IHttpSendFileFeature));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public IHttpSendFileFeature GetLastViaExtension()
|
|
||||||
{
|
|
||||||
return _collection.GetType<IHttpSendFileFeature>();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public IHttpSendFileFeature GetLastViaGeneric()
|
|
||||||
{
|
{
|
||||||
return _collection.Get<IHttpSendFileFeature>();
|
return _collection.Get<IHttpSendFileFeature>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private object Get(Type type)
|
[Benchmark]
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
public object GetViaTypeOf_Custom()
|
||||||
{
|
{
|
||||||
return _collection[type];
|
return (IHttpCustomFeature)_collection[typeof(IHttpCustomFeature)];
|
||||||
}
|
}
|
||||||
|
|
||||||
private object GetFastFeature(Type type)
|
[Benchmark]
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
public object GetViaGeneric_Custom()
|
||||||
{
|
{
|
||||||
return _http1Connection.FastFeatureGet(type);
|
return _collection.Get<IHttpCustomFeature>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
public object GetViaTypeOf_NotFound()
|
||||||
|
{
|
||||||
|
return (IHttpNotFoundFeature)_collection[typeof(IHttpNotFoundFeature)];
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
|
public object GetViaGeneric_NotFound()
|
||||||
|
{
|
||||||
|
return _collection.Get<IHttpNotFoundFeature>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public HttpProtocolFeatureCollection()
|
public HttpProtocolFeatureCollection()
|
||||||
|
|
@ -99,21 +100,24 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Performance
|
||||||
|
|
||||||
http1Connection.Reset();
|
http1Connection.Reset();
|
||||||
|
|
||||||
_http1Connection = http1Connection;
|
_collection = http1Connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
[IterationSetup]
|
private class SendFileFeature : IHttpSendFileFeature
|
||||||
public void Setup()
|
|
||||||
{
|
{
|
||||||
_collection = _http1Connection;
|
public Task SendFileAsync(string path, long offset, long? count, CancellationToken cancellation)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
private interface IHttpCustomFeature
|
||||||
public static class IFeatureCollectionExtensions
|
{
|
||||||
{
|
}
|
||||||
public static T GetType<T>(this IFeatureCollection collection)
|
|
||||||
|
private interface IHttpNotFoundFeature
|
||||||
{
|
{
|
||||||
return (T)collection[typeof(T)];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -400,7 +400,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
||||||
|
|
||||||
protected override void OnReset()
|
protected override void OnReset()
|
||||||
{
|
{
|
||||||
FastFeatureSet(typeof(IHttpUpgradeFeature), this);
|
ResetIHttpUpgradeFeature();
|
||||||
|
|
||||||
_requestTimedOut = false;
|
_requestTimedOut = false;
|
||||||
_requestTargetForm = HttpRequestTarget.Unknown;
|
_requestTargetForm = HttpRequestTarget.Unknown;
|
||||||
|
|
|
||||||
|
|
@ -243,20 +243,14 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
||||||
set => MinResponseDataRate = value;
|
set => MinResponseDataRate = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
object IFeatureCollection.this[Type key]
|
protected void ResetIHttpUpgradeFeature()
|
||||||
{
|
{
|
||||||
get => FastFeatureGet(key) ?? ConnectionFeatures[key];
|
_currentIHttpUpgradeFeature = this;
|
||||||
set => FastFeatureSet(key, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TFeature IFeatureCollection.Get<TFeature>()
|
protected void ResetIHttp2StreamIdFeature()
|
||||||
{
|
{
|
||||||
return (TFeature)(FastFeatureGet(typeof(TFeature)) ?? ConnectionFeatures[typeof(TFeature)]);
|
_currentIHttp2StreamIdFeature = this;
|
||||||
}
|
|
||||||
|
|
||||||
void IFeatureCollection.Set<TFeature>(TFeature instance)
|
|
||||||
{
|
|
||||||
FastFeatureSet(typeof(TFeature), instance);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IHttpResponseFeature.OnStarting(Func<object, Task> callback, object state)
|
void IHttpResponseFeature.OnStarting(Func<object, Task> callback, object state)
|
||||||
|
|
|
||||||
|
|
@ -82,205 +82,389 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
||||||
_currentIHttpSendFileFeature = null;
|
_currentIHttpSendFileFeature = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal object FastFeatureGet(Type key)
|
object IFeatureCollection.this[Type key]
|
||||||
{
|
{
|
||||||
if (key == IHttpRequestFeatureType)
|
get
|
||||||
{
|
{
|
||||||
return _currentIHttpRequestFeature;
|
object feature = null;
|
||||||
|
if (key == IHttpRequestFeatureType)
|
||||||
|
{
|
||||||
|
feature = _currentIHttpRequestFeature;
|
||||||
|
}
|
||||||
|
else if (key == IHttpResponseFeatureType)
|
||||||
|
{
|
||||||
|
feature = _currentIHttpResponseFeature;
|
||||||
|
}
|
||||||
|
else if (key == IHttpRequestIdentifierFeatureType)
|
||||||
|
{
|
||||||
|
feature = _currentIHttpRequestIdentifierFeature;
|
||||||
|
}
|
||||||
|
else if (key == IServiceProvidersFeatureType)
|
||||||
|
{
|
||||||
|
feature = _currentIServiceProvidersFeature;
|
||||||
|
}
|
||||||
|
else if (key == IHttpRequestLifetimeFeatureType)
|
||||||
|
{
|
||||||
|
feature = _currentIHttpRequestLifetimeFeature;
|
||||||
|
}
|
||||||
|
else if (key == IHttpConnectionFeatureType)
|
||||||
|
{
|
||||||
|
feature = _currentIHttpConnectionFeature;
|
||||||
|
}
|
||||||
|
else if (key == IHttpAuthenticationFeatureType)
|
||||||
|
{
|
||||||
|
feature = _currentIHttpAuthenticationFeature;
|
||||||
|
}
|
||||||
|
else if (key == IQueryFeatureType)
|
||||||
|
{
|
||||||
|
feature = _currentIQueryFeature;
|
||||||
|
}
|
||||||
|
else if (key == IFormFeatureType)
|
||||||
|
{
|
||||||
|
feature = _currentIFormFeature;
|
||||||
|
}
|
||||||
|
else if (key == IHttpUpgradeFeatureType)
|
||||||
|
{
|
||||||
|
feature = _currentIHttpUpgradeFeature;
|
||||||
|
}
|
||||||
|
else if (key == IHttp2StreamIdFeatureType)
|
||||||
|
{
|
||||||
|
feature = _currentIHttp2StreamIdFeature;
|
||||||
|
}
|
||||||
|
else if (key == IResponseCookiesFeatureType)
|
||||||
|
{
|
||||||
|
feature = _currentIResponseCookiesFeature;
|
||||||
|
}
|
||||||
|
else if (key == IItemsFeatureType)
|
||||||
|
{
|
||||||
|
feature = _currentIItemsFeature;
|
||||||
|
}
|
||||||
|
else if (key == ITlsConnectionFeatureType)
|
||||||
|
{
|
||||||
|
feature = _currentITlsConnectionFeature;
|
||||||
|
}
|
||||||
|
else if (key == IHttpWebSocketFeatureType)
|
||||||
|
{
|
||||||
|
feature = _currentIHttpWebSocketFeature;
|
||||||
|
}
|
||||||
|
else if (key == ISessionFeatureType)
|
||||||
|
{
|
||||||
|
feature = _currentISessionFeature;
|
||||||
|
}
|
||||||
|
else if (key == IHttpMaxRequestBodySizeFeatureType)
|
||||||
|
{
|
||||||
|
feature = _currentIHttpMaxRequestBodySizeFeature;
|
||||||
|
}
|
||||||
|
else if (key == IHttpMinRequestBodyDataRateFeatureType)
|
||||||
|
{
|
||||||
|
feature = _currentIHttpMinRequestBodyDataRateFeature;
|
||||||
|
}
|
||||||
|
else if (key == IHttpMinResponseDataRateFeatureType)
|
||||||
|
{
|
||||||
|
feature = _currentIHttpMinResponseDataRateFeature;
|
||||||
|
}
|
||||||
|
else if (key == IHttpBodyControlFeatureType)
|
||||||
|
{
|
||||||
|
feature = _currentIHttpBodyControlFeature;
|
||||||
|
}
|
||||||
|
else if (key == IHttpSendFileFeatureType)
|
||||||
|
{
|
||||||
|
feature = _currentIHttpSendFileFeature;
|
||||||
|
}
|
||||||
|
else if (MaybeExtra != null)
|
||||||
|
{
|
||||||
|
feature = ExtraFeatureGet(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return feature ?? ConnectionFeatures[key];
|
||||||
}
|
}
|
||||||
if (key == IHttpResponseFeatureType)
|
|
||||||
|
set
|
||||||
{
|
{
|
||||||
return _currentIHttpResponseFeature;
|
_featureRevision++;
|
||||||
|
|
||||||
|
if (key == IHttpRequestFeatureType)
|
||||||
|
{
|
||||||
|
_currentIHttpRequestFeature = value;
|
||||||
|
}
|
||||||
|
else if (key == IHttpResponseFeatureType)
|
||||||
|
{
|
||||||
|
_currentIHttpResponseFeature = value;
|
||||||
|
}
|
||||||
|
else if (key == IHttpRequestIdentifierFeatureType)
|
||||||
|
{
|
||||||
|
_currentIHttpRequestIdentifierFeature = value;
|
||||||
|
}
|
||||||
|
else if (key == IServiceProvidersFeatureType)
|
||||||
|
{
|
||||||
|
_currentIServiceProvidersFeature = value;
|
||||||
|
}
|
||||||
|
else if (key == IHttpRequestLifetimeFeatureType)
|
||||||
|
{
|
||||||
|
_currentIHttpRequestLifetimeFeature = value;
|
||||||
|
}
|
||||||
|
else if (key == IHttpConnectionFeatureType)
|
||||||
|
{
|
||||||
|
_currentIHttpConnectionFeature = value;
|
||||||
|
}
|
||||||
|
else if (key == IHttpAuthenticationFeatureType)
|
||||||
|
{
|
||||||
|
_currentIHttpAuthenticationFeature = value;
|
||||||
|
}
|
||||||
|
else if (key == IQueryFeatureType)
|
||||||
|
{
|
||||||
|
_currentIQueryFeature = value;
|
||||||
|
}
|
||||||
|
else if (key == IFormFeatureType)
|
||||||
|
{
|
||||||
|
_currentIFormFeature = value;
|
||||||
|
}
|
||||||
|
else if (key == IHttpUpgradeFeatureType)
|
||||||
|
{
|
||||||
|
_currentIHttpUpgradeFeature = value;
|
||||||
|
}
|
||||||
|
else if (key == IHttp2StreamIdFeatureType)
|
||||||
|
{
|
||||||
|
_currentIHttp2StreamIdFeature = value;
|
||||||
|
}
|
||||||
|
else if (key == IResponseCookiesFeatureType)
|
||||||
|
{
|
||||||
|
_currentIResponseCookiesFeature = value;
|
||||||
|
}
|
||||||
|
else if (key == IItemsFeatureType)
|
||||||
|
{
|
||||||
|
_currentIItemsFeature = value;
|
||||||
|
}
|
||||||
|
else if (key == ITlsConnectionFeatureType)
|
||||||
|
{
|
||||||
|
_currentITlsConnectionFeature = value;
|
||||||
|
}
|
||||||
|
else if (key == IHttpWebSocketFeatureType)
|
||||||
|
{
|
||||||
|
_currentIHttpWebSocketFeature = value;
|
||||||
|
}
|
||||||
|
else if (key == ISessionFeatureType)
|
||||||
|
{
|
||||||
|
_currentISessionFeature = value;
|
||||||
|
}
|
||||||
|
else if (key == IHttpMaxRequestBodySizeFeatureType)
|
||||||
|
{
|
||||||
|
_currentIHttpMaxRequestBodySizeFeature = value;
|
||||||
|
}
|
||||||
|
else if (key == IHttpMinRequestBodyDataRateFeatureType)
|
||||||
|
{
|
||||||
|
_currentIHttpMinRequestBodyDataRateFeature = value;
|
||||||
|
}
|
||||||
|
else if (key == IHttpMinResponseDataRateFeatureType)
|
||||||
|
{
|
||||||
|
_currentIHttpMinResponseDataRateFeature = value;
|
||||||
|
}
|
||||||
|
else if (key == IHttpBodyControlFeatureType)
|
||||||
|
{
|
||||||
|
_currentIHttpBodyControlFeature = value;
|
||||||
|
}
|
||||||
|
else if (key == IHttpSendFileFeatureType)
|
||||||
|
{
|
||||||
|
_currentIHttpSendFileFeature = value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ExtraFeatureSet(key, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (key == IHttpRequestIdentifierFeatureType)
|
|
||||||
{
|
|
||||||
return _currentIHttpRequestIdentifierFeature;
|
|
||||||
}
|
|
||||||
if (key == IServiceProvidersFeatureType)
|
|
||||||
{
|
|
||||||
return _currentIServiceProvidersFeature;
|
|
||||||
}
|
|
||||||
if (key == IHttpRequestLifetimeFeatureType)
|
|
||||||
{
|
|
||||||
return _currentIHttpRequestLifetimeFeature;
|
|
||||||
}
|
|
||||||
if (key == IHttpConnectionFeatureType)
|
|
||||||
{
|
|
||||||
return _currentIHttpConnectionFeature;
|
|
||||||
}
|
|
||||||
if (key == IHttpAuthenticationFeatureType)
|
|
||||||
{
|
|
||||||
return _currentIHttpAuthenticationFeature;
|
|
||||||
}
|
|
||||||
if (key == IQueryFeatureType)
|
|
||||||
{
|
|
||||||
return _currentIQueryFeature;
|
|
||||||
}
|
|
||||||
if (key == IFormFeatureType)
|
|
||||||
{
|
|
||||||
return _currentIFormFeature;
|
|
||||||
}
|
|
||||||
if (key == IHttpUpgradeFeatureType)
|
|
||||||
{
|
|
||||||
return _currentIHttpUpgradeFeature;
|
|
||||||
}
|
|
||||||
if (key == IHttp2StreamIdFeatureType)
|
|
||||||
{
|
|
||||||
return _currentIHttp2StreamIdFeature;
|
|
||||||
}
|
|
||||||
if (key == IResponseCookiesFeatureType)
|
|
||||||
{
|
|
||||||
return _currentIResponseCookiesFeature;
|
|
||||||
}
|
|
||||||
if (key == IItemsFeatureType)
|
|
||||||
{
|
|
||||||
return _currentIItemsFeature;
|
|
||||||
}
|
|
||||||
if (key == ITlsConnectionFeatureType)
|
|
||||||
{
|
|
||||||
return _currentITlsConnectionFeature;
|
|
||||||
}
|
|
||||||
if (key == IHttpWebSocketFeatureType)
|
|
||||||
{
|
|
||||||
return _currentIHttpWebSocketFeature;
|
|
||||||
}
|
|
||||||
if (key == ISessionFeatureType)
|
|
||||||
{
|
|
||||||
return _currentISessionFeature;
|
|
||||||
}
|
|
||||||
if (key == IHttpMaxRequestBodySizeFeatureType)
|
|
||||||
{
|
|
||||||
return _currentIHttpMaxRequestBodySizeFeature;
|
|
||||||
}
|
|
||||||
if (key == IHttpMinRequestBodyDataRateFeatureType)
|
|
||||||
{
|
|
||||||
return _currentIHttpMinRequestBodyDataRateFeature;
|
|
||||||
}
|
|
||||||
if (key == IHttpMinResponseDataRateFeatureType)
|
|
||||||
{
|
|
||||||
return _currentIHttpMinResponseDataRateFeature;
|
|
||||||
}
|
|
||||||
if (key == IHttpBodyControlFeatureType)
|
|
||||||
{
|
|
||||||
return _currentIHttpBodyControlFeature;
|
|
||||||
}
|
|
||||||
if (key == IHttpSendFileFeatureType)
|
|
||||||
{
|
|
||||||
return _currentIHttpSendFileFeature;
|
|
||||||
}
|
|
||||||
return ExtraFeatureGet(key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void FastFeatureSet(Type key, object feature)
|
void IFeatureCollection.Set<TFeature>(TFeature feature)
|
||||||
{
|
{
|
||||||
_featureRevision++;
|
_featureRevision++;
|
||||||
|
if (typeof(TFeature) == typeof(IHttpRequestFeature))
|
||||||
if (key == IHttpRequestFeatureType)
|
|
||||||
{
|
{
|
||||||
_currentIHttpRequestFeature = feature;
|
_currentIHttpRequestFeature = feature;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (key == IHttpResponseFeatureType)
|
else if (typeof(TFeature) == typeof(IHttpResponseFeature))
|
||||||
{
|
{
|
||||||
_currentIHttpResponseFeature = feature;
|
_currentIHttpResponseFeature = feature;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (key == IHttpRequestIdentifierFeatureType)
|
else if (typeof(TFeature) == typeof(IHttpRequestIdentifierFeature))
|
||||||
{
|
{
|
||||||
_currentIHttpRequestIdentifierFeature = feature;
|
_currentIHttpRequestIdentifierFeature = feature;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (key == IServiceProvidersFeatureType)
|
else if (typeof(TFeature) == typeof(IServiceProvidersFeature))
|
||||||
{
|
{
|
||||||
_currentIServiceProvidersFeature = feature;
|
_currentIServiceProvidersFeature = feature;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (key == IHttpRequestLifetimeFeatureType)
|
else if (typeof(TFeature) == typeof(IHttpRequestLifetimeFeature))
|
||||||
{
|
{
|
||||||
_currentIHttpRequestLifetimeFeature = feature;
|
_currentIHttpRequestLifetimeFeature = feature;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (key == IHttpConnectionFeatureType)
|
else if (typeof(TFeature) == typeof(IHttpConnectionFeature))
|
||||||
{
|
{
|
||||||
_currentIHttpConnectionFeature = feature;
|
_currentIHttpConnectionFeature = feature;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (key == IHttpAuthenticationFeatureType)
|
else if (typeof(TFeature) == typeof(IHttpAuthenticationFeature))
|
||||||
{
|
{
|
||||||
_currentIHttpAuthenticationFeature = feature;
|
_currentIHttpAuthenticationFeature = feature;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (key == IQueryFeatureType)
|
else if (typeof(TFeature) == typeof(IQueryFeature))
|
||||||
{
|
{
|
||||||
_currentIQueryFeature = feature;
|
_currentIQueryFeature = feature;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (key == IFormFeatureType)
|
else if (typeof(TFeature) == typeof(IFormFeature))
|
||||||
{
|
{
|
||||||
_currentIFormFeature = feature;
|
_currentIFormFeature = feature;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (key == IHttpUpgradeFeatureType)
|
else if (typeof(TFeature) == typeof(IHttpUpgradeFeature))
|
||||||
{
|
{
|
||||||
_currentIHttpUpgradeFeature = feature;
|
_currentIHttpUpgradeFeature = feature;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (key == IHttp2StreamIdFeatureType)
|
else if (typeof(TFeature) == typeof(IHttp2StreamIdFeature))
|
||||||
{
|
{
|
||||||
_currentIHttp2StreamIdFeature = feature;
|
_currentIHttp2StreamIdFeature = feature;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (key == IResponseCookiesFeatureType)
|
else if (typeof(TFeature) == typeof(IResponseCookiesFeature))
|
||||||
{
|
{
|
||||||
_currentIResponseCookiesFeature = feature;
|
_currentIResponseCookiesFeature = feature;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (key == IItemsFeatureType)
|
else if (typeof(TFeature) == typeof(IItemsFeature))
|
||||||
{
|
{
|
||||||
_currentIItemsFeature = feature;
|
_currentIItemsFeature = feature;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (key == ITlsConnectionFeatureType)
|
else if (typeof(TFeature) == typeof(ITlsConnectionFeature))
|
||||||
{
|
{
|
||||||
_currentITlsConnectionFeature = feature;
|
_currentITlsConnectionFeature = feature;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (key == IHttpWebSocketFeatureType)
|
else if (typeof(TFeature) == typeof(IHttpWebSocketFeature))
|
||||||
{
|
{
|
||||||
_currentIHttpWebSocketFeature = feature;
|
_currentIHttpWebSocketFeature = feature;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (key == ISessionFeatureType)
|
else if (typeof(TFeature) == typeof(ISessionFeature))
|
||||||
{
|
{
|
||||||
_currentISessionFeature = feature;
|
_currentISessionFeature = feature;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (key == IHttpMaxRequestBodySizeFeatureType)
|
else if (typeof(TFeature) == typeof(IHttpMaxRequestBodySizeFeature))
|
||||||
{
|
{
|
||||||
_currentIHttpMaxRequestBodySizeFeature = feature;
|
_currentIHttpMaxRequestBodySizeFeature = feature;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (key == IHttpMinRequestBodyDataRateFeatureType)
|
else if (typeof(TFeature) == typeof(IHttpMinRequestBodyDataRateFeature))
|
||||||
{
|
{
|
||||||
_currentIHttpMinRequestBodyDataRateFeature = feature;
|
_currentIHttpMinRequestBodyDataRateFeature = feature;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (key == IHttpMinResponseDataRateFeatureType)
|
else if (typeof(TFeature) == typeof(IHttpMinResponseDataRateFeature))
|
||||||
{
|
{
|
||||||
_currentIHttpMinResponseDataRateFeature = feature;
|
_currentIHttpMinResponseDataRateFeature = feature;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (key == IHttpBodyControlFeatureType)
|
else if (typeof(TFeature) == typeof(IHttpBodyControlFeature))
|
||||||
{
|
{
|
||||||
_currentIHttpBodyControlFeature = feature;
|
_currentIHttpBodyControlFeature = feature;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (key == IHttpSendFileFeatureType)
|
else if (typeof(TFeature) == typeof(IHttpSendFileFeature))
|
||||||
{
|
{
|
||||||
_currentIHttpSendFileFeature = feature;
|
_currentIHttpSendFileFeature = feature;
|
||||||
return;
|
}
|
||||||
};
|
else
|
||||||
ExtraFeatureSet(key, feature);
|
{
|
||||||
|
ExtraFeatureSet(typeof(TFeature), feature);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TFeature IFeatureCollection.Get<TFeature>()
|
||||||
|
{
|
||||||
|
TFeature feature = default;
|
||||||
|
if (typeof(TFeature) == typeof(IHttpRequestFeature))
|
||||||
|
{
|
||||||
|
feature = (TFeature)_currentIHttpRequestFeature;
|
||||||
|
}
|
||||||
|
else if (typeof(TFeature) == typeof(IHttpResponseFeature))
|
||||||
|
{
|
||||||
|
feature = (TFeature)_currentIHttpResponseFeature;
|
||||||
|
}
|
||||||
|
else if (typeof(TFeature) == typeof(IHttpRequestIdentifierFeature))
|
||||||
|
{
|
||||||
|
feature = (TFeature)_currentIHttpRequestIdentifierFeature;
|
||||||
|
}
|
||||||
|
else if (typeof(TFeature) == typeof(IServiceProvidersFeature))
|
||||||
|
{
|
||||||
|
feature = (TFeature)_currentIServiceProvidersFeature;
|
||||||
|
}
|
||||||
|
else if (typeof(TFeature) == typeof(IHttpRequestLifetimeFeature))
|
||||||
|
{
|
||||||
|
feature = (TFeature)_currentIHttpRequestLifetimeFeature;
|
||||||
|
}
|
||||||
|
else if (typeof(TFeature) == typeof(IHttpConnectionFeature))
|
||||||
|
{
|
||||||
|
feature = (TFeature)_currentIHttpConnectionFeature;
|
||||||
|
}
|
||||||
|
else if (typeof(TFeature) == typeof(IHttpAuthenticationFeature))
|
||||||
|
{
|
||||||
|
feature = (TFeature)_currentIHttpAuthenticationFeature;
|
||||||
|
}
|
||||||
|
else if (typeof(TFeature) == typeof(IQueryFeature))
|
||||||
|
{
|
||||||
|
feature = (TFeature)_currentIQueryFeature;
|
||||||
|
}
|
||||||
|
else if (typeof(TFeature) == typeof(IFormFeature))
|
||||||
|
{
|
||||||
|
feature = (TFeature)_currentIFormFeature;
|
||||||
|
}
|
||||||
|
else if (typeof(TFeature) == typeof(IHttpUpgradeFeature))
|
||||||
|
{
|
||||||
|
feature = (TFeature)_currentIHttpUpgradeFeature;
|
||||||
|
}
|
||||||
|
else if (typeof(TFeature) == typeof(IHttp2StreamIdFeature))
|
||||||
|
{
|
||||||
|
feature = (TFeature)_currentIHttp2StreamIdFeature;
|
||||||
|
}
|
||||||
|
else if (typeof(TFeature) == typeof(IResponseCookiesFeature))
|
||||||
|
{
|
||||||
|
feature = (TFeature)_currentIResponseCookiesFeature;
|
||||||
|
}
|
||||||
|
else if (typeof(TFeature) == typeof(IItemsFeature))
|
||||||
|
{
|
||||||
|
feature = (TFeature)_currentIItemsFeature;
|
||||||
|
}
|
||||||
|
else if (typeof(TFeature) == typeof(ITlsConnectionFeature))
|
||||||
|
{
|
||||||
|
feature = (TFeature)_currentITlsConnectionFeature;
|
||||||
|
}
|
||||||
|
else if (typeof(TFeature) == typeof(IHttpWebSocketFeature))
|
||||||
|
{
|
||||||
|
feature = (TFeature)_currentIHttpWebSocketFeature;
|
||||||
|
}
|
||||||
|
else if (typeof(TFeature) == typeof(ISessionFeature))
|
||||||
|
{
|
||||||
|
feature = (TFeature)_currentISessionFeature;
|
||||||
|
}
|
||||||
|
else if (typeof(TFeature) == typeof(IHttpMaxRequestBodySizeFeature))
|
||||||
|
{
|
||||||
|
feature = (TFeature)_currentIHttpMaxRequestBodySizeFeature;
|
||||||
|
}
|
||||||
|
else if (typeof(TFeature) == typeof(IHttpMinRequestBodyDataRateFeature))
|
||||||
|
{
|
||||||
|
feature = (TFeature)_currentIHttpMinRequestBodyDataRateFeature;
|
||||||
|
}
|
||||||
|
else if (typeof(TFeature) == typeof(IHttpMinResponseDataRateFeature))
|
||||||
|
{
|
||||||
|
feature = (TFeature)_currentIHttpMinResponseDataRateFeature;
|
||||||
|
}
|
||||||
|
else if (typeof(TFeature) == typeof(IHttpBodyControlFeature))
|
||||||
|
{
|
||||||
|
feature = (TFeature)_currentIHttpBodyControlFeature;
|
||||||
|
}
|
||||||
|
else if (typeof(TFeature) == typeof(IHttpSendFileFeature))
|
||||||
|
{
|
||||||
|
feature = (TFeature)_currentIHttpSendFileFeature;
|
||||||
|
}
|
||||||
|
else if (MaybeExtra != null)
|
||||||
|
{
|
||||||
|
feature = (TFeature)(ExtraFeatureGet(typeof(TFeature)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (feature == null)
|
||||||
|
{
|
||||||
|
feature = ConnectionFeatures.Get<TFeature>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return feature;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable<KeyValuePair<Type, object>> FastEnumerable()
|
private IEnumerable<KeyValuePair<Type, object>> FastEnumerable()
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2
|
||||||
|
|
||||||
protected override void OnReset()
|
protected override void OnReset()
|
||||||
{
|
{
|
||||||
FastFeatureSet(typeof(IHttp2StreamIdFeature), this);
|
ResetIHttp2StreamIdFeature();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnRequestProcessingEnded()
|
protected override void OnRequestProcessingEnded()
|
||||||
|
|
|
||||||
|
|
@ -111,69 +111,101 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal
|
||||||
|
|
||||||
object IFeatureCollection.this[Type key]
|
object IFeatureCollection.this[Type key]
|
||||||
{
|
{
|
||||||
get => FastFeatureGet(key);
|
get
|
||||||
set => FastFeatureSet(key, value);
|
{
|
||||||
|
if (key == IHttpConnectionFeatureType)
|
||||||
|
{
|
||||||
|
return _currentIHttpConnectionFeature;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key == IConnectionIdFeatureType)
|
||||||
|
{
|
||||||
|
return _currentIConnectionIdFeature;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (key == IConnectionTransportFeatureType)
|
||||||
|
{
|
||||||
|
return _currentIConnectionTransportFeature;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MaybeExtra != null)
|
||||||
|
{
|
||||||
|
return ExtraFeatureGet(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_featureRevision++;
|
||||||
|
|
||||||
|
if (key == IHttpConnectionFeatureType)
|
||||||
|
{
|
||||||
|
_currentIHttpConnectionFeature = value;
|
||||||
|
}
|
||||||
|
else if (key == IConnectionIdFeatureType)
|
||||||
|
{
|
||||||
|
_currentIConnectionIdFeature = value;
|
||||||
|
}
|
||||||
|
else if (key == IConnectionTransportFeatureType)
|
||||||
|
{
|
||||||
|
_currentIConnectionTransportFeature = value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ExtraFeatureSet(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TFeature IFeatureCollection.Get<TFeature>()
|
TFeature IFeatureCollection.Get<TFeature>()
|
||||||
{
|
{
|
||||||
return (TFeature)FastFeatureGet(typeof(TFeature));
|
if (typeof(TFeature) == typeof(IHttpConnectionFeature))
|
||||||
|
{
|
||||||
|
return (TFeature)_currentIHttpConnectionFeature;
|
||||||
|
}
|
||||||
|
else if (typeof(TFeature) == typeof(IConnectionIdFeature))
|
||||||
|
{
|
||||||
|
return (TFeature)_currentIConnectionIdFeature;
|
||||||
|
}
|
||||||
|
else if (typeof(TFeature) == typeof(IConnectionTransportFeature))
|
||||||
|
{
|
||||||
|
return (TFeature)_currentIConnectionTransportFeature;
|
||||||
|
}
|
||||||
|
else if (MaybeExtra != null)
|
||||||
|
{
|
||||||
|
return (TFeature)ExtraFeatureGet(typeof(TFeature));
|
||||||
|
}
|
||||||
|
|
||||||
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IFeatureCollection.Set<TFeature>(TFeature instance)
|
void IFeatureCollection.Set<TFeature>(TFeature instance)
|
||||||
{
|
{
|
||||||
FastFeatureSet(typeof(TFeature), instance);
|
_featureRevision++;
|
||||||
|
|
||||||
|
if (typeof(TFeature) == typeof(IHttpConnectionFeature))
|
||||||
|
{
|
||||||
|
_currentIHttpConnectionFeature = instance;
|
||||||
|
}
|
||||||
|
else if (typeof(TFeature) == typeof(IConnectionIdFeature))
|
||||||
|
{
|
||||||
|
_currentIConnectionIdFeature = instance;
|
||||||
|
}
|
||||||
|
else if (typeof(TFeature) == typeof(IConnectionTransportFeature))
|
||||||
|
{
|
||||||
|
_currentIConnectionTransportFeature = instance;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ExtraFeatureSet(typeof(TFeature), instance);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IEnumerator<KeyValuePair<Type, object>> IEnumerable<KeyValuePair<Type, object>>.GetEnumerator() => FastEnumerable().GetEnumerator();
|
IEnumerator<KeyValuePair<Type, object>> IEnumerable<KeyValuePair<Type, object>>.GetEnumerator() => FastEnumerable().GetEnumerator();
|
||||||
|
|
||||||
IEnumerator IEnumerable.GetEnumerator() => FastEnumerable().GetEnumerator();
|
IEnumerator IEnumerable.GetEnumerator() => FastEnumerable().GetEnumerator();
|
||||||
|
|
||||||
private object FastFeatureGet(Type key)
|
|
||||||
{
|
|
||||||
if (key == IHttpConnectionFeatureType)
|
|
||||||
{
|
|
||||||
return _currentIHttpConnectionFeature;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key == IConnectionIdFeatureType)
|
|
||||||
{
|
|
||||||
return _currentIConnectionIdFeature;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key == IConnectionTransportFeatureType)
|
|
||||||
{
|
|
||||||
return _currentIConnectionTransportFeature;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ExtraFeatureGet(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void FastFeatureSet(Type key, object feature)
|
|
||||||
{
|
|
||||||
_featureRevision++;
|
|
||||||
|
|
||||||
if (key == IHttpConnectionFeatureType)
|
|
||||||
{
|
|
||||||
_currentIHttpConnectionFeature = feature;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key == IConnectionIdFeatureType)
|
|
||||||
{
|
|
||||||
_currentIConnectionIdFeature = feature;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key == IConnectionTransportFeatureType)
|
|
||||||
{
|
|
||||||
_currentIConnectionTransportFeature = feature;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExtraFeatureSet(key, feature);
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerable<KeyValuePair<Type, object>> FastEnumerable()
|
private IEnumerable<KeyValuePair<Type, object>> FastEnumerable()
|
||||||
{
|
{
|
||||||
if (_currentIHttpConnectionFeature != null)
|
if (_currentIHttpConnectionFeature != null)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,224 @@
|
||||||
|
// 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;
|
||||||
|
using System.Buffers;
|
||||||
|
using System.IO.Pipelines;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
|
using Microsoft.AspNetCore.Server.Kestrel.Core.Features;
|
||||||
|
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
|
||||||
|
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
|
||||||
|
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
|
||||||
|
using Microsoft.AspNetCore.Testing;
|
||||||
|
using Moq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
||||||
|
{
|
||||||
|
public class HttpProtocolFeatureCollectionTests : IDisposable
|
||||||
|
{
|
||||||
|
private readonly IDuplexPipe _transport;
|
||||||
|
private readonly IDuplexPipe _application;
|
||||||
|
private readonly TestHttp1Connection _http1Connection;
|
||||||
|
private readonly ServiceContext _serviceContext;
|
||||||
|
private readonly Http1ConnectionContext _http1ConnectionContext;
|
||||||
|
private readonly MemoryPool _pipelineFactory;
|
||||||
|
private Mock<ITimeoutControl> _timeoutControl;
|
||||||
|
|
||||||
|
private readonly IFeatureCollection _collection;
|
||||||
|
|
||||||
|
public HttpProtocolFeatureCollectionTests()
|
||||||
|
{
|
||||||
|
_pipelineFactory = new MemoryPool();
|
||||||
|
var pair = DuplexPipe.CreateConnectionPair(_pipelineFactory);
|
||||||
|
|
||||||
|
_transport = pair.Transport;
|
||||||
|
_application = pair.Application;
|
||||||
|
|
||||||
|
_serviceContext = new TestServiceContext();
|
||||||
|
_timeoutControl = new Mock<ITimeoutControl>();
|
||||||
|
_http1ConnectionContext = new Http1ConnectionContext
|
||||||
|
{
|
||||||
|
ServiceContext = _serviceContext,
|
||||||
|
ConnectionFeatures = new FeatureCollection(),
|
||||||
|
MemoryPool = _pipelineFactory,
|
||||||
|
TimeoutControl = _timeoutControl.Object,
|
||||||
|
Application = pair.Application,
|
||||||
|
Transport = pair.Transport
|
||||||
|
};
|
||||||
|
|
||||||
|
_http1Connection = new TestHttp1Connection(_http1ConnectionContext);
|
||||||
|
_http1Connection.Reset();
|
||||||
|
_collection = _http1Connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_transport.Input.Complete();
|
||||||
|
_transport.Output.Complete();
|
||||||
|
|
||||||
|
_application.Input.Complete();
|
||||||
|
_application.Output.Complete();
|
||||||
|
|
||||||
|
_pipelineFactory.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public int FeaturesStartAsSelf()
|
||||||
|
{
|
||||||
|
var featureCount = 0;
|
||||||
|
foreach (var featureIter in _collection)
|
||||||
|
{
|
||||||
|
Type type = featureIter.Key;
|
||||||
|
if (type.IsAssignableFrom(typeof(HttpProtocol)))
|
||||||
|
{
|
||||||
|
var featureLookup = _collection[type];
|
||||||
|
Assert.Same(featureLookup, featureIter.Value);
|
||||||
|
Assert.Same(featureLookup, _collection);
|
||||||
|
featureCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.NotEqual(0, featureCount);
|
||||||
|
|
||||||
|
return featureCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public int FeaturesCanBeAssignedTo()
|
||||||
|
{
|
||||||
|
var featureCount = SetFeaturesToNonDefault();
|
||||||
|
Assert.NotEqual(0, featureCount);
|
||||||
|
|
||||||
|
featureCount = 0;
|
||||||
|
foreach (var feature in _collection)
|
||||||
|
{
|
||||||
|
Type type = feature.Key;
|
||||||
|
if (type.IsAssignableFrom(typeof(HttpProtocol)))
|
||||||
|
{
|
||||||
|
Assert.Same(_collection[type], feature.Value);
|
||||||
|
Assert.NotSame(_collection[type], _collection);
|
||||||
|
featureCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.NotEqual(0, featureCount);
|
||||||
|
|
||||||
|
return featureCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FeaturesResetToSelf()
|
||||||
|
{
|
||||||
|
var featuresAssigned = SetFeaturesToNonDefault();
|
||||||
|
_http1Connection.ResetFeatureCollection();
|
||||||
|
var featuresReset = FeaturesStartAsSelf();
|
||||||
|
|
||||||
|
Assert.Equal(featuresAssigned, featuresReset);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FeaturesByGenericSameAsByType()
|
||||||
|
{
|
||||||
|
var featuresAssigned = SetFeaturesToNonDefault();
|
||||||
|
|
||||||
|
CompareGenericGetterToIndexer();
|
||||||
|
|
||||||
|
_http1Connection.ResetFeatureCollection();
|
||||||
|
var featuresReset = FeaturesStartAsSelf();
|
||||||
|
|
||||||
|
Assert.Equal(featuresAssigned, featuresReset);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FeaturesSetByTypeSameAsGeneric()
|
||||||
|
{
|
||||||
|
_collection[typeof(IHttpRequestFeature)] = CreateHttp1Connection();
|
||||||
|
_collection[typeof(IHttpResponseFeature)] = CreateHttp1Connection();
|
||||||
|
_collection[typeof(IHttpRequestIdentifierFeature)] = CreateHttp1Connection();
|
||||||
|
_collection[typeof(IHttpRequestLifetimeFeature)] = CreateHttp1Connection();
|
||||||
|
_collection[typeof(IHttpConnectionFeature)] = CreateHttp1Connection();
|
||||||
|
_collection[typeof(IHttpMaxRequestBodySizeFeature)] = CreateHttp1Connection();
|
||||||
|
_collection[typeof(IHttpMinRequestBodyDataRateFeature)] = CreateHttp1Connection();
|
||||||
|
_collection[typeof(IHttpMinResponseDataRateFeature)] = CreateHttp1Connection();
|
||||||
|
_collection[typeof(IHttpBodyControlFeature)] = CreateHttp1Connection();
|
||||||
|
|
||||||
|
CompareGenericGetterToIndexer();
|
||||||
|
|
||||||
|
EachHttpProtocolFeatureSetAndUnique();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void FeaturesSetByGenericSameAsByType()
|
||||||
|
{
|
||||||
|
_collection.Set<IHttpRequestFeature>(CreateHttp1Connection());
|
||||||
|
_collection.Set<IHttpResponseFeature>(CreateHttp1Connection());
|
||||||
|
_collection.Set<IHttpRequestIdentifierFeature>(CreateHttp1Connection());
|
||||||
|
_collection.Set<IHttpRequestLifetimeFeature>(CreateHttp1Connection());
|
||||||
|
_collection.Set<IHttpConnectionFeature>(CreateHttp1Connection());
|
||||||
|
_collection.Set<IHttpMaxRequestBodySizeFeature>(CreateHttp1Connection());
|
||||||
|
_collection.Set<IHttpMinRequestBodyDataRateFeature>(CreateHttp1Connection());
|
||||||
|
_collection.Set<IHttpMinResponseDataRateFeature>(CreateHttp1Connection());
|
||||||
|
_collection.Set<IHttpBodyControlFeature>(CreateHttp1Connection());
|
||||||
|
|
||||||
|
CompareGenericGetterToIndexer();
|
||||||
|
|
||||||
|
EachHttpProtocolFeatureSetAndUnique();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CompareGenericGetterToIndexer()
|
||||||
|
{
|
||||||
|
Assert.Same(_collection.Get<IHttpRequestFeature>(), _collection[typeof(IHttpRequestFeature)]);
|
||||||
|
Assert.Same(_collection.Get<IHttpResponseFeature>(), _collection[typeof(IHttpResponseFeature)]);
|
||||||
|
Assert.Same(_collection.Get<IHttpRequestIdentifierFeature>(), _collection[typeof(IHttpRequestIdentifierFeature)]);
|
||||||
|
Assert.Same(_collection.Get<IHttpRequestLifetimeFeature>(), _collection[typeof(IHttpRequestLifetimeFeature)]);
|
||||||
|
Assert.Same(_collection.Get<IHttpConnectionFeature>(), _collection[typeof(IHttpConnectionFeature)]);
|
||||||
|
Assert.Same(_collection.Get<IHttpMaxRequestBodySizeFeature>(), _collection[typeof(IHttpMaxRequestBodySizeFeature)]);
|
||||||
|
Assert.Same(_collection.Get<IHttpMinRequestBodyDataRateFeature>(), _collection[typeof(IHttpMinRequestBodyDataRateFeature)]);
|
||||||
|
Assert.Same(_collection.Get<IHttpMinResponseDataRateFeature>(), _collection[typeof(IHttpMinResponseDataRateFeature)]);
|
||||||
|
Assert.Same(_collection.Get<IHttpBodyControlFeature>(), _collection[typeof(IHttpBodyControlFeature)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int EachHttpProtocolFeatureSetAndUnique()
|
||||||
|
{
|
||||||
|
int featureCount = 0;
|
||||||
|
foreach (var item in _collection)
|
||||||
|
{
|
||||||
|
Type type = item.Key;
|
||||||
|
if (type.IsAssignableFrom(typeof(HttpProtocol)))
|
||||||
|
{
|
||||||
|
Assert.Equal(1, _collection.Count(kv => ReferenceEquals(kv.Value, item.Value)));
|
||||||
|
|
||||||
|
featureCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.NotEqual(0, featureCount);
|
||||||
|
|
||||||
|
return featureCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int SetFeaturesToNonDefault()
|
||||||
|
{
|
||||||
|
int featureCount = 0;
|
||||||
|
foreach (var feature in _collection)
|
||||||
|
{
|
||||||
|
Type type = feature.Key;
|
||||||
|
if (type.IsAssignableFrom(typeof(HttpProtocol)))
|
||||||
|
{
|
||||||
|
_collection[type] = CreateHttp1Connection();
|
||||||
|
featureCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var protocolFeaturesCount = EachHttpProtocolFeatureSetAndUnique();
|
||||||
|
|
||||||
|
Assert.Equal(protocolFeaturesCount, featureCount);
|
||||||
|
|
||||||
|
return featureCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpProtocol CreateHttp1Connection() => new TestHttp1Connection(_http1ConnectionContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -16,6 +16,12 @@ namespace CodeGenerator
|
||||||
return values.Select(formatter).Aggregate((a, b) => a + b);
|
return values.Select(formatter).Aggregate((a, b) => a + b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class KnownFeature
|
||||||
|
{
|
||||||
|
public string Name;
|
||||||
|
public int Index;
|
||||||
|
}
|
||||||
|
|
||||||
public static string GeneratedFile(string className)
|
public static string GeneratedFile(string className)
|
||||||
{
|
{
|
||||||
var alwaysFeatures = new[]
|
var alwaysFeatures = new[]
|
||||||
|
|
@ -55,7 +61,15 @@ namespace CodeGenerator
|
||||||
"IHttpSendFileFeature",
|
"IHttpSendFileFeature",
|
||||||
};
|
};
|
||||||
|
|
||||||
var allFeatures = alwaysFeatures.Concat(commonFeatures).Concat(sometimesFeatures).Concat(rareFeatures);
|
var allFeatures = alwaysFeatures
|
||||||
|
.Concat(commonFeatures)
|
||||||
|
.Concat(sometimesFeatures)
|
||||||
|
.Concat(rareFeatures)
|
||||||
|
.Select((type, index) => new KnownFeature
|
||||||
|
{
|
||||||
|
Name = type,
|
||||||
|
Index = index
|
||||||
|
});
|
||||||
|
|
||||||
// NOTE: This list MUST always match the set of feature interfaces implemented by HttpProtocol.
|
// NOTE: This list MUST always match the set of feature interfaces implemented by HttpProtocol.
|
||||||
// See also: src/Kestrel/Http/HttpProtocol.FeatureCollection.cs
|
// See also: src/Kestrel/Http/HttpProtocol.FeatureCollection.cs
|
||||||
|
|
@ -86,43 +100,87 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
|
||||||
{{
|
{{
|
||||||
public partial class {className}
|
public partial class {className}
|
||||||
{{{Each(allFeatures, feature => $@"
|
{{{Each(allFeatures, feature => $@"
|
||||||
private static readonly Type {feature}Type = typeof({feature});")}
|
private static readonly Type {feature.Name}Type = typeof({feature.Name});")}
|
||||||
{Each(allFeatures, feature => $@"
|
{Each(allFeatures, feature => $@"
|
||||||
private object _current{feature};")}
|
private object _current{feature.Name};")}
|
||||||
|
|
||||||
private void FastReset()
|
private void FastReset()
|
||||||
{{{Each(implementedFeatures, feature => $@"
|
{{{Each(implementedFeatures, feature => $@"
|
||||||
_current{feature} = this;")}
|
_current{feature} = this;")}
|
||||||
{Each(allFeatures.Where(f => !implementedFeatures.Contains(f)), feature => $@"
|
{Each(allFeatures.Where(f => !implementedFeatures.Contains(f.Name)), feature => $@"
|
||||||
_current{feature} = null;")}
|
_current{feature.Name} = null;")}
|
||||||
}}
|
}}
|
||||||
|
|
||||||
internal object FastFeatureGet(Type key)
|
object IFeatureCollection.this[Type key]
|
||||||
{{{Each(allFeatures, feature => $@"
|
|
||||||
if (key == {feature}Type)
|
|
||||||
{{
|
|
||||||
return _current{feature};
|
|
||||||
}}")}
|
|
||||||
return ExtraFeatureGet(key);
|
|
||||||
}}
|
|
||||||
|
|
||||||
protected void FastFeatureSet(Type key, object feature)
|
|
||||||
{{
|
{{
|
||||||
_featureRevision++;
|
get
|
||||||
{Each(allFeatures, feature => $@"
|
|
||||||
if (key == {feature}Type)
|
|
||||||
{{
|
{{
|
||||||
_current{feature} = feature;
|
object feature = null;{Each(allFeatures, feature => $@"
|
||||||
return;
|
{(feature.Index != 0 ? "else " : "")}if (key == {feature.Name}Type)
|
||||||
}}")};
|
{{
|
||||||
ExtraFeatureSet(key, feature);
|
feature = _current{feature.Name};
|
||||||
|
}}")}
|
||||||
|
else if (MaybeExtra != null)
|
||||||
|
{{
|
||||||
|
feature = ExtraFeatureGet(key);
|
||||||
|
}}
|
||||||
|
|
||||||
|
return feature ?? ConnectionFeatures[key];
|
||||||
|
}}
|
||||||
|
|
||||||
|
set
|
||||||
|
{{
|
||||||
|
_featureRevision++;
|
||||||
|
{Each(allFeatures, feature => $@"
|
||||||
|
{(feature.Index != 0 ? "else " : "")}if (key == {feature.Name}Type)
|
||||||
|
{{
|
||||||
|
_current{feature.Name} = value;
|
||||||
|
}}")}
|
||||||
|
else
|
||||||
|
{{
|
||||||
|
ExtraFeatureSet(key, value);
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
|
||||||
|
void IFeatureCollection.Set<TFeature>(TFeature feature)
|
||||||
|
{{
|
||||||
|
_featureRevision++;{Each(allFeatures, feature => $@"
|
||||||
|
{(feature.Index != 0 ? "else " : "")}if (typeof(TFeature) == typeof({feature.Name}))
|
||||||
|
{{
|
||||||
|
_current{feature.Name} = feature;
|
||||||
|
}}")}
|
||||||
|
else
|
||||||
|
{{
|
||||||
|
ExtraFeatureSet(typeof(TFeature), feature);
|
||||||
|
}}
|
||||||
|
}}
|
||||||
|
|
||||||
|
TFeature IFeatureCollection.Get<TFeature>()
|
||||||
|
{{
|
||||||
|
TFeature feature = default;{Each(allFeatures, feature => $@"
|
||||||
|
{(feature.Index != 0 ? "else " : "")}if (typeof(TFeature) == typeof({feature.Name}))
|
||||||
|
{{
|
||||||
|
feature = (TFeature)_current{feature.Name};
|
||||||
|
}}")}
|
||||||
|
else if (MaybeExtra != null)
|
||||||
|
{{
|
||||||
|
feature = (TFeature)(ExtraFeatureGet(typeof(TFeature)));
|
||||||
|
}}
|
||||||
|
|
||||||
|
if (feature == null)
|
||||||
|
{{
|
||||||
|
feature = ConnectionFeatures.Get<TFeature>();
|
||||||
|
}}
|
||||||
|
|
||||||
|
return feature;
|
||||||
}}
|
}}
|
||||||
|
|
||||||
private IEnumerable<KeyValuePair<Type, object>> FastEnumerable()
|
private IEnumerable<KeyValuePair<Type, object>> FastEnumerable()
|
||||||
{{{Each(allFeatures, feature => $@"
|
{{{Each(allFeatures, feature => $@"
|
||||||
if (_current{feature} != null)
|
if (_current{feature.Name} != null)
|
||||||
{{
|
{{
|
||||||
yield return new KeyValuePair<Type, object>({feature}Type, _current{feature} as {feature});
|
yield return new KeyValuePair<Type, object>({feature.Name}Type, _current{feature.Name} as {feature.Name});
|
||||||
}}")}
|
}}")}
|
||||||
|
|
||||||
if (MaybeExtra != null)
|
if (MaybeExtra != null)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue