Faster IFeatureCollection.Get<TFeature> (#2290)

This commit is contained in:
Ben Adams 2018-02-23 00:44:38 +00:00 committed by Stephen Halter
parent d996f6b7fc
commit c0f88ebdc1
8 changed files with 755 additions and 259 deletions

View File

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

View File

@ -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;

View File

@ -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)

View File

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

View File

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

View File

@ -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)

View File

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

View File

@ -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)