diff --git a/src/Servers/Kestrel/Core/src/Internal/Http/Http1Connection.cs b/src/Servers/Kestrel/Core/src/Internal/Http/Http1Connection.cs index 88c35d5f670e..e5978de39b07 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http/Http1Connection.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http/Http1Connection.cs @@ -616,7 +616,6 @@ private void ValidateNonOriginHostHeader(string hostText) protected override void OnReset() { - ResetHttp1Features(); _requestTimedOut = false; _requestTargetForm = HttpRequestTarget.Unknown; @@ -625,6 +624,10 @@ protected override void OnReset() _requestCount++; MinResponseDataRate = ServerOptions.Limits.MinResponseDataRate; + + // Reset Http1 Features + _currentIHttpMinRequestBodyDataRateFeature = this; + _currentIHttpMinResponseDataRateFeature = this; } protected override void OnRequestProcessingEnding() diff --git a/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.FeatureCollection.cs b/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.FeatureCollection.cs index 71e9ed2047a6..7dac07e16956 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.FeatureCollection.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.FeatureCollection.cs @@ -2,8 +2,6 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.IO.Pipelines; using System.Net; @@ -18,23 +16,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http { - internal partial class HttpProtocol : IHttpRequestFeature, - IHttpRequestBodyDetectionFeature, - IHttpResponseFeature, - IHttpResponseBodyFeature, - IRequestBodyPipeFeature, - IHttpUpgradeFeature, - IHttpConnectionFeature, - IHttpRequestLifetimeFeature, - IHttpRequestIdentifierFeature, - IHttpRequestTrailersFeature, - IHttpBodyControlFeature, - IHttpMaxRequestBodySizeFeature, - IEndpointFeature, - IRouteValuesFeature + internal partial class HttpProtocol { // NOTE: When feature interfaces are added to or removed from this HttpProtocol class implementation, - // then the list of `implementedFeatures` in the generated code project MUST also be updated. + // then the list of `implementedFeatures` in the generated code project MUST also be updated first + // and the code generator re-reun, which will change the interface list. // See also: tools/CodeGenerator/HttpProtocolFeatureCollection.cs string IHttpRequestFeature.Protocol @@ -255,25 +241,6 @@ RouteValueDictionary IRouteValuesFeature.RouteValues Stream IHttpResponseBodyFeature.Stream => ResponseBody; - protected void ResetHttp1Features() - { - _currentIHttpMinRequestBodyDataRateFeature = this; - _currentIHttpMinResponseDataRateFeature = this; - } - - protected void ResetHttp2Features() - { - _currentIHttp2StreamIdFeature = this; - _currentIHttpResponseTrailersFeature = this; - _currentIHttpResetFeature = this; - } - - protected void ResetHttp3Features() - { - _currentIHttpResponseTrailersFeature = this; - _currentIHttpResetFeature = this; - } - void IHttpResponseFeature.OnStarting(Func callback, object state) { OnStarting(callback, state); diff --git a/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.Generated.cs b/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.Generated.cs index f6420cc0c955..1cc8eea7c01c 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.Generated.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http/HttpProtocol.Generated.cs @@ -4,6 +4,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Runtime.CompilerServices; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http.Features.Authentication; @@ -13,36 +14,53 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http { - internal partial class HttpProtocol : IFeatureCollection + internal partial class HttpProtocol : IFeatureCollection, + IHttpRequestFeature, + IHttpResponseFeature, + IHttpResponseBodyFeature, + IRouteValuesFeature, + IEndpointFeature, + IHttpRequestIdentifierFeature, + IHttpRequestTrailersFeature, + IHttpUpgradeFeature, + IRequestBodyPipeFeature, + IHttpConnectionFeature, + IHttpRequestLifetimeFeature, + IHttpBodyControlFeature, + IHttpMaxRequestBodySizeFeature, + IHttpRequestBodyDetectionFeature { - private object? _currentIHttpRequestFeature; - private object? _currentIHttpRequestBodyDetectionFeature; - private object? _currentIHttpResponseFeature; - private object? _currentIHttpResponseBodyFeature; - private object? _currentIRequestBodyPipeFeature; - private object? _currentIHttpRequestIdentifierFeature; - private object? _currentIServiceProvidersFeature; - private object? _currentIHttpRequestLifetimeFeature; - private object? _currentIHttpConnectionFeature; - private object? _currentIRouteValuesFeature; - private object? _currentIEndpointFeature; - private object? _currentIHttpAuthenticationFeature; - private object? _currentIHttpRequestTrailersFeature; - private object? _currentIQueryFeature; - private object? _currentIFormFeature; - private object? _currentIHttpUpgradeFeature; - private object? _currentIHttp2StreamIdFeature; - private object? _currentIHttpResponseTrailersFeature; - private object? _currentIResponseCookiesFeature; - private object? _currentIItemsFeature; - private object? _currentITlsConnectionFeature; - private object? _currentIHttpWebSocketFeature; - private object? _currentISessionFeature; - private object? _currentIHttpMaxRequestBodySizeFeature; - private object? _currentIHttpMinRequestBodyDataRateFeature; - private object? _currentIHttpMinResponseDataRateFeature; - private object? _currentIHttpBodyControlFeature; - private object? _currentIHttpResetFeature; + // Implemented features + internal protected IHttpRequestFeature? _currentIHttpRequestFeature; + internal protected IHttpResponseFeature? _currentIHttpResponseFeature; + internal protected IHttpResponseBodyFeature? _currentIHttpResponseBodyFeature; + internal protected IRouteValuesFeature? _currentIRouteValuesFeature; + internal protected IEndpointFeature? _currentIEndpointFeature; + internal protected IHttpRequestIdentifierFeature? _currentIHttpRequestIdentifierFeature; + internal protected IHttpRequestTrailersFeature? _currentIHttpRequestTrailersFeature; + internal protected IHttpUpgradeFeature? _currentIHttpUpgradeFeature; + internal protected IRequestBodyPipeFeature? _currentIRequestBodyPipeFeature; + internal protected IHttpConnectionFeature? _currentIHttpConnectionFeature; + internal protected IHttpRequestLifetimeFeature? _currentIHttpRequestLifetimeFeature; + internal protected IHttpBodyControlFeature? _currentIHttpBodyControlFeature; + internal protected IHttpMaxRequestBodySizeFeature? _currentIHttpMaxRequestBodySizeFeature; + internal protected IHttpRequestBodyDetectionFeature? _currentIHttpRequestBodyDetectionFeature; + + // Other reserved feature slots + internal protected IServiceProvidersFeature? _currentIServiceProvidersFeature; + internal protected IItemsFeature? _currentIItemsFeature; + internal protected IQueryFeature? _currentIQueryFeature; + internal protected IFormFeature? _currentIFormFeature; + internal protected IHttpAuthenticationFeature? _currentIHttpAuthenticationFeature; + internal protected ISessionFeature? _currentISessionFeature; + internal protected IResponseCookiesFeature? _currentIResponseCookiesFeature; + internal protected IHttpResponseTrailersFeature? _currentIHttpResponseTrailersFeature; + internal protected ITlsConnectionFeature? _currentITlsConnectionFeature; + internal protected IHttpWebSocketFeature? _currentIHttpWebSocketFeature; + internal protected IHttp2StreamIdFeature? _currentIHttp2StreamIdFeature; + internal protected IHttpMinRequestBodyDataRateFeature? _currentIHttpMinRequestBodyDataRateFeature; + internal protected IHttpMinResponseDataRateFeature? _currentIHttpMinResponseDataRateFeature; + internal protected IHttpResetFeature? _currentIHttpResetFeature; private int _featureRevision; @@ -51,32 +69,32 @@ internal partial class HttpProtocol : IFeatureCollection private void FastReset() { _currentIHttpRequestFeature = this; - _currentIHttpRequestBodyDetectionFeature = this; _currentIHttpResponseFeature = this; _currentIHttpResponseBodyFeature = this; - _currentIRequestBodyPipeFeature = this; - _currentIHttpUpgradeFeature = this; + _currentIRouteValuesFeature = this; + _currentIEndpointFeature = this; _currentIHttpRequestIdentifierFeature = this; - _currentIHttpRequestLifetimeFeature = this; _currentIHttpRequestTrailersFeature = this; + _currentIHttpUpgradeFeature = this; + _currentIRequestBodyPipeFeature = this; _currentIHttpConnectionFeature = this; - _currentIHttpMaxRequestBodySizeFeature = this; - _currentIHttpMinRequestBodyDataRateFeature = this; + _currentIHttpRequestLifetimeFeature = this; _currentIHttpBodyControlFeature = this; - _currentIRouteValuesFeature = this; - _currentIEndpointFeature = this; + _currentIHttpMaxRequestBodySizeFeature = this; + _currentIHttpRequestBodyDetectionFeature = this; _currentIServiceProvidersFeature = null; - _currentIHttpAuthenticationFeature = null; + _currentIItemsFeature = null; _currentIQueryFeature = null; _currentIFormFeature = null; - _currentIHttp2StreamIdFeature = null; - _currentIHttpResponseTrailersFeature = null; + _currentIHttpAuthenticationFeature = null; + _currentISessionFeature = null; _currentIResponseCookiesFeature = null; - _currentIItemsFeature = null; + _currentIHttpResponseTrailersFeature = null; _currentITlsConnectionFeature = null; _currentIHttpWebSocketFeature = null; - _currentISessionFeature = null; + _currentIHttp2StreamIdFeature = null; + _currentIHttpMinRequestBodyDataRateFeature = null; _currentIHttpMinResponseDataRateFeature = null; _currentIHttpResetFeature = null; } @@ -154,10 +172,6 @@ private void ExtraFeatureSet(Type key, object? value) { feature = _currentIHttpRequestFeature; } - else if (key == typeof(IHttpRequestBodyDetectionFeature)) - { - feature = _currentIHttpRequestBodyDetectionFeature; - } else if (key == typeof(IHttpResponseFeature)) { feature = _currentIHttpResponseFeature; @@ -166,81 +180,81 @@ private void ExtraFeatureSet(Type key, object? value) { feature = _currentIHttpResponseBodyFeature; } - else if (key == typeof(IRequestBodyPipeFeature)) + else if (key == typeof(IRouteValuesFeature)) { - feature = _currentIRequestBodyPipeFeature; + feature = _currentIRouteValuesFeature; } - else if (key == typeof(IHttpRequestIdentifierFeature)) + else if (key == typeof(IEndpointFeature)) { - feature = _currentIHttpRequestIdentifierFeature; + feature = _currentIEndpointFeature; } else if (key == typeof(IServiceProvidersFeature)) { feature = _currentIServiceProvidersFeature; } - else if (key == typeof(IHttpRequestLifetimeFeature)) + else if (key == typeof(IItemsFeature)) { - feature = _currentIHttpRequestLifetimeFeature; + feature = _currentIItemsFeature; } - else if (key == typeof(IHttpConnectionFeature)) + else if (key == typeof(IQueryFeature)) { - feature = _currentIHttpConnectionFeature; + feature = _currentIQueryFeature; } - else if (key == typeof(IRouteValuesFeature)) + else if (key == typeof(IRequestBodyPipeFeature)) { - feature = _currentIRouteValuesFeature; + feature = _currentIRequestBodyPipeFeature; } - else if (key == typeof(IEndpointFeature)) + else if (key == typeof(IFormFeature)) { - feature = _currentIEndpointFeature; + feature = _currentIFormFeature; } else if (key == typeof(IHttpAuthenticationFeature)) { feature = _currentIHttpAuthenticationFeature; } - else if (key == typeof(IHttpRequestTrailersFeature)) + else if (key == typeof(IHttpRequestIdentifierFeature)) { - feature = _currentIHttpRequestTrailersFeature; + feature = _currentIHttpRequestIdentifierFeature; } - else if (key == typeof(IQueryFeature)) + else if (key == typeof(IHttpConnectionFeature)) { - feature = _currentIQueryFeature; + feature = _currentIHttpConnectionFeature; } - else if (key == typeof(IFormFeature)) + else if (key == typeof(ISessionFeature)) { - feature = _currentIFormFeature; + feature = _currentISessionFeature; } - else if (key == typeof(IHttpUpgradeFeature)) + else if (key == typeof(IResponseCookiesFeature)) { - feature = _currentIHttpUpgradeFeature; + feature = _currentIResponseCookiesFeature; } - else if (key == typeof(IHttp2StreamIdFeature)) + else if (key == typeof(IHttpRequestTrailersFeature)) { - feature = _currentIHttp2StreamIdFeature; + feature = _currentIHttpRequestTrailersFeature; } else if (key == typeof(IHttpResponseTrailersFeature)) { feature = _currentIHttpResponseTrailersFeature; } - else if (key == typeof(IResponseCookiesFeature)) - { - feature = _currentIResponseCookiesFeature; - } - else if (key == typeof(IItemsFeature)) - { - feature = _currentIItemsFeature; - } else if (key == typeof(ITlsConnectionFeature)) { feature = _currentITlsConnectionFeature; } + else if (key == typeof(IHttpUpgradeFeature)) + { + feature = _currentIHttpUpgradeFeature; + } else if (key == typeof(IHttpWebSocketFeature)) { feature = _currentIHttpWebSocketFeature; } - else if (key == typeof(ISessionFeature)) + else if (key == typeof(IHttp2StreamIdFeature)) { - feature = _currentISessionFeature; + feature = _currentIHttp2StreamIdFeature; + } + else if (key == typeof(IHttpRequestLifetimeFeature)) + { + feature = _currentIHttpRequestLifetimeFeature; } else if (key == typeof(IHttpMaxRequestBodySizeFeature)) { @@ -258,6 +272,10 @@ private void ExtraFeatureSet(Type key, object? value) { feature = _currentIHttpBodyControlFeature; } + else if (key == typeof(IHttpRequestBodyDetectionFeature)) + { + feature = _currentIHttpRequestBodyDetectionFeature; + } else if (key == typeof(IHttpResetFeature)) { feature = _currentIHttpResetFeature; @@ -276,115 +294,115 @@ private void ExtraFeatureSet(Type key, object? value) if (key == typeof(IHttpRequestFeature)) { - _currentIHttpRequestFeature = value; - } - else if (key == typeof(IHttpRequestBodyDetectionFeature)) - { - _currentIHttpRequestBodyDetectionFeature = value; + _currentIHttpRequestFeature = (IHttpRequestFeature?)value; } else if (key == typeof(IHttpResponseFeature)) { - _currentIHttpResponseFeature = value; + _currentIHttpResponseFeature = (IHttpResponseFeature?)value; } else if (key == typeof(IHttpResponseBodyFeature)) { - _currentIHttpResponseBodyFeature = value; + _currentIHttpResponseBodyFeature = (IHttpResponseBodyFeature?)value; } - else if (key == typeof(IRequestBodyPipeFeature)) + else if (key == typeof(IRouteValuesFeature)) { - _currentIRequestBodyPipeFeature = value; + _currentIRouteValuesFeature = (IRouteValuesFeature?)value; } - else if (key == typeof(IHttpRequestIdentifierFeature)) + else if (key == typeof(IEndpointFeature)) { - _currentIHttpRequestIdentifierFeature = value; + _currentIEndpointFeature = (IEndpointFeature?)value; } else if (key == typeof(IServiceProvidersFeature)) { - _currentIServiceProvidersFeature = value; + _currentIServiceProvidersFeature = (IServiceProvidersFeature?)value; } - else if (key == typeof(IHttpRequestLifetimeFeature)) + else if (key == typeof(IItemsFeature)) { - _currentIHttpRequestLifetimeFeature = value; + _currentIItemsFeature = (IItemsFeature?)value; } - else if (key == typeof(IHttpConnectionFeature)) + else if (key == typeof(IQueryFeature)) { - _currentIHttpConnectionFeature = value; + _currentIQueryFeature = (IQueryFeature?)value; } - else if (key == typeof(IRouteValuesFeature)) + else if (key == typeof(IRequestBodyPipeFeature)) { - _currentIRouteValuesFeature = value; + _currentIRequestBodyPipeFeature = (IRequestBodyPipeFeature?)value; } - else if (key == typeof(IEndpointFeature)) + else if (key == typeof(IFormFeature)) { - _currentIEndpointFeature = value; + _currentIFormFeature = (IFormFeature?)value; } else if (key == typeof(IHttpAuthenticationFeature)) { - _currentIHttpAuthenticationFeature = value; + _currentIHttpAuthenticationFeature = (IHttpAuthenticationFeature?)value; } - else if (key == typeof(IHttpRequestTrailersFeature)) + else if (key == typeof(IHttpRequestIdentifierFeature)) { - _currentIHttpRequestTrailersFeature = value; + _currentIHttpRequestIdentifierFeature = (IHttpRequestIdentifierFeature?)value; } - else if (key == typeof(IQueryFeature)) + else if (key == typeof(IHttpConnectionFeature)) { - _currentIQueryFeature = value; + _currentIHttpConnectionFeature = (IHttpConnectionFeature?)value; } - else if (key == typeof(IFormFeature)) + else if (key == typeof(ISessionFeature)) { - _currentIFormFeature = value; + _currentISessionFeature = (ISessionFeature?)value; } - else if (key == typeof(IHttpUpgradeFeature)) + else if (key == typeof(IResponseCookiesFeature)) { - _currentIHttpUpgradeFeature = value; + _currentIResponseCookiesFeature = (IResponseCookiesFeature?)value; } - else if (key == typeof(IHttp2StreamIdFeature)) + else if (key == typeof(IHttpRequestTrailersFeature)) { - _currentIHttp2StreamIdFeature = value; + _currentIHttpRequestTrailersFeature = (IHttpRequestTrailersFeature?)value; } else if (key == typeof(IHttpResponseTrailersFeature)) { - _currentIHttpResponseTrailersFeature = value; + _currentIHttpResponseTrailersFeature = (IHttpResponseTrailersFeature?)value; } - else if (key == typeof(IResponseCookiesFeature)) + else if (key == typeof(ITlsConnectionFeature)) { - _currentIResponseCookiesFeature = value; + _currentITlsConnectionFeature = (ITlsConnectionFeature?)value; } - else if (key == typeof(IItemsFeature)) + else if (key == typeof(IHttpUpgradeFeature)) { - _currentIItemsFeature = value; + _currentIHttpUpgradeFeature = (IHttpUpgradeFeature?)value; } - else if (key == typeof(ITlsConnectionFeature)) + else if (key == typeof(IHttpWebSocketFeature)) { - _currentITlsConnectionFeature = value; + _currentIHttpWebSocketFeature = (IHttpWebSocketFeature?)value; } - else if (key == typeof(IHttpWebSocketFeature)) + else if (key == typeof(IHttp2StreamIdFeature)) { - _currentIHttpWebSocketFeature = value; + _currentIHttp2StreamIdFeature = (IHttp2StreamIdFeature?)value; } - else if (key == typeof(ISessionFeature)) + else if (key == typeof(IHttpRequestLifetimeFeature)) { - _currentISessionFeature = value; + _currentIHttpRequestLifetimeFeature = (IHttpRequestLifetimeFeature?)value; } else if (key == typeof(IHttpMaxRequestBodySizeFeature)) { - _currentIHttpMaxRequestBodySizeFeature = value; + _currentIHttpMaxRequestBodySizeFeature = (IHttpMaxRequestBodySizeFeature?)value; } else if (key == typeof(IHttpMinRequestBodyDataRateFeature)) { - _currentIHttpMinRequestBodyDataRateFeature = value; + _currentIHttpMinRequestBodyDataRateFeature = (IHttpMinRequestBodyDataRateFeature?)value; } else if (key == typeof(IHttpMinResponseDataRateFeature)) { - _currentIHttpMinResponseDataRateFeature = value; + _currentIHttpMinResponseDataRateFeature = (IHttpMinResponseDataRateFeature?)value; } else if (key == typeof(IHttpBodyControlFeature)) { - _currentIHttpBodyControlFeature = value; + _currentIHttpBodyControlFeature = (IHttpBodyControlFeature?)value; + } + else if (key == typeof(IHttpRequestBodyDetectionFeature)) + { + _currentIHttpRequestBodyDetectionFeature = (IHttpRequestBodyDetectionFeature?)value; } else if (key == typeof(IHttpResetFeature)) { - _currentIHttpResetFeature = value; + _currentIHttpResetFeature = (IHttpResetFeature?)value; } else { @@ -395,118 +413,122 @@ private void ExtraFeatureSet(Type key, object? value) TFeature? IFeatureCollection.Get() where TFeature : default { + // Using Unsafe.As for the cast due to https://github.com/dotnet/runtime/issues/49614 + // The type of TFeature is confirmed by the typeof() check and the As cast only accepts + // that type; however the Jit does not eliminate a regular cast in a shared generic. + TFeature? feature = default; if (typeof(TFeature) == typeof(IHttpRequestFeature)) { - feature = (TFeature?)_currentIHttpRequestFeature; - } - else if (typeof(TFeature) == typeof(IHttpRequestBodyDetectionFeature)) - { - feature = (TFeature?)_currentIHttpRequestBodyDetectionFeature; + feature = Unsafe.As(ref _currentIHttpRequestFeature); } else if (typeof(TFeature) == typeof(IHttpResponseFeature)) { - feature = (TFeature?)_currentIHttpResponseFeature; + feature = Unsafe.As(ref _currentIHttpResponseFeature); } else if (typeof(TFeature) == typeof(IHttpResponseBodyFeature)) { - feature = (TFeature?)_currentIHttpResponseBodyFeature; + feature = Unsafe.As(ref _currentIHttpResponseBodyFeature); } - else if (typeof(TFeature) == typeof(IRequestBodyPipeFeature)) + else if (typeof(TFeature) == typeof(IRouteValuesFeature)) { - feature = (TFeature?)_currentIRequestBodyPipeFeature; + feature = Unsafe.As(ref _currentIRouteValuesFeature); } - else if (typeof(TFeature) == typeof(IHttpRequestIdentifierFeature)) + else if (typeof(TFeature) == typeof(IEndpointFeature)) { - feature = (TFeature?)_currentIHttpRequestIdentifierFeature; + feature = Unsafe.As(ref _currentIEndpointFeature); } else if (typeof(TFeature) == typeof(IServiceProvidersFeature)) { - feature = (TFeature?)_currentIServiceProvidersFeature; + feature = Unsafe.As(ref _currentIServiceProvidersFeature); } - else if (typeof(TFeature) == typeof(IHttpRequestLifetimeFeature)) + else if (typeof(TFeature) == typeof(IItemsFeature)) { - feature = (TFeature?)_currentIHttpRequestLifetimeFeature; + feature = Unsafe.As(ref _currentIItemsFeature); } - else if (typeof(TFeature) == typeof(IHttpConnectionFeature)) + else if (typeof(TFeature) == typeof(IQueryFeature)) { - feature = (TFeature?)_currentIHttpConnectionFeature; + feature = Unsafe.As(ref _currentIQueryFeature); } - else if (typeof(TFeature) == typeof(IRouteValuesFeature)) + else if (typeof(TFeature) == typeof(IRequestBodyPipeFeature)) { - feature = (TFeature?)_currentIRouteValuesFeature; + feature = Unsafe.As(ref _currentIRequestBodyPipeFeature); } - else if (typeof(TFeature) == typeof(IEndpointFeature)) + else if (typeof(TFeature) == typeof(IFormFeature)) { - feature = (TFeature?)_currentIEndpointFeature; + feature = Unsafe.As(ref _currentIFormFeature); } else if (typeof(TFeature) == typeof(IHttpAuthenticationFeature)) { - feature = (TFeature?)_currentIHttpAuthenticationFeature; + feature = Unsafe.As(ref _currentIHttpAuthenticationFeature); } - else if (typeof(TFeature) == typeof(IHttpRequestTrailersFeature)) + else if (typeof(TFeature) == typeof(IHttpRequestIdentifierFeature)) { - feature = (TFeature?)_currentIHttpRequestTrailersFeature; + feature = Unsafe.As(ref _currentIHttpRequestIdentifierFeature); } - else if (typeof(TFeature) == typeof(IQueryFeature)) + else if (typeof(TFeature) == typeof(IHttpConnectionFeature)) { - feature = (TFeature?)_currentIQueryFeature; + feature = Unsafe.As(ref _currentIHttpConnectionFeature); } - else if (typeof(TFeature) == typeof(IFormFeature)) + else if (typeof(TFeature) == typeof(ISessionFeature)) { - feature = (TFeature?)_currentIFormFeature; + feature = Unsafe.As(ref _currentISessionFeature); } - else if (typeof(TFeature) == typeof(IHttpUpgradeFeature)) + else if (typeof(TFeature) == typeof(IResponseCookiesFeature)) { - feature = (TFeature?)_currentIHttpUpgradeFeature; + feature = Unsafe.As(ref _currentIResponseCookiesFeature); } - else if (typeof(TFeature) == typeof(IHttp2StreamIdFeature)) + else if (typeof(TFeature) == typeof(IHttpRequestTrailersFeature)) { - feature = (TFeature?)_currentIHttp2StreamIdFeature; + feature = Unsafe.As(ref _currentIHttpRequestTrailersFeature); } else if (typeof(TFeature) == typeof(IHttpResponseTrailersFeature)) { - feature = (TFeature?)_currentIHttpResponseTrailersFeature; + feature = Unsafe.As(ref _currentIHttpResponseTrailersFeature); } - else if (typeof(TFeature) == typeof(IResponseCookiesFeature)) + else if (typeof(TFeature) == typeof(ITlsConnectionFeature)) { - feature = (TFeature?)_currentIResponseCookiesFeature; + feature = Unsafe.As(ref _currentITlsConnectionFeature); } - else if (typeof(TFeature) == typeof(IItemsFeature)) + else if (typeof(TFeature) == typeof(IHttpUpgradeFeature)) { - feature = (TFeature?)_currentIItemsFeature; + feature = Unsafe.As(ref _currentIHttpUpgradeFeature); } - else if (typeof(TFeature) == typeof(ITlsConnectionFeature)) + else if (typeof(TFeature) == typeof(IHttpWebSocketFeature)) { - feature = (TFeature?)_currentITlsConnectionFeature; + feature = Unsafe.As(ref _currentIHttpWebSocketFeature); } - else if (typeof(TFeature) == typeof(IHttpWebSocketFeature)) + else if (typeof(TFeature) == typeof(IHttp2StreamIdFeature)) { - feature = (TFeature?)_currentIHttpWebSocketFeature; + feature = Unsafe.As(ref _currentIHttp2StreamIdFeature); } - else if (typeof(TFeature) == typeof(ISessionFeature)) + else if (typeof(TFeature) == typeof(IHttpRequestLifetimeFeature)) { - feature = (TFeature?)_currentISessionFeature; + feature = Unsafe.As(ref _currentIHttpRequestLifetimeFeature); } else if (typeof(TFeature) == typeof(IHttpMaxRequestBodySizeFeature)) { - feature = (TFeature?)_currentIHttpMaxRequestBodySizeFeature; + feature = Unsafe.As(ref _currentIHttpMaxRequestBodySizeFeature); } else if (typeof(TFeature) == typeof(IHttpMinRequestBodyDataRateFeature)) { - feature = (TFeature?)_currentIHttpMinRequestBodyDataRateFeature; + feature = Unsafe.As(ref _currentIHttpMinRequestBodyDataRateFeature); } else if (typeof(TFeature) == typeof(IHttpMinResponseDataRateFeature)) { - feature = (TFeature?)_currentIHttpMinResponseDataRateFeature; + feature = Unsafe.As(ref _currentIHttpMinResponseDataRateFeature); } else if (typeof(TFeature) == typeof(IHttpBodyControlFeature)) { - feature = (TFeature?)_currentIHttpBodyControlFeature; + feature = Unsafe.As(ref _currentIHttpBodyControlFeature); + } + else if (typeof(TFeature) == typeof(IHttpRequestBodyDetectionFeature)) + { + feature = Unsafe.As(ref _currentIHttpRequestBodyDetectionFeature); } else if (typeof(TFeature) == typeof(IHttpResetFeature)) { - feature = (TFeature?)_currentIHttpResetFeature; + feature = Unsafe.As(ref _currentIHttpResetFeature); } else if (MaybeExtra != null) { @@ -523,118 +545,122 @@ private void ExtraFeatureSet(Type key, object? value) void IFeatureCollection.Set(TFeature? feature) where TFeature : default { + // Using Unsafe.As for the cast due to https://github.com/dotnet/runtime/issues/49614 + // The type of TFeature is confirmed by the typeof() check and the As cast only accepts + // that type; however the Jit does not eliminate a regular cast in a shared generic. + _featureRevision++; if (typeof(TFeature) == typeof(IHttpRequestFeature)) { - _currentIHttpRequestFeature = feature; - } - else if (typeof(TFeature) == typeof(IHttpRequestBodyDetectionFeature)) - { - _currentIHttpRequestBodyDetectionFeature = feature; + _currentIHttpRequestFeature = Unsafe.As(ref feature); } else if (typeof(TFeature) == typeof(IHttpResponseFeature)) { - _currentIHttpResponseFeature = feature; + _currentIHttpResponseFeature = Unsafe.As(ref feature); } else if (typeof(TFeature) == typeof(IHttpResponseBodyFeature)) { - _currentIHttpResponseBodyFeature = feature; + _currentIHttpResponseBodyFeature = Unsafe.As(ref feature); } - else if (typeof(TFeature) == typeof(IRequestBodyPipeFeature)) + else if (typeof(TFeature) == typeof(IRouteValuesFeature)) { - _currentIRequestBodyPipeFeature = feature; + _currentIRouteValuesFeature = Unsafe.As(ref feature); } - else if (typeof(TFeature) == typeof(IHttpRequestIdentifierFeature)) + else if (typeof(TFeature) == typeof(IEndpointFeature)) { - _currentIHttpRequestIdentifierFeature = feature; + _currentIEndpointFeature = Unsafe.As(ref feature); } else if (typeof(TFeature) == typeof(IServiceProvidersFeature)) { - _currentIServiceProvidersFeature = feature; + _currentIServiceProvidersFeature = Unsafe.As(ref feature); } - else if (typeof(TFeature) == typeof(IHttpRequestLifetimeFeature)) + else if (typeof(TFeature) == typeof(IItemsFeature)) { - _currentIHttpRequestLifetimeFeature = feature; + _currentIItemsFeature = Unsafe.As(ref feature); } - else if (typeof(TFeature) == typeof(IHttpConnectionFeature)) + else if (typeof(TFeature) == typeof(IQueryFeature)) { - _currentIHttpConnectionFeature = feature; + _currentIQueryFeature = Unsafe.As(ref feature); } - else if (typeof(TFeature) == typeof(IRouteValuesFeature)) + else if (typeof(TFeature) == typeof(IRequestBodyPipeFeature)) { - _currentIRouteValuesFeature = feature; + _currentIRequestBodyPipeFeature = Unsafe.As(ref feature); } - else if (typeof(TFeature) == typeof(IEndpointFeature)) + else if (typeof(TFeature) == typeof(IFormFeature)) { - _currentIEndpointFeature = feature; + _currentIFormFeature = Unsafe.As(ref feature); } else if (typeof(TFeature) == typeof(IHttpAuthenticationFeature)) { - _currentIHttpAuthenticationFeature = feature; + _currentIHttpAuthenticationFeature = Unsafe.As(ref feature); } - else if (typeof(TFeature) == typeof(IHttpRequestTrailersFeature)) + else if (typeof(TFeature) == typeof(IHttpRequestIdentifierFeature)) { - _currentIHttpRequestTrailersFeature = feature; + _currentIHttpRequestIdentifierFeature = Unsafe.As(ref feature); } - else if (typeof(TFeature) == typeof(IQueryFeature)) + else if (typeof(TFeature) == typeof(IHttpConnectionFeature)) { - _currentIQueryFeature = feature; + _currentIHttpConnectionFeature = Unsafe.As(ref feature); } - else if (typeof(TFeature) == typeof(IFormFeature)) + else if (typeof(TFeature) == typeof(ISessionFeature)) { - _currentIFormFeature = feature; + _currentISessionFeature = Unsafe.As(ref feature); } - else if (typeof(TFeature) == typeof(IHttpUpgradeFeature)) + else if (typeof(TFeature) == typeof(IResponseCookiesFeature)) { - _currentIHttpUpgradeFeature = feature; + _currentIResponseCookiesFeature = Unsafe.As(ref feature); } - else if (typeof(TFeature) == typeof(IHttp2StreamIdFeature)) + else if (typeof(TFeature) == typeof(IHttpRequestTrailersFeature)) { - _currentIHttp2StreamIdFeature = feature; + _currentIHttpRequestTrailersFeature = Unsafe.As(ref feature); } else if (typeof(TFeature) == typeof(IHttpResponseTrailersFeature)) { - _currentIHttpResponseTrailersFeature = feature; + _currentIHttpResponseTrailersFeature = Unsafe.As(ref feature); } - else if (typeof(TFeature) == typeof(IResponseCookiesFeature)) + else if (typeof(TFeature) == typeof(ITlsConnectionFeature)) { - _currentIResponseCookiesFeature = feature; + _currentITlsConnectionFeature = Unsafe.As(ref feature); } - else if (typeof(TFeature) == typeof(IItemsFeature)) + else if (typeof(TFeature) == typeof(IHttpUpgradeFeature)) { - _currentIItemsFeature = feature; + _currentIHttpUpgradeFeature = Unsafe.As(ref feature); } - else if (typeof(TFeature) == typeof(ITlsConnectionFeature)) + else if (typeof(TFeature) == typeof(IHttpWebSocketFeature)) { - _currentITlsConnectionFeature = feature; + _currentIHttpWebSocketFeature = Unsafe.As(ref feature); } - else if (typeof(TFeature) == typeof(IHttpWebSocketFeature)) + else if (typeof(TFeature) == typeof(IHttp2StreamIdFeature)) { - _currentIHttpWebSocketFeature = feature; + _currentIHttp2StreamIdFeature = Unsafe.As(ref feature); } - else if (typeof(TFeature) == typeof(ISessionFeature)) + else if (typeof(TFeature) == typeof(IHttpRequestLifetimeFeature)) { - _currentISessionFeature = feature; + _currentIHttpRequestLifetimeFeature = Unsafe.As(ref feature); } else if (typeof(TFeature) == typeof(IHttpMaxRequestBodySizeFeature)) { - _currentIHttpMaxRequestBodySizeFeature = feature; + _currentIHttpMaxRequestBodySizeFeature = Unsafe.As(ref feature); } else if (typeof(TFeature) == typeof(IHttpMinRequestBodyDataRateFeature)) { - _currentIHttpMinRequestBodyDataRateFeature = feature; + _currentIHttpMinRequestBodyDataRateFeature = Unsafe.As(ref feature); } else if (typeof(TFeature) == typeof(IHttpMinResponseDataRateFeature)) { - _currentIHttpMinResponseDataRateFeature = feature; + _currentIHttpMinResponseDataRateFeature = Unsafe.As(ref feature); } else if (typeof(TFeature) == typeof(IHttpBodyControlFeature)) { - _currentIHttpBodyControlFeature = feature; + _currentIHttpBodyControlFeature = Unsafe.As(ref feature); + } + else if (typeof(TFeature) == typeof(IHttpRequestBodyDetectionFeature)) + { + _currentIHttpRequestBodyDetectionFeature = Unsafe.As(ref feature); } else if (typeof(TFeature) == typeof(IHttpResetFeature)) { - _currentIHttpResetFeature = feature; + _currentIHttpResetFeature = Unsafe.As(ref feature); } else { @@ -648,10 +674,6 @@ private IEnumerable> FastEnumerable() { yield return new KeyValuePair(typeof(IHttpRequestFeature), _currentIHttpRequestFeature); } - if (_currentIHttpRequestBodyDetectionFeature != null) - { - yield return new KeyValuePair(typeof(IHttpRequestBodyDetectionFeature), _currentIHttpRequestBodyDetectionFeature); - } if (_currentIHttpResponseFeature != null) { yield return new KeyValuePair(typeof(IHttpResponseFeature), _currentIHttpResponseFeature); @@ -660,81 +682,81 @@ private IEnumerable> FastEnumerable() { yield return new KeyValuePair(typeof(IHttpResponseBodyFeature), _currentIHttpResponseBodyFeature); } - if (_currentIRequestBodyPipeFeature != null) + if (_currentIRouteValuesFeature != null) { - yield return new KeyValuePair(typeof(IRequestBodyPipeFeature), _currentIRequestBodyPipeFeature); + yield return new KeyValuePair(typeof(IRouteValuesFeature), _currentIRouteValuesFeature); } - if (_currentIHttpRequestIdentifierFeature != null) + if (_currentIEndpointFeature != null) { - yield return new KeyValuePair(typeof(IHttpRequestIdentifierFeature), _currentIHttpRequestIdentifierFeature); + yield return new KeyValuePair(typeof(IEndpointFeature), _currentIEndpointFeature); } if (_currentIServiceProvidersFeature != null) { yield return new KeyValuePair(typeof(IServiceProvidersFeature), _currentIServiceProvidersFeature); } - if (_currentIHttpRequestLifetimeFeature != null) + if (_currentIItemsFeature != null) { - yield return new KeyValuePair(typeof(IHttpRequestLifetimeFeature), _currentIHttpRequestLifetimeFeature); + yield return new KeyValuePair(typeof(IItemsFeature), _currentIItemsFeature); } - if (_currentIHttpConnectionFeature != null) + if (_currentIQueryFeature != null) { - yield return new KeyValuePair(typeof(IHttpConnectionFeature), _currentIHttpConnectionFeature); + yield return new KeyValuePair(typeof(IQueryFeature), _currentIQueryFeature); } - if (_currentIRouteValuesFeature != null) + if (_currentIRequestBodyPipeFeature != null) { - yield return new KeyValuePair(typeof(IRouteValuesFeature), _currentIRouteValuesFeature); + yield return new KeyValuePair(typeof(IRequestBodyPipeFeature), _currentIRequestBodyPipeFeature); } - if (_currentIEndpointFeature != null) + if (_currentIFormFeature != null) { - yield return new KeyValuePair(typeof(IEndpointFeature), _currentIEndpointFeature); + yield return new KeyValuePair(typeof(IFormFeature), _currentIFormFeature); } if (_currentIHttpAuthenticationFeature != null) { yield return new KeyValuePair(typeof(IHttpAuthenticationFeature), _currentIHttpAuthenticationFeature); } - if (_currentIHttpRequestTrailersFeature != null) + if (_currentIHttpRequestIdentifierFeature != null) { - yield return new KeyValuePair(typeof(IHttpRequestTrailersFeature), _currentIHttpRequestTrailersFeature); + yield return new KeyValuePair(typeof(IHttpRequestIdentifierFeature), _currentIHttpRequestIdentifierFeature); } - if (_currentIQueryFeature != null) + if (_currentIHttpConnectionFeature != null) { - yield return new KeyValuePair(typeof(IQueryFeature), _currentIQueryFeature); + yield return new KeyValuePair(typeof(IHttpConnectionFeature), _currentIHttpConnectionFeature); } - if (_currentIFormFeature != null) + if (_currentISessionFeature != null) { - yield return new KeyValuePair(typeof(IFormFeature), _currentIFormFeature); + yield return new KeyValuePair(typeof(ISessionFeature), _currentISessionFeature); } - if (_currentIHttpUpgradeFeature != null) + if (_currentIResponseCookiesFeature != null) { - yield return new KeyValuePair(typeof(IHttpUpgradeFeature), _currentIHttpUpgradeFeature); + yield return new KeyValuePair(typeof(IResponseCookiesFeature), _currentIResponseCookiesFeature); } - if (_currentIHttp2StreamIdFeature != null) + if (_currentIHttpRequestTrailersFeature != null) { - yield return new KeyValuePair(typeof(IHttp2StreamIdFeature), _currentIHttp2StreamIdFeature); + yield return new KeyValuePair(typeof(IHttpRequestTrailersFeature), _currentIHttpRequestTrailersFeature); } if (_currentIHttpResponseTrailersFeature != null) { yield return new KeyValuePair(typeof(IHttpResponseTrailersFeature), _currentIHttpResponseTrailersFeature); } - if (_currentIResponseCookiesFeature != null) - { - yield return new KeyValuePair(typeof(IResponseCookiesFeature), _currentIResponseCookiesFeature); - } - if (_currentIItemsFeature != null) - { - yield return new KeyValuePair(typeof(IItemsFeature), _currentIItemsFeature); - } if (_currentITlsConnectionFeature != null) { yield return new KeyValuePair(typeof(ITlsConnectionFeature), _currentITlsConnectionFeature); } + if (_currentIHttpUpgradeFeature != null) + { + yield return new KeyValuePair(typeof(IHttpUpgradeFeature), _currentIHttpUpgradeFeature); + } if (_currentIHttpWebSocketFeature != null) { yield return new KeyValuePair(typeof(IHttpWebSocketFeature), _currentIHttpWebSocketFeature); } - if (_currentISessionFeature != null) + if (_currentIHttp2StreamIdFeature != null) { - yield return new KeyValuePair(typeof(ISessionFeature), _currentISessionFeature); + yield return new KeyValuePair(typeof(IHttp2StreamIdFeature), _currentIHttp2StreamIdFeature); + } + if (_currentIHttpRequestLifetimeFeature != null) + { + yield return new KeyValuePair(typeof(IHttpRequestLifetimeFeature), _currentIHttpRequestLifetimeFeature); } if (_currentIHttpMaxRequestBodySizeFeature != null) { @@ -752,6 +774,10 @@ private IEnumerable> FastEnumerable() { yield return new KeyValuePair(typeof(IHttpBodyControlFeature), _currentIHttpBodyControlFeature); } + if (_currentIHttpRequestBodyDetectionFeature != null) + { + yield return new KeyValuePair(typeof(IHttpRequestBodyDetectionFeature), _currentIHttpRequestBodyDetectionFeature); + } if (_currentIHttpResetFeature != null) { yield return new KeyValuePair(typeof(IHttpResetFeature), _currentIHttpResetFeature); diff --git a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs index c8507f9e4bb7..28dc06ff310a 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http2/Http2Stream.cs @@ -114,7 +114,11 @@ protected override void OnReset() _keepAlive = true; _connectionAborted = false; - ResetHttp2Features(); + // Reset Http2 Features + _currentIHttpMinRequestBodyDataRateFeature = this; + _currentIHttp2StreamIdFeature = this; + _currentIHttpResponseTrailersFeature = this; + _currentIHttpResetFeature = this; } protected override void OnRequestProcessingEnded() diff --git a/src/Servers/Kestrel/Core/src/Internal/Http3/Http3Stream.cs b/src/Servers/Kestrel/Core/src/Internal/Http3/Http3Stream.cs index fa269864302c..2911ef0b1d19 100644 --- a/src/Servers/Kestrel/Core/src/Internal/Http3/Http3Stream.cs +++ b/src/Servers/Kestrel/Core/src/Internal/Http3/Http3Stream.cs @@ -577,7 +577,10 @@ public void Tick(DateTimeOffset now) protected override void OnReset() { - ResetHttp3Features(); + // Reset Http3 Features + _currentIHttpMinRequestBodyDataRateFeature = this; + _currentIHttpResponseTrailersFeature = this; + _currentIHttpResetFeature = this; } protected override void ApplicationAbort() => ApplicationAbort(new ConnectionAbortedException(CoreStrings.ConnectionAbortedByApplication), Http3ErrorCode.InternalError); diff --git a/src/Servers/Kestrel/perf/Microbenchmarks/HttpProtocolFeatureCollection.cs b/src/Servers/Kestrel/perf/Microbenchmarks/HttpProtocolFeatureCollection.cs index 00463d9f31cf..ee0fc5c7f45e 100644 --- a/src/Servers/Kestrel/perf/Microbenchmarks/HttpProtocolFeatureCollection.cs +++ b/src/Servers/Kestrel/perf/Microbenchmarks/HttpProtocolFeatureCollection.cs @@ -5,12 +5,13 @@ using System.Buffers; using System.IO.Pipelines; using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; + using BenchmarkDotNet.Attributes; + using Microsoft.AspNetCore.Http.Features; +using Microsoft.AspNetCore.Http.Features.Authentication; using Microsoft.AspNetCore.Server.Kestrel.Core; -using Microsoft.AspNetCore.Server.Kestrel.Core.Internal; +using Microsoft.AspNetCore.Server.Kestrel.Core.Features; using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http; using Microsoft.AspNetCore.Testing; @@ -20,45 +21,205 @@ public class HttpProtocolFeatureCollection { private readonly IFeatureCollection _collection; - [Benchmark] + [Benchmark(Description = "Get*")] [MethodImpl(MethodImplOptions.NoInlining)] - public IHttpRequestFeature GetViaTypeOf_First() + public IHttpRequestFeature Get_IHttpRequestFeature() { - return (IHttpRequestFeature)_collection[typeof(IHttpRequestFeature)]; + return _collection.Get(); } - [Benchmark] + [Benchmark(Description = "Get*")] [MethodImpl(MethodImplOptions.NoInlining)] - public IHttpRequestFeature GetViaGeneric_First() + public IHttpResponseFeature Get_IHttpResponseFeature() { - return _collection.Get(); + return _collection.Get(); + } + + [Benchmark(Description = "Get*")] + [MethodImpl(MethodImplOptions.NoInlining)] + public IHttpResponseBodyFeature Get_IHttpResponseBodyFeature() + { + return _collection.Get(); + } + + [Benchmark(Description = "Get*")] + [MethodImpl(MethodImplOptions.NoInlining)] + public IRouteValuesFeature Get_IRouteValuesFeature() + { + return _collection.Get(); + } + + [Benchmark(Description = "Get*")] + [MethodImpl(MethodImplOptions.NoInlining)] + public IEndpointFeature Get_IEndpointFeature() + { + return _collection.Get(); + } + + [Benchmark(Description = "Get")] + [MethodImpl(MethodImplOptions.NoInlining)] + public IServiceProvidersFeature Get_IServiceProvidersFeature() + { + return _collection.Get(); + } + + [Benchmark(Description = "Get")] + [MethodImpl(MethodImplOptions.NoInlining)] + public IItemsFeature Get_IItemsFeature() + { + return _collection.Get(); + } + + [Benchmark(Description = "Get")] + [MethodImpl(MethodImplOptions.NoInlining)] + public IQueryFeature Get_IQueryFeature() + { + return _collection.Get(); + } + + [Benchmark(Description = "Get*")] + [MethodImpl(MethodImplOptions.NoInlining)] + public IRequestBodyPipeFeature Get_IRequestBodyPipeFeature() + { + return _collection.Get(); + } + + [Benchmark(Description = "Get")] + [MethodImpl(MethodImplOptions.NoInlining)] + public IFormFeature Get_IFormFeature() + { + return _collection.Get(); + } + + [Benchmark(Description = "Get")] + [MethodImpl(MethodImplOptions.NoInlining)] + public IHttpAuthenticationFeature Get_IHttpAuthenticationFeature() + { + return _collection.Get(); + } + + [Benchmark(Description = "Get*")] + [MethodImpl(MethodImplOptions.NoInlining)] + public IHttpRequestIdentifierFeature Get_IHttpRequestIdentifierFeature() + { + return _collection.Get(); + } + + [Benchmark(Description = "Get*")] + [MethodImpl(MethodImplOptions.NoInlining)] + public IHttpConnectionFeature Get_IHttpConnectionFeature() + { + return _collection.Get(); + } + + [Benchmark(Description = "Get")] + [MethodImpl(MethodImplOptions.NoInlining)] + public ISessionFeature Get_ISessionFeature() + { + return _collection.Get(); } - [Benchmark] + [Benchmark(Description = "Get")] [MethodImpl(MethodImplOptions.NoInlining)] - public object GetViaTypeOf_Custom() + public IResponseCookiesFeature Get_IResponseCookiesFeature() { - return (IHttpCustomFeature)_collection[typeof(IHttpCustomFeature)]; + return _collection.Get(); } - [Benchmark] + [Benchmark(Description = "Get*")] [MethodImpl(MethodImplOptions.NoInlining)] - public object GetViaGeneric_Custom() + public IHttpRequestTrailersFeature Get_IHttpRequestTrailersFeature() { - return _collection.Get(); + return _collection.Get(); } + [Benchmark(Description = "Get")] + [MethodImpl(MethodImplOptions.NoInlining)] + public IHttpResponseTrailersFeature Get_IHttpResponseTrailersFeature() + { + return _collection.Get(); + } - [Benchmark] + [Benchmark(Description = "Get")] [MethodImpl(MethodImplOptions.NoInlining)] - public object GetViaTypeOf_NotFound() + public ITlsConnectionFeature Get_ITlsConnectionFeature() { - return (IHttpNotFoundFeature)_collection[typeof(IHttpNotFoundFeature)]; + return _collection.Get(); } - [Benchmark] + [Benchmark(Description = "Get*")] [MethodImpl(MethodImplOptions.NoInlining)] - public object GetViaGeneric_NotFound() + public IHttpUpgradeFeature Get_IHttpUpgradeFeature() + { + return _collection.Get(); + } + + [Benchmark(Description = "Get")] + [MethodImpl(MethodImplOptions.NoInlining)] + public IHttpWebSocketFeature Get_IHttpWebSocketFeature() + { + return _collection.Get(); + } + + [Benchmark(Description = "Get")] + [MethodImpl(MethodImplOptions.NoInlining)] + public IHttp2StreamIdFeature Get_IHttp2StreamIdFeature() + { + return _collection.Get(); + } + + [Benchmark(Description = "Get*")] + [MethodImpl(MethodImplOptions.NoInlining)] + public IHttpRequestLifetimeFeature Get_IHttpRequestLifetimeFeature() + { + return _collection.Get(); + } + + [Benchmark(Description = "Get*")] + [MethodImpl(MethodImplOptions.NoInlining)] + public IHttpMaxRequestBodySizeFeature Get_IHttpMaxRequestBodySizeFeature() + { + return _collection.Get(); + } + + [Benchmark(Description = "Get*")] + [MethodImpl(MethodImplOptions.NoInlining)] + public IHttpMinRequestBodyDataRateFeature Get_IHttpMinRequestBodyDataRateFeature() + { + return _collection.Get(); + } + + [Benchmark(Description = "Get*")] + [MethodImpl(MethodImplOptions.NoInlining)] + public IHttpMinResponseDataRateFeature Get_IHttpMinResponseDataRateFeature() + { + return _collection.Get(); + } + + [Benchmark(Description = "Get*")] + [MethodImpl(MethodImplOptions.NoInlining)] + public IHttpBodyControlFeature Get_IHttpBodyControlFeature() + { + return _collection.Get(); + } + + [Benchmark(Description = "Get*")] + [MethodImpl(MethodImplOptions.NoInlining)] + public IHttpRequestBodyDetectionFeature Get_IHttpRequestBodyDetectionFeature() + { + return _collection.Get(); + } + + [Benchmark(Description = "Get")] + [MethodImpl(MethodImplOptions.NoInlining)] + public IHttpResetFeature Get_IHttpResetFeature() + { + return _collection.Get(); + } + + [Benchmark(Description = "Get")] + [MethodImpl(MethodImplOptions.NoInlining)] + public IHttpNotFoundFeature Get_IHttpNotFoundFeature() { return _collection.Get(); } @@ -89,11 +250,7 @@ public HttpProtocolFeatureCollection() _collection = http1Connection; } - private interface IHttpCustomFeature - { - } - - private interface IHttpNotFoundFeature + public interface IHttpNotFoundFeature { } } diff --git a/src/Servers/Kestrel/shared/TransportConnection.FeatureCollection.cs b/src/Servers/Kestrel/shared/TransportConnection.FeatureCollection.cs index 5d94f6e3c485..757927e2f433 100644 --- a/src/Servers/Kestrel/shared/TransportConnection.FeatureCollection.cs +++ b/src/Servers/Kestrel/shared/TransportConnection.FeatureCollection.cs @@ -11,14 +11,11 @@ namespace Microsoft.AspNetCore.Connections { - internal partial class TransportConnection : IConnectionIdFeature, - IConnectionTransportFeature, - IConnectionItemsFeature, - IMemoryPoolFeature, - IConnectionLifetimeFeature + internal partial class TransportConnection { // NOTE: When feature interfaces are added to or removed from this TransportConnection class implementation, - // then the list of `features` in the generated code project MUST also be updated. + // then the list of `features` in the generated code project MUST also be updated first + // and the code generator re-reun, which will change the interface list. // See also: tools/CodeGenerator/TransportConnectionFeatureCollection.cs MemoryPool IMemoryPoolFeature.MemoryPool => MemoryPool; diff --git a/src/Servers/Kestrel/shared/TransportConnection.Generated.cs b/src/Servers/Kestrel/shared/TransportConnection.Generated.cs index b35954bb6fa2..0f32b7d54549 100644 --- a/src/Servers/Kestrel/shared/TransportConnection.Generated.cs +++ b/src/Servers/Kestrel/shared/TransportConnection.Generated.cs @@ -4,6 +4,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Runtime.CompilerServices; using Microsoft.AspNetCore.Connections.Features; using Microsoft.AspNetCore.Http.Features; @@ -12,13 +13,19 @@ namespace Microsoft.AspNetCore.Connections { - internal partial class TransportConnection : IFeatureCollection + internal partial class TransportConnection : IFeatureCollection, + IConnectionIdFeature, + IConnectionTransportFeature, + IConnectionItemsFeature, + IMemoryPoolFeature, + IConnectionLifetimeFeature { - private object? _currentIConnectionIdFeature; - private object? _currentIConnectionTransportFeature; - private object? _currentIConnectionItemsFeature; - private object? _currentIMemoryPoolFeature; - private object? _currentIConnectionLifetimeFeature; + // Implemented features + internal protected IConnectionIdFeature? _currentIConnectionIdFeature; + internal protected IConnectionTransportFeature? _currentIConnectionTransportFeature; + internal protected IConnectionItemsFeature? _currentIConnectionItemsFeature; + internal protected IMemoryPoolFeature? _currentIMemoryPoolFeature; + internal protected IConnectionLifetimeFeature? _currentIConnectionLifetimeFeature; private int _featureRevision; @@ -137,23 +144,23 @@ private void ExtraFeatureSet(Type key, object? value) if (key == typeof(IConnectionIdFeature)) { - _currentIConnectionIdFeature = value; + _currentIConnectionIdFeature = (IConnectionIdFeature?)value; } else if (key == typeof(IConnectionTransportFeature)) { - _currentIConnectionTransportFeature = value; + _currentIConnectionTransportFeature = (IConnectionTransportFeature?)value; } else if (key == typeof(IConnectionItemsFeature)) { - _currentIConnectionItemsFeature = value; + _currentIConnectionItemsFeature = (IConnectionItemsFeature?)value; } else if (key == typeof(IMemoryPoolFeature)) { - _currentIMemoryPoolFeature = value; + _currentIMemoryPoolFeature = (IMemoryPoolFeature?)value; } else if (key == typeof(IConnectionLifetimeFeature)) { - _currentIConnectionLifetimeFeature = value; + _currentIConnectionLifetimeFeature = (IConnectionLifetimeFeature?)value; } else { @@ -164,26 +171,30 @@ private void ExtraFeatureSet(Type key, object? value) TFeature? IFeatureCollection.Get() where TFeature : default { + // Using Unsafe.As for the cast due to https://github.com/dotnet/runtime/issues/49614 + // The type of TFeature is confirmed by the typeof() check and the As cast only accepts + // that type; however the Jit does not eliminate a regular cast in a shared generic. + TFeature? feature = default; if (typeof(TFeature) == typeof(IConnectionIdFeature)) { - feature = (TFeature?)_currentIConnectionIdFeature; + feature = Unsafe.As(ref _currentIConnectionIdFeature); } else if (typeof(TFeature) == typeof(IConnectionTransportFeature)) { - feature = (TFeature?)_currentIConnectionTransportFeature; + feature = Unsafe.As(ref _currentIConnectionTransportFeature); } else if (typeof(TFeature) == typeof(IConnectionItemsFeature)) { - feature = (TFeature?)_currentIConnectionItemsFeature; + feature = Unsafe.As(ref _currentIConnectionItemsFeature); } else if (typeof(TFeature) == typeof(IMemoryPoolFeature)) { - feature = (TFeature?)_currentIMemoryPoolFeature; + feature = Unsafe.As(ref _currentIMemoryPoolFeature); } else if (typeof(TFeature) == typeof(IConnectionLifetimeFeature)) { - feature = (TFeature?)_currentIConnectionLifetimeFeature; + feature = Unsafe.As(ref _currentIConnectionLifetimeFeature); } else if (MaybeExtra != null) { @@ -195,26 +206,30 @@ private void ExtraFeatureSet(Type key, object? value) void IFeatureCollection.Set(TFeature? feature) where TFeature : default { + // Using Unsafe.As for the cast due to https://github.com/dotnet/runtime/issues/49614 + // The type of TFeature is confirmed by the typeof() check and the As cast only accepts + // that type; however the Jit does not eliminate a regular cast in a shared generic. + _featureRevision++; if (typeof(TFeature) == typeof(IConnectionIdFeature)) { - _currentIConnectionIdFeature = feature; + _currentIConnectionIdFeature = Unsafe.As(ref feature); } else if (typeof(TFeature) == typeof(IConnectionTransportFeature)) { - _currentIConnectionTransportFeature = feature; + _currentIConnectionTransportFeature = Unsafe.As(ref feature); } else if (typeof(TFeature) == typeof(IConnectionItemsFeature)) { - _currentIConnectionItemsFeature = feature; + _currentIConnectionItemsFeature = Unsafe.As(ref feature); } else if (typeof(TFeature) == typeof(IMemoryPoolFeature)) { - _currentIMemoryPoolFeature = feature; + _currentIMemoryPoolFeature = Unsafe.As(ref feature); } else if (typeof(TFeature) == typeof(IConnectionLifetimeFeature)) { - _currentIConnectionLifetimeFeature = feature; + _currentIConnectionLifetimeFeature = Unsafe.As(ref feature); } else { diff --git a/src/Servers/Kestrel/shared/TransportMultiplexedConnection.FeatureCollection.cs b/src/Servers/Kestrel/shared/TransportMultiplexedConnection.FeatureCollection.cs index 7edfc98014b9..918903b4bbe0 100644 --- a/src/Servers/Kestrel/shared/TransportMultiplexedConnection.FeatureCollection.cs +++ b/src/Servers/Kestrel/shared/TransportMultiplexedConnection.FeatureCollection.cs @@ -3,19 +3,16 @@ using System.Buffers; using System.Collections.Generic; -using System.IO.Pipelines; using System.Threading; using Microsoft.AspNetCore.Connections.Features; namespace Microsoft.AspNetCore.Connections { - internal partial class TransportMultiplexedConnection : IConnectionIdFeature, - IConnectionItemsFeature, - IMemoryPoolFeature, - IConnectionLifetimeFeature + internal partial class TransportMultiplexedConnection { // NOTE: When feature interfaces are added to or removed from this TransportConnection class implementation, - // then the list of `features` in the generated code project MUST also be updated. + // then the list of `features` in the generated code project MUST also be updated first + // and the code generator re-reun, which will change the interface list. // See also: tools/CodeGenerator/TransportConnectionFeatureCollection.cs MemoryPool IMemoryPoolFeature.MemoryPool => MemoryPool; diff --git a/src/Servers/Kestrel/shared/TransportMultiplexedConnection.Generated.cs b/src/Servers/Kestrel/shared/TransportMultiplexedConnection.Generated.cs index cdfc0bff16c3..5be4c4c48cc8 100644 --- a/src/Servers/Kestrel/shared/TransportMultiplexedConnection.Generated.cs +++ b/src/Servers/Kestrel/shared/TransportMultiplexedConnection.Generated.cs @@ -4,6 +4,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Runtime.CompilerServices; using Microsoft.AspNetCore.Connections.Features; using Microsoft.AspNetCore.Http.Features; @@ -12,13 +13,20 @@ namespace Microsoft.AspNetCore.Connections { - internal partial class TransportMultiplexedConnection : IFeatureCollection + internal partial class TransportMultiplexedConnection : IFeatureCollection, + IConnectionIdFeature, + IConnectionItemsFeature, + IMemoryPoolFeature, + IConnectionLifetimeFeature { - private object? _currentIConnectionIdFeature; - private object? _currentIConnectionTransportFeature; - private object? _currentIConnectionItemsFeature; - private object? _currentIMemoryPoolFeature; - private object? _currentIConnectionLifetimeFeature; + // Implemented features + internal protected IConnectionIdFeature? _currentIConnectionIdFeature; + internal protected IConnectionItemsFeature? _currentIConnectionItemsFeature; + internal protected IMemoryPoolFeature? _currentIMemoryPoolFeature; + internal protected IConnectionLifetimeFeature? _currentIConnectionLifetimeFeature; + + // Other reserved feature slots + internal protected IConnectionTransportFeature? _currentIConnectionTransportFeature; private int _featureRevision; @@ -27,11 +35,11 @@ internal partial class TransportMultiplexedConnection : IFeatureCollection private void FastReset() { _currentIConnectionIdFeature = this; - _currentIConnectionTransportFeature = this; _currentIConnectionItemsFeature = this; _currentIMemoryPoolFeature = this; _currentIConnectionLifetimeFeature = this; + _currentIConnectionTransportFeature = null; } // Internal for testing @@ -137,23 +145,23 @@ private void ExtraFeatureSet(Type key, object? value) if (key == typeof(IConnectionIdFeature)) { - _currentIConnectionIdFeature = value; + _currentIConnectionIdFeature = (IConnectionIdFeature?)value; } else if (key == typeof(IConnectionTransportFeature)) { - _currentIConnectionTransportFeature = value; + _currentIConnectionTransportFeature = (IConnectionTransportFeature?)value; } else if (key == typeof(IConnectionItemsFeature)) { - _currentIConnectionItemsFeature = value; + _currentIConnectionItemsFeature = (IConnectionItemsFeature?)value; } else if (key == typeof(IMemoryPoolFeature)) { - _currentIMemoryPoolFeature = value; + _currentIMemoryPoolFeature = (IMemoryPoolFeature?)value; } else if (key == typeof(IConnectionLifetimeFeature)) { - _currentIConnectionLifetimeFeature = value; + _currentIConnectionLifetimeFeature = (IConnectionLifetimeFeature?)value; } else { @@ -164,26 +172,30 @@ private void ExtraFeatureSet(Type key, object? value) TFeature? IFeatureCollection.Get() where TFeature : default { + // Using Unsafe.As for the cast due to https://github.com/dotnet/runtime/issues/49614 + // The type of TFeature is confirmed by the typeof() check and the As cast only accepts + // that type; however the Jit does not eliminate a regular cast in a shared generic. + TFeature? feature = default; if (typeof(TFeature) == typeof(IConnectionIdFeature)) { - feature = (TFeature?)_currentIConnectionIdFeature; + feature = Unsafe.As(ref _currentIConnectionIdFeature); } else if (typeof(TFeature) == typeof(IConnectionTransportFeature)) { - feature = (TFeature?)_currentIConnectionTransportFeature; + feature = Unsafe.As(ref _currentIConnectionTransportFeature); } else if (typeof(TFeature) == typeof(IConnectionItemsFeature)) { - feature = (TFeature?)_currentIConnectionItemsFeature; + feature = Unsafe.As(ref _currentIConnectionItemsFeature); } else if (typeof(TFeature) == typeof(IMemoryPoolFeature)) { - feature = (TFeature?)_currentIMemoryPoolFeature; + feature = Unsafe.As(ref _currentIMemoryPoolFeature); } else if (typeof(TFeature) == typeof(IConnectionLifetimeFeature)) { - feature = (TFeature?)_currentIConnectionLifetimeFeature; + feature = Unsafe.As(ref _currentIConnectionLifetimeFeature); } else if (MaybeExtra != null) { @@ -195,26 +207,30 @@ private void ExtraFeatureSet(Type key, object? value) void IFeatureCollection.Set(TFeature? feature) where TFeature : default { + // Using Unsafe.As for the cast due to https://github.com/dotnet/runtime/issues/49614 + // The type of TFeature is confirmed by the typeof() check and the As cast only accepts + // that type; however the Jit does not eliminate a regular cast in a shared generic. + _featureRevision++; if (typeof(TFeature) == typeof(IConnectionIdFeature)) { - _currentIConnectionIdFeature = feature; + _currentIConnectionIdFeature = Unsafe.As(ref feature); } else if (typeof(TFeature) == typeof(IConnectionTransportFeature)) { - _currentIConnectionTransportFeature = feature; + _currentIConnectionTransportFeature = Unsafe.As(ref feature); } else if (typeof(TFeature) == typeof(IConnectionItemsFeature)) { - _currentIConnectionItemsFeature = feature; + _currentIConnectionItemsFeature = Unsafe.As(ref feature); } else if (typeof(TFeature) == typeof(IMemoryPoolFeature)) { - _currentIMemoryPoolFeature = feature; + _currentIMemoryPoolFeature = Unsafe.As(ref feature); } else if (typeof(TFeature) == typeof(IConnectionLifetimeFeature)) { - _currentIConnectionLifetimeFeature = feature; + _currentIConnectionLifetimeFeature = Unsafe.As(ref feature); } else { diff --git a/src/Servers/Kestrel/tools/CodeGenerator/FeatureCollectionGenerator.cs b/src/Servers/Kestrel/tools/CodeGenerator/FeatureCollectionGenerator.cs index 3f75cd4e21e6..f8f4ad91b64a 100644 --- a/src/Servers/Kestrel/tools/CodeGenerator/FeatureCollectionGenerator.cs +++ b/src/Servers/Kestrel/tools/CodeGenerator/FeatureCollectionGenerator.cs @@ -25,15 +25,21 @@ public static string GenerateFile(string namespaceName, string className, string using System; using System.Collections; using System.Collections.Generic; +using System.Runtime.CompilerServices; {extraUsings} #nullable enable namespace {namespaceName} {{ - internal partial class {className} : IFeatureCollection - {{{Each(features, feature => $@" - private object? _current{feature.Name};")} + internal partial class {className} : IFeatureCollection{Each(implementedFeatures, feature => $@", + {new string(' ', className.Length)}{feature}")} + {{ + // Implemented features{Each(implementedFeatures, feature => $@" + internal protected {feature}? _current{feature};")}{(allFeatures.Where(f => !implementedFeatures.Contains(f)).FirstOrDefault() is not null ? @" + + // Other reserved feature slots" : "")}{Each(allFeatures.Where(f => !implementedFeatures.Contains(f)), feature => $@" + internal protected {feature}? _current{feature};")} private int _featureRevision; @@ -133,7 +139,7 @@ private void ExtraFeatureSet(Type key, object? value) {Each(features, feature => $@" {(feature.Index != 0 ? "else " : "")}if (key == typeof({feature.Name})) {{ - _current{feature.Name} = value; + _current{feature.Name} = ({feature.Name}?)value; }}")} else {{ @@ -144,10 +150,14 @@ private void ExtraFeatureSet(Type key, object? value) TFeature? IFeatureCollection.Get() where TFeature : default {{ + // Using Unsafe.As for the cast due to https://github.com/dotnet/runtime/issues/49614 + // The type of TFeature is confirmed by the typeof() check and the As cast only accepts + // that type; however the Jit does not eliminate a regular cast in a shared generic. + TFeature? feature = default;{Each(features, feature => $@" {(feature.Index != 0 ? "else " : "")}if (typeof(TFeature) == typeof({feature.Name})) {{ - feature = (TFeature?)_current{feature.Name}; + feature = Unsafe.As<{feature.Name}?, TFeature?>(ref _current{feature.Name}); }}")} else if (MaybeExtra != null) {{ @@ -164,10 +174,14 @@ private void ExtraFeatureSet(Type key, object? value) void IFeatureCollection.Set(TFeature? feature) where TFeature : default {{ + // Using Unsafe.As for the cast due to https://github.com/dotnet/runtime/issues/49614 + // The type of TFeature is confirmed by the typeof() check and the As cast only accepts + // that type; however the Jit does not eliminate a regular cast in a shared generic. + _featureRevision++;{Each(features, feature => $@" {(feature.Index != 0 ? "else " : "")}if (typeof(TFeature) == typeof({feature.Name})) {{ - _current{feature.Name} = feature; + _current{feature.Name} = Unsafe.As(ref feature); }}")} else {{ diff --git a/src/Servers/Kestrel/tools/CodeGenerator/HttpProtocolFeatureCollection.cs b/src/Servers/Kestrel/tools/CodeGenerator/HttpProtocolFeatureCollection.cs index 7c8dc1bce725..fb793e76b8f5 100644 --- a/src/Servers/Kestrel/tools/CodeGenerator/HttpProtocolFeatureCollection.cs +++ b/src/Servers/Kestrel/tools/CodeGenerator/HttpProtocolFeatureCollection.cs @@ -12,46 +12,50 @@ public static string GenerateFile() var alwaysFeatures = new[] { "IHttpRequestFeature", - "IHttpRequestBodyDetectionFeature", "IHttpResponseFeature", "IHttpResponseBodyFeature", - "IRequestBodyPipeFeature", - "IHttpRequestIdentifierFeature", - "IServiceProvidersFeature", - "IHttpRequestLifetimeFeature", - "IHttpConnectionFeature", "IRouteValuesFeature", - "IEndpointFeature" + "IEndpointFeature", + "IServiceProvidersFeature" }; var commonFeatures = new[] { - "IHttpAuthenticationFeature", - "IHttpRequestTrailersFeature", + "IItemsFeature", "IQueryFeature", + "IRequestBodyPipeFeature", "IFormFeature", + "IHttpAuthenticationFeature", + "IHttpRequestIdentifierFeature", }; var sometimesFeatures = new[] { - "IHttpUpgradeFeature", - "IHttp2StreamIdFeature", - "IHttpResponseTrailersFeature", + "IHttpConnectionFeature", + "ISessionFeature", "IResponseCookiesFeature", - "IItemsFeature", + "IHttpRequestTrailersFeature", + "IHttpResponseTrailersFeature", "ITlsConnectionFeature", - "IHttpWebSocketFeature", - "ISessionFeature", + "IHttpUpgradeFeature", + "IHttpWebSocketFeature" + }; + var maybeFeatures = new[] + { + "IHttp2StreamIdFeature", + "IHttpRequestLifetimeFeature", "IHttpMaxRequestBodySizeFeature", "IHttpMinRequestBodyDataRateFeature", "IHttpMinResponseDataRateFeature", "IHttpBodyControlFeature", + "IHttpRequestBodyDetectionFeature", "IHttpResetFeature" }; var allFeatures = alwaysFeatures .Concat(commonFeatures) .Concat(sometimesFeatures) + .Concat(maybeFeatures) .ToArray(); // NOTE: This list MUST always match the set of feature interfaces implemented by HttpProtocol. @@ -59,22 +63,21 @@ public static string GenerateFile() var implementedFeatures = new[] { "IHttpRequestFeature", - "IHttpRequestBodyDetectionFeature", "IHttpResponseFeature", "IHttpResponseBodyFeature", - "IRequestBodyPipeFeature", - "IHttpUpgradeFeature", + "IRouteValuesFeature", + "IEndpointFeature", "IHttpRequestIdentifierFeature", - "IHttpRequestLifetimeFeature", "IHttpRequestTrailersFeature", + "IHttpUpgradeFeature", + "IRequestBodyPipeFeature", "IHttpConnectionFeature", - "IHttpMaxRequestBodySizeFeature", - "IHttpMinRequestBodyDataRateFeature", + "IHttpRequestLifetimeFeature", "IHttpBodyControlFeature", - "IRouteValuesFeature", - "IEndpointFeature" + "IHttpMaxRequestBodySizeFeature", + "IHttpRequestBodyDetectionFeature", }; - + var usings = $@" using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http.Features.Authentication; diff --git a/src/Servers/Kestrel/tools/CodeGenerator/TransportConnectionFeatureCollection.cs b/src/Servers/Kestrel/tools/CodeGenerator/TransportConnectionFeatureCollection.cs index fdc759e7f31f..60d00f404a2b 100644 --- a/src/Servers/Kestrel/tools/CodeGenerator/TransportConnectionFeatureCollection.cs +++ b/src/Servers/Kestrel/tools/CodeGenerator/TransportConnectionFeatureCollection.cs @@ -1,6 +1,8 @@ // 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 CodeGenerator { public class TransportConnectionFeatureCollection diff --git a/src/Servers/Kestrel/tools/CodeGenerator/TransportMultiplexedConnectionFeatureCollection.cs b/src/Servers/Kestrel/tools/CodeGenerator/TransportMultiplexedConnectionFeatureCollection.cs index 8c0ad9d3ff99..b9296cac82bd 100644 --- a/src/Servers/Kestrel/tools/CodeGenerator/TransportMultiplexedConnectionFeatureCollection.cs +++ b/src/Servers/Kestrel/tools/CodeGenerator/TransportMultiplexedConnectionFeatureCollection.cs @@ -1,6 +1,8 @@ // 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 CodeGenerator { public class TransportMultiplexedConnectionFeatureCollection @@ -9,7 +11,7 @@ public static string GenerateFile() { // NOTE: This list MUST always match the set of feature interfaces implemented by TransportConnectionBase. // See also: shared/TransportConnectionBase.FeatureCollection.cs - var features = new[] + var allFeatures = new[] { "IConnectionIdFeature", "IConnectionTransportFeature", @@ -17,6 +19,13 @@ public static string GenerateFile() "IMemoryPoolFeature", "IConnectionLifetimeFeature" }; + var implementedFeatures = new[] + { + "IConnectionIdFeature", + "IConnectionItemsFeature", + "IMemoryPoolFeature", + "IConnectionLifetimeFeature" + }; var usings = $@" using Microsoft.AspNetCore.Connections.Features; @@ -25,8 +34,8 @@ public static string GenerateFile() return FeatureCollectionGenerator.GenerateFile( namespaceName: "Microsoft.AspNetCore.Connections", className: "TransportMultiplexedConnection", - allFeatures: features, - implementedFeatures: features, + allFeatures: allFeatures, + implementedFeatures: implementedFeatures, extraUsings: usings, fallbackFeatures: null); }