From c683316253829a575629d7b2047fc37dedcd85c9 Mon Sep 17 00:00:00 2001 From: Stephen Halter Date: Mon, 14 May 2018 17:20:48 -0700 Subject: [PATCH] Generate repetitive TransportConnection FeatureCollection code (#2548) --- .../Http/HttpProtocol.FeatureCollection.cs | 60 +--- .../Internal/Http/HttpProtocol.Generated.cs | 294 +++++++++++------- .../TransportConnection.FeatureCollection.cs | 91 ++++++ ...es.cs => TransportConnection.Generated.cs} | 219 +++++-------- .../Internal/TransportConnection.cs | 5 +- .../GeneratedCodeTests.cs | 8 +- test/Kestrel.Tests/Kestrel.Tests.csproj | 4 + ...rel.Transport.Libuv.FunctionalTests.csproj | 4 - ...l.Transport.Sockets.FunctionalTests.csproj | 4 - tools/CodeGenerator/CodeGenerator.csproj | 4 +- .../FeatureCollectionGenerator.cs | 196 ++++++++++++ .../HttpProtocolFeatureCollection.cs | 139 +-------- tools/CodeGenerator/KnownHeaders.cs | 7 - tools/CodeGenerator/Program.cs | 22 +- .../TransportConnectionFeatureCollection.cs | 38 +++ 15 files changed, 627 insertions(+), 468 deletions(-) create mode 100644 src/Kestrel.Transport.Abstractions/Internal/TransportConnection.FeatureCollection.cs rename src/Kestrel.Transport.Abstractions/Internal/{TransportConnection.Features.cs => TransportConnection.Generated.cs} (65%) rename test/{Kestrel.FunctionalTests => Kestrel.Tests}/GeneratedCodeTests.cs (77%) create mode 100644 tools/CodeGenerator/FeatureCollectionGenerator.cs create mode 100644 tools/CodeGenerator/TransportConnectionFeatureCollection.cs diff --git a/src/Kestrel.Core/Internal/Http/HttpProtocol.FeatureCollection.cs b/src/Kestrel.Core/Internal/Http/HttpProtocol.FeatureCollection.cs index 3c53523f8a..5b9cb5c5d6 100644 --- a/src/Kestrel.Core/Internal/Http/HttpProtocol.FeatureCollection.cs +++ b/src/Kestrel.Core/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; -using System.Collections.Generic; using System.IO; using System.Net; using System.Threading; @@ -16,8 +14,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure; namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http { - public partial class HttpProtocol : IFeatureCollection, - IHttpRequestFeature, + public partial class HttpProtocol : IHttpRequestFeature, IHttpResponseFeature, IHttpConnectionFeature, IHttpRequestLifetimeFeature, @@ -29,53 +26,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http { // 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. - // See also: tools/Microsoft.AspNetCore.Server.Kestrel.GeneratedCode/HttpProtocolFeatureCollection.cs - - private int _featureRevision; - - private List> MaybeExtra; - - public void ResetFeatureCollection() - { - FastReset(); - MaybeExtra?.Clear(); - _featureRevision++; - } - - private object ExtraFeatureGet(Type key) - { - if (MaybeExtra == null) - { - return null; - } - for (var i = 0; i < MaybeExtra.Count; i++) - { - var kv = MaybeExtra[i]; - if (kv.Key == key) - { - return kv.Value; - } - } - return null; - } - - private void ExtraFeatureSet(Type key, object value) - { - if (MaybeExtra == null) - { - MaybeExtra = new List>(2); - } - - for (var i = 0; i < MaybeExtra.Count; i++) - { - if (MaybeExtra[i].Key == key) - { - MaybeExtra[i] = new KeyValuePair(key, value); - return; - } - } - MaybeExtra.Add(new KeyValuePair(key, value)); - } + // See also: tools/CodeGenerator/HttpProtocolFeatureCollection.cs string IHttpRequestFeature.Protocol { @@ -175,9 +126,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http bool IHttpResponseFeature.HasStarted => HasResponseStarted; - bool IFeatureCollection.IsReadOnly => false; - - int IFeatureCollection.Revision => _featureRevision; IPAddress IHttpConnectionFeature.RemoteIpAddress { @@ -277,10 +225,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http OnCompleted(callback, state); } - IEnumerator> IEnumerable>.GetEnumerator() => FastEnumerable().GetEnumerator(); - - IEnumerator IEnumerable.GetEnumerator() => FastEnumerable().GetEnumerator(); - void IHttpRequestLifetimeFeature.Abort() { Abort(new ConnectionAbortedException()); diff --git a/src/Kestrel.Core/Internal/Http/HttpProtocol.Generated.cs b/src/Kestrel.Core/Internal/Http/HttpProtocol.Generated.cs index 3bac0a0778..03ec02cc8e 100644 --- a/src/Kestrel.Core/Internal/Http/HttpProtocol.Generated.cs +++ b/src/Kestrel.Core/Internal/Http/HttpProtocol.Generated.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections; using System.Collections.Generic; using Microsoft.AspNetCore.Http.Features; @@ -10,7 +11,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Core.Features; namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http { - public partial class HttpProtocol + public partial class HttpProtocol : IFeatureCollection { private static readonly Type IHttpRequestFeatureType = typeof(IHttpRequestFeature); private static readonly Type IHttpResponseFeatureType = typeof(IHttpResponseFeature); @@ -56,6 +57,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http private object _currentIHttpBodyControlFeature; private object _currentIHttpSendFileFeature; + private int _featureRevision; + + private List> MaybeExtra; + private void FastReset() { _currentIHttpRequestFeature = this; @@ -67,7 +72,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http _currentIHttpMinRequestBodyDataRateFeature = this; _currentIHttpMinResponseDataRateFeature = this; _currentIHttpBodyControlFeature = this; - + _currentIServiceProvidersFeature = null; _currentIHttpAuthenticationFeature = null; _currentIQueryFeature = null; @@ -82,6 +87,53 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http _currentIHttpSendFileFeature = null; } + // Internal for testing + internal void ResetFeatureCollection() + { + FastReset(); + MaybeExtra?.Clear(); + _featureRevision++; + } + + private object ExtraFeatureGet(Type key) + { + if (MaybeExtra == null) + { + return null; + } + for (var i = 0; i < MaybeExtra.Count; i++) + { + var kv = MaybeExtra[i]; + if (kv.Key == key) + { + return kv.Value; + } + } + return null; + } + + private void ExtraFeatureSet(Type key, object value) + { + if (MaybeExtra == null) + { + MaybeExtra = new List>(2); + } + + for (var i = 0; i < MaybeExtra.Count; i++) + { + if (MaybeExtra[i].Key == key) + { + MaybeExtra[i] = new KeyValuePair(key, value); + return; + } + } + MaybeExtra.Add(new KeyValuePair(key, value)); + } + + bool IFeatureCollection.IsReadOnly => false; + + int IFeatureCollection.Revision => _featureRevision; + object IFeatureCollection.this[Type key] { get @@ -182,7 +234,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http set { _featureRevision++; - + if (key == IHttpRequestFeatureType) { _currentIHttpRequestFeature = value; @@ -274,99 +326,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http } } - void IFeatureCollection.Set(TFeature feature) - { - _featureRevision++; - if (typeof(TFeature) == typeof(IHttpRequestFeature)) - { - _currentIHttpRequestFeature = feature; - } - else if (typeof(TFeature) == typeof(IHttpResponseFeature)) - { - _currentIHttpResponseFeature = feature; - } - else if (typeof(TFeature) == typeof(IHttpRequestIdentifierFeature)) - { - _currentIHttpRequestIdentifierFeature = feature; - } - else if (typeof(TFeature) == typeof(IServiceProvidersFeature)) - { - _currentIServiceProvidersFeature = feature; - } - else if (typeof(TFeature) == typeof(IHttpRequestLifetimeFeature)) - { - _currentIHttpRequestLifetimeFeature = feature; - } - else if (typeof(TFeature) == typeof(IHttpConnectionFeature)) - { - _currentIHttpConnectionFeature = feature; - } - else if (typeof(TFeature) == typeof(IHttpAuthenticationFeature)) - { - _currentIHttpAuthenticationFeature = feature; - } - else if (typeof(TFeature) == typeof(IQueryFeature)) - { - _currentIQueryFeature = feature; - } - else if (typeof(TFeature) == typeof(IFormFeature)) - { - _currentIFormFeature = feature; - } - else if (typeof(TFeature) == typeof(IHttpUpgradeFeature)) - { - _currentIHttpUpgradeFeature = feature; - } - else if (typeof(TFeature) == typeof(IHttp2StreamIdFeature)) - { - _currentIHttp2StreamIdFeature = feature; - } - else if (typeof(TFeature) == typeof(IResponseCookiesFeature)) - { - _currentIResponseCookiesFeature = feature; - } - else if (typeof(TFeature) == typeof(IItemsFeature)) - { - _currentIItemsFeature = feature; - } - else if (typeof(TFeature) == typeof(ITlsConnectionFeature)) - { - _currentITlsConnectionFeature = feature; - } - else if (typeof(TFeature) == typeof(IHttpWebSocketFeature)) - { - _currentIHttpWebSocketFeature = feature; - } - else if (typeof(TFeature) == typeof(ISessionFeature)) - { - _currentISessionFeature = feature; - } - else if (typeof(TFeature) == typeof(IHttpMaxRequestBodySizeFeature)) - { - _currentIHttpMaxRequestBodySizeFeature = feature; - } - else if (typeof(TFeature) == typeof(IHttpMinRequestBodyDataRateFeature)) - { - _currentIHttpMinRequestBodyDataRateFeature = feature; - } - else if (typeof(TFeature) == typeof(IHttpMinResponseDataRateFeature)) - { - _currentIHttpMinResponseDataRateFeature = feature; - } - else if (typeof(TFeature) == typeof(IHttpBodyControlFeature)) - { - _currentIHttpBodyControlFeature = feature; - } - else if (typeof(TFeature) == typeof(IHttpSendFileFeature)) - { - _currentIHttpSendFileFeature = feature; - } - else - { - ExtraFeatureSet(typeof(TFeature), feature); - } - } - TFeature IFeatureCollection.Get() { TFeature feature = default; @@ -458,7 +417,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http { feature = (TFeature)(ExtraFeatureGet(typeof(TFeature))); } - + if (feature == null) { feature = ConnectionFeatures.Get(); @@ -467,100 +426,197 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http return feature; } + void IFeatureCollection.Set(TFeature feature) + { + _featureRevision++; + if (typeof(TFeature) == typeof(IHttpRequestFeature)) + { + _currentIHttpRequestFeature = feature; + } + else if (typeof(TFeature) == typeof(IHttpResponseFeature)) + { + _currentIHttpResponseFeature = feature; + } + else if (typeof(TFeature) == typeof(IHttpRequestIdentifierFeature)) + { + _currentIHttpRequestIdentifierFeature = feature; + } + else if (typeof(TFeature) == typeof(IServiceProvidersFeature)) + { + _currentIServiceProvidersFeature = feature; + } + else if (typeof(TFeature) == typeof(IHttpRequestLifetimeFeature)) + { + _currentIHttpRequestLifetimeFeature = feature; + } + else if (typeof(TFeature) == typeof(IHttpConnectionFeature)) + { + _currentIHttpConnectionFeature = feature; + } + else if (typeof(TFeature) == typeof(IHttpAuthenticationFeature)) + { + _currentIHttpAuthenticationFeature = feature; + } + else if (typeof(TFeature) == typeof(IQueryFeature)) + { + _currentIQueryFeature = feature; + } + else if (typeof(TFeature) == typeof(IFormFeature)) + { + _currentIFormFeature = feature; + } + else if (typeof(TFeature) == typeof(IHttpUpgradeFeature)) + { + _currentIHttpUpgradeFeature = feature; + } + else if (typeof(TFeature) == typeof(IHttp2StreamIdFeature)) + { + _currentIHttp2StreamIdFeature = feature; + } + else if (typeof(TFeature) == typeof(IResponseCookiesFeature)) + { + _currentIResponseCookiesFeature = feature; + } + else if (typeof(TFeature) == typeof(IItemsFeature)) + { + _currentIItemsFeature = feature; + } + else if (typeof(TFeature) == typeof(ITlsConnectionFeature)) + { + _currentITlsConnectionFeature = feature; + } + else if (typeof(TFeature) == typeof(IHttpWebSocketFeature)) + { + _currentIHttpWebSocketFeature = feature; + } + else if (typeof(TFeature) == typeof(ISessionFeature)) + { + _currentISessionFeature = feature; + } + else if (typeof(TFeature) == typeof(IHttpMaxRequestBodySizeFeature)) + { + _currentIHttpMaxRequestBodySizeFeature = feature; + } + else if (typeof(TFeature) == typeof(IHttpMinRequestBodyDataRateFeature)) + { + _currentIHttpMinRequestBodyDataRateFeature = feature; + } + else if (typeof(TFeature) == typeof(IHttpMinResponseDataRateFeature)) + { + _currentIHttpMinResponseDataRateFeature = feature; + } + else if (typeof(TFeature) == typeof(IHttpBodyControlFeature)) + { + _currentIHttpBodyControlFeature = feature; + } + else if (typeof(TFeature) == typeof(IHttpSendFileFeature)) + { + _currentIHttpSendFileFeature = feature; + } + else + { + ExtraFeatureSet(typeof(TFeature), feature); + } + } + private IEnumerable> FastEnumerable() { if (_currentIHttpRequestFeature != null) { - yield return new KeyValuePair(IHttpRequestFeatureType, _currentIHttpRequestFeature as IHttpRequestFeature); + yield return new KeyValuePair(IHttpRequestFeatureType, _currentIHttpRequestFeature); } if (_currentIHttpResponseFeature != null) { - yield return new KeyValuePair(IHttpResponseFeatureType, _currentIHttpResponseFeature as IHttpResponseFeature); + yield return new KeyValuePair(IHttpResponseFeatureType, _currentIHttpResponseFeature); } if (_currentIHttpRequestIdentifierFeature != null) { - yield return new KeyValuePair(IHttpRequestIdentifierFeatureType, _currentIHttpRequestIdentifierFeature as IHttpRequestIdentifierFeature); + yield return new KeyValuePair(IHttpRequestIdentifierFeatureType, _currentIHttpRequestIdentifierFeature); } if (_currentIServiceProvidersFeature != null) { - yield return new KeyValuePair(IServiceProvidersFeatureType, _currentIServiceProvidersFeature as IServiceProvidersFeature); + yield return new KeyValuePair(IServiceProvidersFeatureType, _currentIServiceProvidersFeature); } if (_currentIHttpRequestLifetimeFeature != null) { - yield return new KeyValuePair(IHttpRequestLifetimeFeatureType, _currentIHttpRequestLifetimeFeature as IHttpRequestLifetimeFeature); + yield return new KeyValuePair(IHttpRequestLifetimeFeatureType, _currentIHttpRequestLifetimeFeature); } if (_currentIHttpConnectionFeature != null) { - yield return new KeyValuePair(IHttpConnectionFeatureType, _currentIHttpConnectionFeature as IHttpConnectionFeature); + yield return new KeyValuePair(IHttpConnectionFeatureType, _currentIHttpConnectionFeature); } if (_currentIHttpAuthenticationFeature != null) { - yield return new KeyValuePair(IHttpAuthenticationFeatureType, _currentIHttpAuthenticationFeature as IHttpAuthenticationFeature); + yield return new KeyValuePair(IHttpAuthenticationFeatureType, _currentIHttpAuthenticationFeature); } if (_currentIQueryFeature != null) { - yield return new KeyValuePair(IQueryFeatureType, _currentIQueryFeature as IQueryFeature); + yield return new KeyValuePair(IQueryFeatureType, _currentIQueryFeature); } if (_currentIFormFeature != null) { - yield return new KeyValuePair(IFormFeatureType, _currentIFormFeature as IFormFeature); + yield return new KeyValuePair(IFormFeatureType, _currentIFormFeature); } if (_currentIHttpUpgradeFeature != null) { - yield return new KeyValuePair(IHttpUpgradeFeatureType, _currentIHttpUpgradeFeature as IHttpUpgradeFeature); + yield return new KeyValuePair(IHttpUpgradeFeatureType, _currentIHttpUpgradeFeature); } if (_currentIHttp2StreamIdFeature != null) { - yield return new KeyValuePair(IHttp2StreamIdFeatureType, _currentIHttp2StreamIdFeature as IHttp2StreamIdFeature); + yield return new KeyValuePair(IHttp2StreamIdFeatureType, _currentIHttp2StreamIdFeature); } if (_currentIResponseCookiesFeature != null) { - yield return new KeyValuePair(IResponseCookiesFeatureType, _currentIResponseCookiesFeature as IResponseCookiesFeature); + yield return new KeyValuePair(IResponseCookiesFeatureType, _currentIResponseCookiesFeature); } if (_currentIItemsFeature != null) { - yield return new KeyValuePair(IItemsFeatureType, _currentIItemsFeature as IItemsFeature); + yield return new KeyValuePair(IItemsFeatureType, _currentIItemsFeature); } if (_currentITlsConnectionFeature != null) { - yield return new KeyValuePair(ITlsConnectionFeatureType, _currentITlsConnectionFeature as ITlsConnectionFeature); + yield return new KeyValuePair(ITlsConnectionFeatureType, _currentITlsConnectionFeature); } if (_currentIHttpWebSocketFeature != null) { - yield return new KeyValuePair(IHttpWebSocketFeatureType, _currentIHttpWebSocketFeature as IHttpWebSocketFeature); + yield return new KeyValuePair(IHttpWebSocketFeatureType, _currentIHttpWebSocketFeature); } if (_currentISessionFeature != null) { - yield return new KeyValuePair(ISessionFeatureType, _currentISessionFeature as ISessionFeature); + yield return new KeyValuePair(ISessionFeatureType, _currentISessionFeature); } if (_currentIHttpMaxRequestBodySizeFeature != null) { - yield return new KeyValuePair(IHttpMaxRequestBodySizeFeatureType, _currentIHttpMaxRequestBodySizeFeature as IHttpMaxRequestBodySizeFeature); + yield return new KeyValuePair(IHttpMaxRequestBodySizeFeatureType, _currentIHttpMaxRequestBodySizeFeature); } if (_currentIHttpMinRequestBodyDataRateFeature != null) { - yield return new KeyValuePair(IHttpMinRequestBodyDataRateFeatureType, _currentIHttpMinRequestBodyDataRateFeature as IHttpMinRequestBodyDataRateFeature); + yield return new KeyValuePair(IHttpMinRequestBodyDataRateFeatureType, _currentIHttpMinRequestBodyDataRateFeature); } if (_currentIHttpMinResponseDataRateFeature != null) { - yield return new KeyValuePair(IHttpMinResponseDataRateFeatureType, _currentIHttpMinResponseDataRateFeature as IHttpMinResponseDataRateFeature); + yield return new KeyValuePair(IHttpMinResponseDataRateFeatureType, _currentIHttpMinResponseDataRateFeature); } if (_currentIHttpBodyControlFeature != null) { - yield return new KeyValuePair(IHttpBodyControlFeatureType, _currentIHttpBodyControlFeature as IHttpBodyControlFeature); + yield return new KeyValuePair(IHttpBodyControlFeatureType, _currentIHttpBodyControlFeature); } if (_currentIHttpSendFileFeature != null) { - yield return new KeyValuePair(IHttpSendFileFeatureType, _currentIHttpSendFileFeature as IHttpSendFileFeature); + yield return new KeyValuePair(IHttpSendFileFeatureType, _currentIHttpSendFileFeature); } if (MaybeExtra != null) { - foreach(var item in MaybeExtra) + foreach (var item in MaybeExtra) { yield return item; } } } + + IEnumerator> IEnumerable>.GetEnumerator() => FastEnumerable().GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => FastEnumerable().GetEnumerator(); } } diff --git a/src/Kestrel.Transport.Abstractions/Internal/TransportConnection.FeatureCollection.cs b/src/Kestrel.Transport.Abstractions/Internal/TransportConnection.FeatureCollection.cs new file mode 100644 index 0000000000..49200bd66a --- /dev/null +++ b/src/Kestrel.Transport.Abstractions/Internal/TransportConnection.FeatureCollection.cs @@ -0,0 +1,91 @@ +// 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.Buffers; +using System.Collections.Generic; +using System.IO.Pipelines; +using System.Net; +using System.Threading; +using Microsoft.AspNetCore.Connections.Features; +using Microsoft.AspNetCore.Http.Features; + +namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal +{ + public partial class TransportConnection : IHttpConnectionFeature, + IConnectionIdFeature, + IConnectionTransportFeature, + IConnectionItemsFeature, + IMemoryPoolFeature, + IApplicationTransportFeature, + ITransportSchedulerFeature, + IConnectionLifetimeFeature, + IBytesWrittenFeature + { + // 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. + // See also: tools/CodeGenerator/TransportConnectionFeatureCollection.cs + + string IHttpConnectionFeature.ConnectionId + { + get => ConnectionId; + set => ConnectionId = value; + } + + IPAddress IHttpConnectionFeature.RemoteIpAddress + { + get => RemoteAddress; + set => RemoteAddress = value; + } + + IPAddress IHttpConnectionFeature.LocalIpAddress + { + get => LocalAddress; + set => LocalAddress = value; + } + + int IHttpConnectionFeature.RemotePort + { + get => RemotePort; + set => RemotePort = value; + } + + int IHttpConnectionFeature.LocalPort + { + get => LocalPort; + set => LocalPort = value; + } + + MemoryPool IMemoryPoolFeature.MemoryPool => MemoryPool; + + IDuplexPipe IConnectionTransportFeature.Transport + { + get => Transport; + set => Transport = value; + } + + IDuplexPipe IApplicationTransportFeature.Application + { + get => Application; + set => Application = value; + } + + IDictionary IConnectionItemsFeature.Items + { + get => Items; + set => Items = value; + } + + PipeScheduler ITransportSchedulerFeature.InputWriterScheduler => InputWriterScheduler; + PipeScheduler ITransportSchedulerFeature.OutputReaderScheduler => OutputReaderScheduler; + + CancellationToken IConnectionLifetimeFeature.ConnectionClosed + { + get => ConnectionClosed; + set => ConnectionClosed = value; + } + + void IConnectionLifetimeFeature.Abort() => Abort(); + + long IBytesWrittenFeature.TotalBytesWritten => TotalBytesWritten; + } +} diff --git a/src/Kestrel.Transport.Abstractions/Internal/TransportConnection.Features.cs b/src/Kestrel.Transport.Abstractions/Internal/TransportConnection.Generated.cs similarity index 65% rename from src/Kestrel.Transport.Abstractions/Internal/TransportConnection.Features.cs rename to src/Kestrel.Transport.Abstractions/Internal/TransportConnection.Generated.cs index 4b65762f30..3a91523b9f 100644 --- a/src/Kestrel.Transport.Abstractions/Internal/TransportConnection.Features.cs +++ b/src/Kestrel.Transport.Abstractions/Internal/TransportConnection.Generated.cs @@ -1,25 +1,16 @@ -using System; -using System.Buffers; +// 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.Collections; using System.Collections.Generic; -using System.IO.Pipelines; -using System.Net; -using System.Threading; + using Microsoft.AspNetCore.Connections.Features; using Microsoft.AspNetCore.Http.Features; namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal { - public partial class TransportConnection : IFeatureCollection, - IHttpConnectionFeature, - IConnectionIdFeature, - IConnectionTransportFeature, - IConnectionItemsFeature, - IMemoryPoolFeature, - IApplicationTransportFeature, - ITransportSchedulerFeature, - IConnectionLifetimeFeature, - IBytesWrittenFeature + public partial class TransportConnection : IFeatureCollection { private static readonly Type IHttpConnectionFeatureType = typeof(IHttpConnectionFeature); private static readonly Type IConnectionIdFeatureType = typeof(IConnectionIdFeature); @@ -45,6 +36,28 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal private List> MaybeExtra; + private void FastReset() + { + _currentIHttpConnectionFeature = this; + _currentIConnectionIdFeature = this; + _currentIConnectionTransportFeature = this; + _currentIConnectionItemsFeature = this; + _currentIMemoryPoolFeature = this; + _currentIApplicationTransportFeature = this; + _currentITransportSchedulerFeature = this; + _currentIConnectionLifetimeFeature = this; + _currentIBytesWrittenFeature = this; + + } + + // Internal for testing + internal void ResetFeatureCollection() + { + FastReset(); + MaybeExtra?.Clear(); + _featureRevision++; + } + private object ExtraFeatureGet(Type key) { if (MaybeExtra == null) @@ -84,125 +97,55 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal int IFeatureCollection.Revision => _featureRevision; - string IHttpConnectionFeature.ConnectionId - { - get => ConnectionId; - set => ConnectionId = value; - } - - IPAddress IHttpConnectionFeature.RemoteIpAddress - { - get => RemoteAddress; - set => RemoteAddress = value; - } - - IPAddress IHttpConnectionFeature.LocalIpAddress - { - get => LocalAddress; - set => LocalAddress = value; - } - - int IHttpConnectionFeature.RemotePort - { - get => RemotePort; - set => RemotePort = value; - } - - int IHttpConnectionFeature.LocalPort - { - get => LocalPort; - set => LocalPort = value; - } - - MemoryPool IMemoryPoolFeature.MemoryPool => MemoryPool; - - IDuplexPipe IConnectionTransportFeature.Transport - { - get => Transport; - set => Transport = value; - } - - IDuplexPipe IApplicationTransportFeature.Application - { - get => Application; - set => Application = value; - } - - IDictionary IConnectionItemsFeature.Items - { - get => Items; - set => Items = value; - } - - CancellationToken IConnectionLifetimeFeature.ConnectionClosed - { - get => ConnectionClosed; - set => ConnectionClosed = value; - } - - void IConnectionLifetimeFeature.Abort() => Abort(); - - long IBytesWrittenFeature.TotalBytesWritten => TotalBytesWritten; - - PipeScheduler ITransportSchedulerFeature.InputWriterScheduler => InputWriterScheduler; - PipeScheduler ITransportSchedulerFeature.OutputReaderScheduler => OutputReaderScheduler; - object IFeatureCollection.this[Type key] { get { + object feature = null; if (key == IHttpConnectionFeatureType) { - return _currentIHttpConnectionFeature; + feature = _currentIHttpConnectionFeature; } - - if (key == IConnectionIdFeatureType) + else if (key == IConnectionIdFeatureType) { - return _currentIConnectionIdFeature; + feature = _currentIConnectionIdFeature; } - - if (key == IConnectionTransportFeatureType) + else if (key == IConnectionTransportFeatureType) { - return _currentIConnectionTransportFeature; + feature = _currentIConnectionTransportFeature; } - - if (key == IConnectionItemsFeatureType) + else if (key == IConnectionItemsFeatureType) { - return _currentIConnectionItemsFeature; + feature = _currentIConnectionItemsFeature; } - - if (key == IMemoryPoolFeatureType) + else if (key == IMemoryPoolFeatureType) { - return _currentIMemoryPoolFeature; + feature = _currentIMemoryPoolFeature; } - - if (key == IApplicationTransportFeatureType) + else if (key == IApplicationTransportFeatureType) { - return _currentIApplicationTransportFeature; + feature = _currentIApplicationTransportFeature; } - - if (key == ITransportSchedulerFeatureType) + else if (key == ITransportSchedulerFeatureType) { - return _currentITransportSchedulerFeature; + feature = _currentITransportSchedulerFeature; } - - if (key == IConnectionLifetimeFeatureType) + else if (key == IConnectionLifetimeFeatureType) { - return _currentIConnectionLifetimeFeature; + feature = _currentIConnectionLifetimeFeature; } - - if (key == IBytesWrittenFeatureType) + else if (key == IBytesWrittenFeatureType) { - return _currentIBytesWrittenFeature; + feature = _currentIBytesWrittenFeature; } - - if (MaybeExtra != null) + else if (MaybeExtra != null) { - return ExtraFeatureGet(key); + feature = ExtraFeatureGet(key); } - return null; + return feature; } + set { _featureRevision++; @@ -252,142 +195,130 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal TFeature IFeatureCollection.Get() { + TFeature feature = default; if (typeof(TFeature) == typeof(IHttpConnectionFeature)) { - return (TFeature)_currentIHttpConnectionFeature; + feature = (TFeature)_currentIHttpConnectionFeature; } else if (typeof(TFeature) == typeof(IConnectionIdFeature)) { - return (TFeature)_currentIConnectionIdFeature; + feature = (TFeature)_currentIConnectionIdFeature; } else if (typeof(TFeature) == typeof(IConnectionTransportFeature)) { - return (TFeature)_currentIConnectionTransportFeature; + feature = (TFeature)_currentIConnectionTransportFeature; } else if (typeof(TFeature) == typeof(IConnectionItemsFeature)) { - return (TFeature)_currentIConnectionItemsFeature; + feature = (TFeature)_currentIConnectionItemsFeature; } else if (typeof(TFeature) == typeof(IMemoryPoolFeature)) { - return (TFeature)_currentIMemoryPoolFeature; + feature = (TFeature)_currentIMemoryPoolFeature; } else if (typeof(TFeature) == typeof(IApplicationTransportFeature)) { - return (TFeature)_currentIApplicationTransportFeature; + feature = (TFeature)_currentIApplicationTransportFeature; } else if (typeof(TFeature) == typeof(ITransportSchedulerFeature)) { - return (TFeature)_currentITransportSchedulerFeature; + feature = (TFeature)_currentITransportSchedulerFeature; } else if (typeof(TFeature) == typeof(IConnectionLifetimeFeature)) { - return (TFeature)_currentIConnectionLifetimeFeature; + feature = (TFeature)_currentIConnectionLifetimeFeature; } else if (typeof(TFeature) == typeof(IBytesWrittenFeature)) { - return (TFeature)_currentIBytesWrittenFeature; + feature = (TFeature)_currentIBytesWrittenFeature; } else if (MaybeExtra != null) { - return (TFeature)ExtraFeatureGet(typeof(TFeature)); + feature = (TFeature)(ExtraFeatureGet(typeof(TFeature))); } - return default; + return feature; } - void IFeatureCollection.Set(TFeature instance) + void IFeatureCollection.Set(TFeature feature) { _featureRevision++; - if (typeof(TFeature) == typeof(IHttpConnectionFeature)) { - _currentIHttpConnectionFeature = instance; + _currentIHttpConnectionFeature = feature; } else if (typeof(TFeature) == typeof(IConnectionIdFeature)) { - _currentIConnectionIdFeature = instance; + _currentIConnectionIdFeature = feature; } else if (typeof(TFeature) == typeof(IConnectionTransportFeature)) { - _currentIConnectionTransportFeature = instance; + _currentIConnectionTransportFeature = feature; } else if (typeof(TFeature) == typeof(IConnectionItemsFeature)) { - _currentIConnectionItemsFeature = instance; + _currentIConnectionItemsFeature = feature; } else if (typeof(TFeature) == typeof(IMemoryPoolFeature)) { - _currentIMemoryPoolFeature = instance; + _currentIMemoryPoolFeature = feature; } else if (typeof(TFeature) == typeof(IApplicationTransportFeature)) { - _currentIApplicationTransportFeature = instance; + _currentIApplicationTransportFeature = feature; } else if (typeof(TFeature) == typeof(ITransportSchedulerFeature)) { - _currentITransportSchedulerFeature = instance; + _currentITransportSchedulerFeature = feature; } else if (typeof(TFeature) == typeof(IConnectionLifetimeFeature)) { - _currentIConnectionLifetimeFeature = instance; + _currentIConnectionLifetimeFeature = feature; } else if (typeof(TFeature) == typeof(IBytesWrittenFeature)) { - _currentIBytesWrittenFeature = instance; + _currentIBytesWrittenFeature = feature; } else { - ExtraFeatureSet(typeof(TFeature), instance); + ExtraFeatureSet(typeof(TFeature), feature); } } - IEnumerator> IEnumerable>.GetEnumerator() => FastEnumerable().GetEnumerator(); - - IEnumerator IEnumerable.GetEnumerator() => FastEnumerable().GetEnumerator(); - private IEnumerable> FastEnumerable() { if (_currentIHttpConnectionFeature != null) { yield return new KeyValuePair(IHttpConnectionFeatureType, _currentIHttpConnectionFeature); } - if (_currentIConnectionIdFeature != null) { yield return new KeyValuePair(IConnectionIdFeatureType, _currentIConnectionIdFeature); } - if (_currentIConnectionTransportFeature != null) { yield return new KeyValuePair(IConnectionTransportFeatureType, _currentIConnectionTransportFeature); } - if (_currentIConnectionItemsFeature != null) { yield return new KeyValuePair(IConnectionItemsFeatureType, _currentIConnectionItemsFeature); } - if (_currentIMemoryPoolFeature != null) { yield return new KeyValuePair(IMemoryPoolFeatureType, _currentIMemoryPoolFeature); } - if (_currentIApplicationTransportFeature != null) { yield return new KeyValuePair(IApplicationTransportFeatureType, _currentIApplicationTransportFeature); } - if (_currentITransportSchedulerFeature != null) { yield return new KeyValuePair(ITransportSchedulerFeatureType, _currentITransportSchedulerFeature); } - if (_currentIConnectionLifetimeFeature != null) { yield return new KeyValuePair(IConnectionLifetimeFeatureType, _currentIConnectionLifetimeFeature); } - if (_currentIBytesWrittenFeature != null) { yield return new KeyValuePair(IBytesWrittenFeatureType, _currentIBytesWrittenFeature); @@ -401,5 +332,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal } } } + + IEnumerator> IEnumerable>.GetEnumerator() => FastEnumerable().GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => FastEnumerable().GetEnumerator(); } } diff --git a/src/Kestrel.Transport.Abstractions/Internal/TransportConnection.cs b/src/Kestrel.Transport.Abstractions/Internal/TransportConnection.cs index 5791033da6..089058a6b0 100644 --- a/src/Kestrel.Transport.Abstractions/Internal/TransportConnection.cs +++ b/src/Kestrel.Transport.Abstractions/Internal/TransportConnection.cs @@ -1,4 +1,7 @@ -using System.Buffers; +// 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.Buffers; using System.Collections.Generic; using System.IO.Pipelines; using System.Net; diff --git a/test/Kestrel.FunctionalTests/GeneratedCodeTests.cs b/test/Kestrel.Tests/GeneratedCodeTests.cs similarity index 77% rename from test/Kestrel.FunctionalTests/GeneratedCodeTests.cs rename to test/Kestrel.Tests/GeneratedCodeTests.cs index 3a4a55a8b3..666e5bee27 100644 --- a/test/Kestrel.FunctionalTests/GeneratedCodeTests.cs +++ b/test/Kestrel.Tests/GeneratedCodeTests.cs @@ -15,26 +15,31 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests const string httpHeadersGeneratedPath = "../../../../../src/Kestrel.Core/Internal/Http/HttpHeaders.Generated.cs"; const string httpProtocolGeneratedPath = "../../../../../src/Kestrel.Core/Internal/Http/HttpProtocol.Generated.cs"; const string httpUtilitiesGeneratedPath = "../../../../../src/Kestrel.Core/Internal/Infrastructure/HttpUtilities.Generated.cs"; + const string transportConnectionGeneratedPath = "../../../../../src/Kestrel.Transport.Abstractions/Internal/TransportConnection.Generated.cs"; var testHttpHeadersGeneratedPath = Path.GetTempFileName(); var testHttpProtocolGeneratedPath = Path.GetTempFileName(); var testHttpUtilitiesGeneratedPath = Path.GetTempFileName(); + var testTransportConnectionGeneratedPath = Path.GetTempFileName(); try { var currentHttpHeadersGenerated = File.ReadAllText(httpHeadersGeneratedPath); var currentHttpProtocolGenerated = File.ReadAllText(httpProtocolGeneratedPath); var currentHttpUtilitiesGenerated = File.ReadAllText(httpUtilitiesGeneratedPath); + var currentTransportConnectionGenerated = File.ReadAllText(transportConnectionGeneratedPath); - CodeGenerator.Program.Run(testHttpHeadersGeneratedPath, testHttpProtocolGeneratedPath, testHttpUtilitiesGeneratedPath); + CodeGenerator.Program.Run(testHttpHeadersGeneratedPath, testHttpProtocolGeneratedPath, testHttpUtilitiesGeneratedPath, testTransportConnectionGeneratedPath); var testHttpHeadersGenerated = File.ReadAllText(testHttpHeadersGeneratedPath); var testHttpProtocolGenerated = File.ReadAllText(testHttpProtocolGeneratedPath); var testHttpUtilitiesGenerated = File.ReadAllText(testHttpUtilitiesGeneratedPath); + var testTransportConnectionGenerated = File.ReadAllText(testTransportConnectionGeneratedPath); Assert.Equal(currentHttpHeadersGenerated, testHttpHeadersGenerated, ignoreLineEndingDifferences: true); Assert.Equal(currentHttpProtocolGenerated, testHttpProtocolGenerated, ignoreLineEndingDifferences: true); Assert.Equal(currentHttpUtilitiesGenerated, testHttpUtilitiesGenerated, ignoreLineEndingDifferences: true); + Assert.Equal(currentTransportConnectionGenerated, testTransportConnectionGenerated, ignoreLineEndingDifferences: true); } finally @@ -42,6 +47,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests File.Delete(testHttpHeadersGeneratedPath); File.Delete(testHttpProtocolGeneratedPath); File.Delete(testHttpUtilitiesGeneratedPath); + File.Delete(testTransportConnectionGeneratedPath); } } } diff --git a/test/Kestrel.Tests/Kestrel.Tests.csproj b/test/Kestrel.Tests/Kestrel.Tests.csproj index d383f39603..6ecbb0b1e2 100644 --- a/test/Kestrel.Tests/Kestrel.Tests.csproj +++ b/test/Kestrel.Tests/Kestrel.Tests.csproj @@ -11,6 +11,10 @@ + + + + diff --git a/test/Kestrel.Transport.Libuv.FunctionalTests/Kestrel.Transport.Libuv.FunctionalTests.csproj b/test/Kestrel.Transport.Libuv.FunctionalTests/Kestrel.Transport.Libuv.FunctionalTests.csproj index 4e48a8d666..0a37ba9fbd 100644 --- a/test/Kestrel.Transport.Libuv.FunctionalTests/Kestrel.Transport.Libuv.FunctionalTests.csproj +++ b/test/Kestrel.Transport.Libuv.FunctionalTests/Kestrel.Transport.Libuv.FunctionalTests.csproj @@ -16,10 +16,6 @@ - - - - diff --git a/test/Kestrel.Transport.Sockets.FunctionalTests/Kestrel.Transport.Sockets.FunctionalTests.csproj b/test/Kestrel.Transport.Sockets.FunctionalTests/Kestrel.Transport.Sockets.FunctionalTests.csproj index ca79f38600..dff7f9c2ea 100644 --- a/test/Kestrel.Transport.Sockets.FunctionalTests/Kestrel.Transport.Sockets.FunctionalTests.csproj +++ b/test/Kestrel.Transport.Sockets.FunctionalTests/Kestrel.Transport.Sockets.FunctionalTests.csproj @@ -15,10 +15,6 @@ - - - - diff --git a/tools/CodeGenerator/CodeGenerator.csproj b/tools/CodeGenerator/CodeGenerator.csproj index c85676e21d..6931838ffe 100644 --- a/tools/CodeGenerator/CodeGenerator.csproj +++ b/tools/CodeGenerator/CodeGenerator.csproj @@ -14,8 +14,8 @@ - $(MSBuildThisFileDirectory)..\..\src\Kestrel.Core - Internal/Http/HttpHeaders.Generated.cs Internal/Http/HttpProtocol.Generated.cs Internal/Infrastructure/HttpUtilities.Generated.cs + $(MSBuildThisFileDirectory)..\..\ + src/Kestrel.Core/Internal/Http/HttpHeaders.Generated.cs src/Kestrel.Core/Internal/Http/HttpProtocol.Generated.cs src/Kestrel.Core/Internal/Infrastructure/HttpUtilities.Generated.cs src/Kestrel.Transport.Abstractions/Internal/TransportConnection.Generated.cs diff --git a/tools/CodeGenerator/FeatureCollectionGenerator.cs b/tools/CodeGenerator/FeatureCollectionGenerator.cs new file mode 100644 index 0000000000..5fd7f978b6 --- /dev/null +++ b/tools/CodeGenerator/FeatureCollectionGenerator.cs @@ -0,0 +1,196 @@ +// 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.Collections.Generic; +using System.Linq; + +namespace CodeGenerator +{ + public static class FeatureCollectionGenerator + { + public static string GenerateFile(string namespaceName, string className, string[] allFeatures, string[] implementedFeatures, string extraUsings, string fallbackFeatures) + { + // NOTE: This list MUST always match the set of feature interfaces implemented by TransportConnection. + // See also: src/Kestrel/Http/TransportConnection.FeatureCollection.cs + var features = allFeatures.Select((type, index) => new KnownFeature + { + Name = type, + Index = index + }); + + return $@"// 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.Collections; +using System.Collections.Generic; +{extraUsings} + +namespace {namespaceName} +{{ + public partial class {className} : IFeatureCollection + {{{Each(features, feature => $@" + private static readonly Type {feature.Name}Type = typeof({feature.Name});")} +{Each(features, feature => $@" + private object _current{feature.Name};")} + + private int _featureRevision; + + private List> MaybeExtra; + + private void FastReset() + {{{Each(implementedFeatures, feature => $@" + _current{feature} = this;")} +{Each(allFeatures.Where(f => !implementedFeatures.Contains(f)), feature => $@" + _current{feature} = null;")} + }} + + // Internal for testing + internal void ResetFeatureCollection() + {{ + FastReset(); + MaybeExtra?.Clear(); + _featureRevision++; + }} + + private object ExtraFeatureGet(Type key) + {{ + if (MaybeExtra == null) + {{ + return null; + }} + for (var i = 0; i < MaybeExtra.Count; i++) + {{ + var kv = MaybeExtra[i]; + if (kv.Key == key) + {{ + return kv.Value; + }} + }} + return null; + }} + + private void ExtraFeatureSet(Type key, object value) + {{ + if (MaybeExtra == null) + {{ + MaybeExtra = new List>(2); + }} + + for (var i = 0; i < MaybeExtra.Count; i++) + {{ + if (MaybeExtra[i].Key == key) + {{ + MaybeExtra[i] = new KeyValuePair(key, value); + return; + }} + }} + MaybeExtra.Add(new KeyValuePair(key, value)); + }} + + bool IFeatureCollection.IsReadOnly => false; + + int IFeatureCollection.Revision => _featureRevision; + + object IFeatureCollection.this[Type key] + {{ + get + {{ + object feature = null;{Each(features, feature => $@" + {(feature.Index != 0 ? "else " : "")}if (key == {feature.Name}Type) + {{ + feature = _current{feature.Name}; + }}")} + else if (MaybeExtra != null) + {{ + feature = ExtraFeatureGet(key); + }} + + return feature{(string.IsNullOrEmpty(fallbackFeatures) ? "" : $" ?? {fallbackFeatures}[key]")}; + }} + + set + {{ + _featureRevision++; +{Each(features, feature => $@" + {(feature.Index != 0 ? "else " : "")}if (key == {feature.Name}Type) + {{ + _current{feature.Name} = value; + }}")} + else + {{ + ExtraFeatureSet(key, value); + }} + }} + }} + + TFeature IFeatureCollection.Get() + {{ + TFeature feature = default;{Each(features, 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))); + }}{(string.IsNullOrEmpty(fallbackFeatures) ? "" : $@" + + if (feature == null) + {{ + feature = {fallbackFeatures}.Get(); + }}")} + + return feature; + }} + + void IFeatureCollection.Set(TFeature feature) + {{ + _featureRevision++;{Each(features, feature => $@" + {(feature.Index != 0 ? "else " : "")}if (typeof(TFeature) == typeof({feature.Name})) + {{ + _current{feature.Name} = feature; + }}")} + else + {{ + ExtraFeatureSet(typeof(TFeature), feature); + }} + }} + + private IEnumerable> FastEnumerable() + {{{Each(features, feature => $@" + if (_current{feature.Name} != null) + {{ + yield return new KeyValuePair({feature.Name}Type, _current{feature.Name}); + }}")} + + if (MaybeExtra != null) + {{ + foreach (var item in MaybeExtra) + {{ + yield return item; + }} + }} + }} + + IEnumerator> IEnumerable>.GetEnumerator() => FastEnumerable().GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => FastEnumerable().GetEnumerator(); + }} +}} +"; + } + + static string Each(IEnumerable values, Func formatter) + { + return values.Any() ? values.Select(formatter).Aggregate((a, b) => a + b) : ""; + } + + private class KnownFeature + { + public string Name; + public int Index; + } + } +} diff --git a/tools/CodeGenerator/HttpProtocolFeatureCollection.cs b/tools/CodeGenerator/HttpProtocolFeatureCollection.cs index db6508080e..6102576832 100644 --- a/tools/CodeGenerator/HttpProtocolFeatureCollection.cs +++ b/tools/CodeGenerator/HttpProtocolFeatureCollection.cs @@ -1,28 +1,13 @@ // 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.Collections.Generic; using System.Linq; namespace CodeGenerator { - // This project can output the Class library as a NuGet Package. - // To enable this option, right-click on the project and select the Properties menu item. In the Build tab select "Produce outputs on build". public class HttpProtocolFeatureCollection { - static string Each(IEnumerable values, Func formatter) - { - 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 GenerateFile() { var alwaysFeatures = new[] { @@ -65,14 +50,10 @@ namespace CodeGenerator .Concat(commonFeatures) .Concat(sometimesFeatures) .Concat(rareFeatures) - .Select((type, index) => new KnownFeature - { - Name = type, - Index = index - }); + .ToArray(); // 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.Core/Internal/Http/HttpProtocol.FeatureCollection.cs var implementedFeatures = new[] { "IHttpRequestFeature", @@ -86,114 +67,18 @@ namespace CodeGenerator "IHttpBodyControlFeature", }; - return $@"// 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.Collections.Generic; - + var usings = $@" using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http.Features.Authentication; -using Microsoft.AspNetCore.Server.Kestrel.Core.Features; +using Microsoft.AspNetCore.Server.Kestrel.Core.Features;"; -namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http -{{ - public partial class {className} - {{{Each(allFeatures, feature => $@" - private static readonly Type {feature.Name}Type = typeof({feature.Name});")} -{Each(allFeatures, feature => $@" - private object _current{feature.Name};")} - - private void FastReset() - {{{Each(implementedFeatures, feature => $@" - _current{feature} = this;")} - {Each(allFeatures.Where(f => !implementedFeatures.Contains(f.Name)), feature => $@" - _current{feature.Name} = null;")} - }} - - object IFeatureCollection.this[Type key] - {{ - get - {{ - object feature = null;{Each(allFeatures, feature => $@" - {(feature.Index != 0 ? "else " : "")}if (key == {feature.Name}Type) - {{ - 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 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 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(); - }} - - return feature; - }} - - private IEnumerable> FastEnumerable() - {{{Each(allFeatures, feature => $@" - if (_current{feature.Name} != null) - {{ - yield return new KeyValuePair({feature.Name}Type, _current{feature.Name} as {feature.Name}); - }}")} - - if (MaybeExtra != null) - {{ - foreach(var item in MaybeExtra) - {{ - yield return item; - }} - }} - }} - }} -}} -"; + return FeatureCollectionGenerator.GenerateFile( + namespaceName: "Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http", + className: "HttpProtocol", + allFeatures: allFeatures, + implementedFeatures: implementedFeatures, + extraUsings: usings, + fallbackFeatures: "ConnectionFeatures"); } } } diff --git a/tools/CodeGenerator/KnownHeaders.cs b/tools/CodeGenerator/KnownHeaders.cs index badf9087a7..12de0493fd 100644 --- a/tools/CodeGenerator/KnownHeaders.cs +++ b/tools/CodeGenerator/KnownHeaders.cs @@ -9,8 +9,6 @@ using System.Text; namespace CodeGenerator { - // This project can output the Class library as a NuGet Package. - // To enable this option, right-click on the project and select the Properties menu item. In the Build tab select "Produce outputs on build". public class KnownHeaders { static string Each(IEnumerable values, Func formatter) @@ -18,11 +16,6 @@ namespace CodeGenerator return values.Any() ? values.Select(formatter).Aggregate((a, b) => a + b) : ""; } - static string If(bool condition, Func formatter) - { - return condition ? formatter() : ""; - } - static string AppendSwitch(IEnumerable> values, string className) => $@"var pUL = (ulong*)pUB; var pUI = (uint*)pUB; diff --git a/tools/CodeGenerator/Program.cs b/tools/CodeGenerator/Program.cs index a7870adec4..eb687fccac 100644 --- a/tools/CodeGenerator/Program.cs +++ b/tools/CodeGenerator/Program.cs @@ -25,17 +25,27 @@ namespace CodeGenerator Console.Error.WriteLine("Missing path to HttpUtilities.Generated.cs"); return 1; } + else if (args.Length < 4) + { + Console.Error.WriteLine("Missing path to TransportConnection.Generated.cs"); + return 1; + } - Run(args[0], args[1], args[2]); + Run(args[0], args[1], args[2], args[3]); return 0; } - public static void Run(string knownHeadersPath, string httpProtocolFeatureCollectionPath, string httpUtilitiesPath) + public static void Run( + string knownHeadersPath, + string httpProtocolFeatureCollectionPath, + string httpUtilitiesPath, + string transportConnectionFeatureCollectionPath) { var knownHeadersContent = KnownHeaders.GeneratedFile(); - var httpProtocolFeatureCollectionContent = HttpProtocolFeatureCollection.GeneratedFile("HttpProtocol"); + var httpProtocolFeatureCollectionContent = HttpProtocolFeatureCollection.GenerateFile(); var httpUtilitiesContent = HttpUtilities.HttpUtilities.GeneratedFile(); + var transportConnectionFeatureCollectionContent = TransportConnectionFeatureCollection.GenerateFile(); var existingKnownHeaders = File.Exists(knownHeadersPath) ? File.ReadAllText(knownHeadersPath) : ""; if (!string.Equals(knownHeadersContent, existingKnownHeaders)) @@ -54,6 +64,12 @@ namespace CodeGenerator { File.WriteAllText(httpUtilitiesPath, httpUtilitiesContent); } + + var existingTransportConnectionFeatureCollection = File.Exists(transportConnectionFeatureCollectionPath) ? File.ReadAllText(transportConnectionFeatureCollectionPath) : ""; + if (!string.Equals(transportConnectionFeatureCollectionContent, existingTransportConnectionFeatureCollection)) + { + File.WriteAllText(transportConnectionFeatureCollectionPath, transportConnectionFeatureCollectionContent); + } } } } diff --git a/tools/CodeGenerator/TransportConnectionFeatureCollection.cs b/tools/CodeGenerator/TransportConnectionFeatureCollection.cs new file mode 100644 index 0000000000..9f19f5387f --- /dev/null +++ b/tools/CodeGenerator/TransportConnectionFeatureCollection.cs @@ -0,0 +1,38 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace CodeGenerator +{ + public class TransportConnectionFeatureCollection + { + public static string GenerateFile() + { + // NOTE: This list MUST always match the set of feature interfaces implemented by TransportConnection. + // See also: src/Kestrel.Transport.Abstractions/Internal/TransportConnection.FeatureCollection.cs + var features = new[] + { + "IHttpConnectionFeature", + "IConnectionIdFeature", + "IConnectionTransportFeature", + "IConnectionItemsFeature", + "IMemoryPoolFeature", + "IApplicationTransportFeature", + "ITransportSchedulerFeature", + "IConnectionLifetimeFeature", + "IBytesWrittenFeature", + }; + + var usings = $@" +using Microsoft.AspNetCore.Connections.Features; +using Microsoft.AspNetCore.Http.Features;"; + + return FeatureCollectionGenerator.GenerateFile( + namespaceName: "Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal", + className: "TransportConnection", + allFeatures: features, + implementedFeatures: features, + extraUsings: usings, + fallbackFeatures: null); + } + } +}