Generate repetitive TransportConnection FeatureCollection code (#2548)

This commit is contained in:
Stephen Halter 2018-05-14 17:20:48 -07:00 committed by GitHub
parent 04f2101696
commit c683316253
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 627 additions and 468 deletions

View File

@ -2,8 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Collections;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Threading; using System.Threading;
@ -16,8 +14,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{ {
public partial class HttpProtocol : IFeatureCollection, public partial class HttpProtocol : IHttpRequestFeature,
IHttpRequestFeature,
IHttpResponseFeature, IHttpResponseFeature,
IHttpConnectionFeature, IHttpConnectionFeature,
IHttpRequestLifetimeFeature, 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, // 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.
// See also: tools/Microsoft.AspNetCore.Server.Kestrel.GeneratedCode/HttpProtocolFeatureCollection.cs // See also: tools/CodeGenerator/HttpProtocolFeatureCollection.cs
private int _featureRevision;
private List<KeyValuePair<Type, object>> 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<KeyValuePair<Type, object>>(2);
}
for (var i = 0; i < MaybeExtra.Count; i++)
{
if (MaybeExtra[i].Key == key)
{
MaybeExtra[i] = new KeyValuePair<Type, object>(key, value);
return;
}
}
MaybeExtra.Add(new KeyValuePair<Type, object>(key, value));
}
string IHttpRequestFeature.Protocol string IHttpRequestFeature.Protocol
{ {
@ -175,9 +126,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
bool IHttpResponseFeature.HasStarted => HasResponseStarted; bool IHttpResponseFeature.HasStarted => HasResponseStarted;
bool IFeatureCollection.IsReadOnly => false;
int IFeatureCollection.Revision => _featureRevision;
IPAddress IHttpConnectionFeature.RemoteIpAddress IPAddress IHttpConnectionFeature.RemoteIpAddress
{ {
@ -277,10 +225,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
OnCompleted(callback, state); OnCompleted(callback, state);
} }
IEnumerator<KeyValuePair<Type, object>> IEnumerable<KeyValuePair<Type, object>>.GetEnumerator() => FastEnumerable().GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => FastEnumerable().GetEnumerator();
void IHttpRequestLifetimeFeature.Abort() void IHttpRequestLifetimeFeature.Abort()
{ {
Abort(new ConnectionAbortedException()); Abort(new ConnectionAbortedException());

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http.Features;
@ -10,7 +11,7 @@ using Microsoft.AspNetCore.Server.Kestrel.Core.Features;
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http 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 IHttpRequestFeatureType = typeof(IHttpRequestFeature);
private static readonly Type IHttpResponseFeatureType = typeof(IHttpResponseFeature); private static readonly Type IHttpResponseFeatureType = typeof(IHttpResponseFeature);
@ -56,6 +57,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
private object _currentIHttpBodyControlFeature; private object _currentIHttpBodyControlFeature;
private object _currentIHttpSendFileFeature; private object _currentIHttpSendFileFeature;
private int _featureRevision;
private List<KeyValuePair<Type, object>> MaybeExtra;
private void FastReset() private void FastReset()
{ {
_currentIHttpRequestFeature = this; _currentIHttpRequestFeature = this;
@ -67,7 +72,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
_currentIHttpMinRequestBodyDataRateFeature = this; _currentIHttpMinRequestBodyDataRateFeature = this;
_currentIHttpMinResponseDataRateFeature = this; _currentIHttpMinResponseDataRateFeature = this;
_currentIHttpBodyControlFeature = this; _currentIHttpBodyControlFeature = this;
_currentIServiceProvidersFeature = null; _currentIServiceProvidersFeature = null;
_currentIHttpAuthenticationFeature = null; _currentIHttpAuthenticationFeature = null;
_currentIQueryFeature = null; _currentIQueryFeature = null;
@ -82,6 +87,53 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
_currentIHttpSendFileFeature = null; _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<KeyValuePair<Type, object>>(2);
}
for (var i = 0; i < MaybeExtra.Count; i++)
{
if (MaybeExtra[i].Key == key)
{
MaybeExtra[i] = new KeyValuePair<Type, object>(key, value);
return;
}
}
MaybeExtra.Add(new KeyValuePair<Type, object>(key, value));
}
bool IFeatureCollection.IsReadOnly => false;
int IFeatureCollection.Revision => _featureRevision;
object IFeatureCollection.this[Type key] object IFeatureCollection.this[Type key]
{ {
get get
@ -182,7 +234,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
set set
{ {
_featureRevision++; _featureRevision++;
if (key == IHttpRequestFeatureType) if (key == IHttpRequestFeatureType)
{ {
_currentIHttpRequestFeature = value; _currentIHttpRequestFeature = value;
@ -274,99 +326,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
} }
} }
void IFeatureCollection.Set<TFeature>(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>() TFeature IFeatureCollection.Get<TFeature>()
{ {
TFeature feature = default; TFeature feature = default;
@ -458,7 +417,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
{ {
feature = (TFeature)(ExtraFeatureGet(typeof(TFeature))); feature = (TFeature)(ExtraFeatureGet(typeof(TFeature)));
} }
if (feature == null) if (feature == null)
{ {
feature = ConnectionFeatures.Get<TFeature>(); feature = ConnectionFeatures.Get<TFeature>();
@ -467,100 +426,197 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
return feature; return feature;
} }
void IFeatureCollection.Set<TFeature>(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<KeyValuePair<Type, object>> FastEnumerable() private IEnumerable<KeyValuePair<Type, object>> FastEnumerable()
{ {
if (_currentIHttpRequestFeature != null) if (_currentIHttpRequestFeature != null)
{ {
yield return new KeyValuePair<Type, object>(IHttpRequestFeatureType, _currentIHttpRequestFeature as IHttpRequestFeature); yield return new KeyValuePair<Type, object>(IHttpRequestFeatureType, _currentIHttpRequestFeature);
} }
if (_currentIHttpResponseFeature != null) if (_currentIHttpResponseFeature != null)
{ {
yield return new KeyValuePair<Type, object>(IHttpResponseFeatureType, _currentIHttpResponseFeature as IHttpResponseFeature); yield return new KeyValuePair<Type, object>(IHttpResponseFeatureType, _currentIHttpResponseFeature);
} }
if (_currentIHttpRequestIdentifierFeature != null) if (_currentIHttpRequestIdentifierFeature != null)
{ {
yield return new KeyValuePair<Type, object>(IHttpRequestIdentifierFeatureType, _currentIHttpRequestIdentifierFeature as IHttpRequestIdentifierFeature); yield return new KeyValuePair<Type, object>(IHttpRequestIdentifierFeatureType, _currentIHttpRequestIdentifierFeature);
} }
if (_currentIServiceProvidersFeature != null) if (_currentIServiceProvidersFeature != null)
{ {
yield return new KeyValuePair<Type, object>(IServiceProvidersFeatureType, _currentIServiceProvidersFeature as IServiceProvidersFeature); yield return new KeyValuePair<Type, object>(IServiceProvidersFeatureType, _currentIServiceProvidersFeature);
} }
if (_currentIHttpRequestLifetimeFeature != null) if (_currentIHttpRequestLifetimeFeature != null)
{ {
yield return new KeyValuePair<Type, object>(IHttpRequestLifetimeFeatureType, _currentIHttpRequestLifetimeFeature as IHttpRequestLifetimeFeature); yield return new KeyValuePair<Type, object>(IHttpRequestLifetimeFeatureType, _currentIHttpRequestLifetimeFeature);
} }
if (_currentIHttpConnectionFeature != null) if (_currentIHttpConnectionFeature != null)
{ {
yield return new KeyValuePair<Type, object>(IHttpConnectionFeatureType, _currentIHttpConnectionFeature as IHttpConnectionFeature); yield return new KeyValuePair<Type, object>(IHttpConnectionFeatureType, _currentIHttpConnectionFeature);
} }
if (_currentIHttpAuthenticationFeature != null) if (_currentIHttpAuthenticationFeature != null)
{ {
yield return new KeyValuePair<Type, object>(IHttpAuthenticationFeatureType, _currentIHttpAuthenticationFeature as IHttpAuthenticationFeature); yield return new KeyValuePair<Type, object>(IHttpAuthenticationFeatureType, _currentIHttpAuthenticationFeature);
} }
if (_currentIQueryFeature != null) if (_currentIQueryFeature != null)
{ {
yield return new KeyValuePair<Type, object>(IQueryFeatureType, _currentIQueryFeature as IQueryFeature); yield return new KeyValuePair<Type, object>(IQueryFeatureType, _currentIQueryFeature);
} }
if (_currentIFormFeature != null) if (_currentIFormFeature != null)
{ {
yield return new KeyValuePair<Type, object>(IFormFeatureType, _currentIFormFeature as IFormFeature); yield return new KeyValuePair<Type, object>(IFormFeatureType, _currentIFormFeature);
} }
if (_currentIHttpUpgradeFeature != null) if (_currentIHttpUpgradeFeature != null)
{ {
yield return new KeyValuePair<Type, object>(IHttpUpgradeFeatureType, _currentIHttpUpgradeFeature as IHttpUpgradeFeature); yield return new KeyValuePair<Type, object>(IHttpUpgradeFeatureType, _currentIHttpUpgradeFeature);
} }
if (_currentIHttp2StreamIdFeature != null) if (_currentIHttp2StreamIdFeature != null)
{ {
yield return new KeyValuePair<Type, object>(IHttp2StreamIdFeatureType, _currentIHttp2StreamIdFeature as IHttp2StreamIdFeature); yield return new KeyValuePair<Type, object>(IHttp2StreamIdFeatureType, _currentIHttp2StreamIdFeature);
} }
if (_currentIResponseCookiesFeature != null) if (_currentIResponseCookiesFeature != null)
{ {
yield return new KeyValuePair<Type, object>(IResponseCookiesFeatureType, _currentIResponseCookiesFeature as IResponseCookiesFeature); yield return new KeyValuePair<Type, object>(IResponseCookiesFeatureType, _currentIResponseCookiesFeature);
} }
if (_currentIItemsFeature != null) if (_currentIItemsFeature != null)
{ {
yield return new KeyValuePair<Type, object>(IItemsFeatureType, _currentIItemsFeature as IItemsFeature); yield return new KeyValuePair<Type, object>(IItemsFeatureType, _currentIItemsFeature);
} }
if (_currentITlsConnectionFeature != null) if (_currentITlsConnectionFeature != null)
{ {
yield return new KeyValuePair<Type, object>(ITlsConnectionFeatureType, _currentITlsConnectionFeature as ITlsConnectionFeature); yield return new KeyValuePair<Type, object>(ITlsConnectionFeatureType, _currentITlsConnectionFeature);
} }
if (_currentIHttpWebSocketFeature != null) if (_currentIHttpWebSocketFeature != null)
{ {
yield return new KeyValuePair<Type, object>(IHttpWebSocketFeatureType, _currentIHttpWebSocketFeature as IHttpWebSocketFeature); yield return new KeyValuePair<Type, object>(IHttpWebSocketFeatureType, _currentIHttpWebSocketFeature);
} }
if (_currentISessionFeature != null) if (_currentISessionFeature != null)
{ {
yield return new KeyValuePair<Type, object>(ISessionFeatureType, _currentISessionFeature as ISessionFeature); yield return new KeyValuePair<Type, object>(ISessionFeatureType, _currentISessionFeature);
} }
if (_currentIHttpMaxRequestBodySizeFeature != null) if (_currentIHttpMaxRequestBodySizeFeature != null)
{ {
yield return new KeyValuePair<Type, object>(IHttpMaxRequestBodySizeFeatureType, _currentIHttpMaxRequestBodySizeFeature as IHttpMaxRequestBodySizeFeature); yield return new KeyValuePair<Type, object>(IHttpMaxRequestBodySizeFeatureType, _currentIHttpMaxRequestBodySizeFeature);
} }
if (_currentIHttpMinRequestBodyDataRateFeature != null) if (_currentIHttpMinRequestBodyDataRateFeature != null)
{ {
yield return new KeyValuePair<Type, object>(IHttpMinRequestBodyDataRateFeatureType, _currentIHttpMinRequestBodyDataRateFeature as IHttpMinRequestBodyDataRateFeature); yield return new KeyValuePair<Type, object>(IHttpMinRequestBodyDataRateFeatureType, _currentIHttpMinRequestBodyDataRateFeature);
} }
if (_currentIHttpMinResponseDataRateFeature != null) if (_currentIHttpMinResponseDataRateFeature != null)
{ {
yield return new KeyValuePair<Type, object>(IHttpMinResponseDataRateFeatureType, _currentIHttpMinResponseDataRateFeature as IHttpMinResponseDataRateFeature); yield return new KeyValuePair<Type, object>(IHttpMinResponseDataRateFeatureType, _currentIHttpMinResponseDataRateFeature);
} }
if (_currentIHttpBodyControlFeature != null) if (_currentIHttpBodyControlFeature != null)
{ {
yield return new KeyValuePair<Type, object>(IHttpBodyControlFeatureType, _currentIHttpBodyControlFeature as IHttpBodyControlFeature); yield return new KeyValuePair<Type, object>(IHttpBodyControlFeatureType, _currentIHttpBodyControlFeature);
} }
if (_currentIHttpSendFileFeature != null) if (_currentIHttpSendFileFeature != null)
{ {
yield return new KeyValuePair<Type, object>(IHttpSendFileFeatureType, _currentIHttpSendFileFeature as IHttpSendFileFeature); yield return new KeyValuePair<Type, object>(IHttpSendFileFeatureType, _currentIHttpSendFileFeature);
} }
if (MaybeExtra != null) if (MaybeExtra != null)
{ {
foreach(var item in MaybeExtra) foreach (var item in MaybeExtra)
{ {
yield return item; yield return item;
} }
} }
} }
IEnumerator<KeyValuePair<Type, object>> IEnumerable<KeyValuePair<Type, object>>.GetEnumerator() => FastEnumerable().GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => FastEnumerable().GetEnumerator();
} }
} }

View File

@ -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<byte> IMemoryPoolFeature.MemoryPool => MemoryPool;
IDuplexPipe IConnectionTransportFeature.Transport
{
get => Transport;
set => Transport = value;
}
IDuplexPipe IApplicationTransportFeature.Application
{
get => Application;
set => Application = value;
}
IDictionary<object, object> 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;
}
}

View File

@ -1,25 +1,16 @@
using System; // Copyright (c) .NET Foundation. All rights reserved.
using System.Buffers; // 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO.Pipelines;
using System.Net;
using System.Threading;
using Microsoft.AspNetCore.Connections.Features; using Microsoft.AspNetCore.Connections.Features;
using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http.Features;
namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal
{ {
public partial class TransportConnection : IFeatureCollection, public partial class TransportConnection : IFeatureCollection
IHttpConnectionFeature,
IConnectionIdFeature,
IConnectionTransportFeature,
IConnectionItemsFeature,
IMemoryPoolFeature,
IApplicationTransportFeature,
ITransportSchedulerFeature,
IConnectionLifetimeFeature,
IBytesWrittenFeature
{ {
private static readonly Type IHttpConnectionFeatureType = typeof(IHttpConnectionFeature); private static readonly Type IHttpConnectionFeatureType = typeof(IHttpConnectionFeature);
private static readonly Type IConnectionIdFeatureType = typeof(IConnectionIdFeature); private static readonly Type IConnectionIdFeatureType = typeof(IConnectionIdFeature);
@ -45,6 +36,28 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal
private List<KeyValuePair<Type, object>> MaybeExtra; private List<KeyValuePair<Type, object>> 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) private object ExtraFeatureGet(Type key)
{ {
if (MaybeExtra == null) if (MaybeExtra == null)
@ -84,125 +97,55 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal
int IFeatureCollection.Revision => _featureRevision; 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<byte> IMemoryPoolFeature.MemoryPool => MemoryPool;
IDuplexPipe IConnectionTransportFeature.Transport
{
get => Transport;
set => Transport = value;
}
IDuplexPipe IApplicationTransportFeature.Application
{
get => Application;
set => Application = value;
}
IDictionary<object, object> 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] object IFeatureCollection.this[Type key]
{ {
get get
{ {
object feature = null;
if (key == IHttpConnectionFeatureType) if (key == IHttpConnectionFeatureType)
{ {
return _currentIHttpConnectionFeature; feature = _currentIHttpConnectionFeature;
} }
else if (key == IConnectionIdFeatureType)
if (key == IConnectionIdFeatureType)
{ {
return _currentIConnectionIdFeature; feature = _currentIConnectionIdFeature;
} }
else if (key == IConnectionTransportFeatureType)
if (key == IConnectionTransportFeatureType)
{ {
return _currentIConnectionTransportFeature; feature = _currentIConnectionTransportFeature;
} }
else if (key == IConnectionItemsFeatureType)
if (key == IConnectionItemsFeatureType)
{ {
return _currentIConnectionItemsFeature; feature = _currentIConnectionItemsFeature;
} }
else if (key == IMemoryPoolFeatureType)
if (key == IMemoryPoolFeatureType)
{ {
return _currentIMemoryPoolFeature; feature = _currentIMemoryPoolFeature;
} }
else if (key == IApplicationTransportFeatureType)
if (key == IApplicationTransportFeatureType)
{ {
return _currentIApplicationTransportFeature; feature = _currentIApplicationTransportFeature;
} }
else if (key == ITransportSchedulerFeatureType)
if (key == ITransportSchedulerFeatureType)
{ {
return _currentITransportSchedulerFeature; feature = _currentITransportSchedulerFeature;
} }
else if (key == IConnectionLifetimeFeatureType)
if (key == IConnectionLifetimeFeatureType)
{ {
return _currentIConnectionLifetimeFeature; feature = _currentIConnectionLifetimeFeature;
} }
else if (key == IBytesWrittenFeatureType)
if (key == IBytesWrittenFeatureType)
{ {
return _currentIBytesWrittenFeature; feature = _currentIBytesWrittenFeature;
} }
else if (MaybeExtra != null)
if (MaybeExtra != null)
{ {
return ExtraFeatureGet(key); feature = ExtraFeatureGet(key);
} }
return null; return feature;
} }
set set
{ {
_featureRevision++; _featureRevision++;
@ -252,142 +195,130 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal
TFeature IFeatureCollection.Get<TFeature>() TFeature IFeatureCollection.Get<TFeature>()
{ {
TFeature feature = default;
if (typeof(TFeature) == typeof(IHttpConnectionFeature)) if (typeof(TFeature) == typeof(IHttpConnectionFeature))
{ {
return (TFeature)_currentIHttpConnectionFeature; feature = (TFeature)_currentIHttpConnectionFeature;
} }
else if (typeof(TFeature) == typeof(IConnectionIdFeature)) else if (typeof(TFeature) == typeof(IConnectionIdFeature))
{ {
return (TFeature)_currentIConnectionIdFeature; feature = (TFeature)_currentIConnectionIdFeature;
} }
else if (typeof(TFeature) == typeof(IConnectionTransportFeature)) else if (typeof(TFeature) == typeof(IConnectionTransportFeature))
{ {
return (TFeature)_currentIConnectionTransportFeature; feature = (TFeature)_currentIConnectionTransportFeature;
} }
else if (typeof(TFeature) == typeof(IConnectionItemsFeature)) else if (typeof(TFeature) == typeof(IConnectionItemsFeature))
{ {
return (TFeature)_currentIConnectionItemsFeature; feature = (TFeature)_currentIConnectionItemsFeature;
} }
else if (typeof(TFeature) == typeof(IMemoryPoolFeature)) else if (typeof(TFeature) == typeof(IMemoryPoolFeature))
{ {
return (TFeature)_currentIMemoryPoolFeature; feature = (TFeature)_currentIMemoryPoolFeature;
} }
else if (typeof(TFeature) == typeof(IApplicationTransportFeature)) else if (typeof(TFeature) == typeof(IApplicationTransportFeature))
{ {
return (TFeature)_currentIApplicationTransportFeature; feature = (TFeature)_currentIApplicationTransportFeature;
} }
else if (typeof(TFeature) == typeof(ITransportSchedulerFeature)) else if (typeof(TFeature) == typeof(ITransportSchedulerFeature))
{ {
return (TFeature)_currentITransportSchedulerFeature; feature = (TFeature)_currentITransportSchedulerFeature;
} }
else if (typeof(TFeature) == typeof(IConnectionLifetimeFeature)) else if (typeof(TFeature) == typeof(IConnectionLifetimeFeature))
{ {
return (TFeature)_currentIConnectionLifetimeFeature; feature = (TFeature)_currentIConnectionLifetimeFeature;
} }
else if (typeof(TFeature) == typeof(IBytesWrittenFeature)) else if (typeof(TFeature) == typeof(IBytesWrittenFeature))
{ {
return (TFeature)_currentIBytesWrittenFeature; feature = (TFeature)_currentIBytesWrittenFeature;
} }
else if (MaybeExtra != null) else if (MaybeExtra != null)
{ {
return (TFeature)ExtraFeatureGet(typeof(TFeature)); feature = (TFeature)(ExtraFeatureGet(typeof(TFeature)));
} }
return default; return feature;
} }
void IFeatureCollection.Set<TFeature>(TFeature instance) void IFeatureCollection.Set<TFeature>(TFeature feature)
{ {
_featureRevision++; _featureRevision++;
if (typeof(TFeature) == typeof(IHttpConnectionFeature)) if (typeof(TFeature) == typeof(IHttpConnectionFeature))
{ {
_currentIHttpConnectionFeature = instance; _currentIHttpConnectionFeature = feature;
} }
else if (typeof(TFeature) == typeof(IConnectionIdFeature)) else if (typeof(TFeature) == typeof(IConnectionIdFeature))
{ {
_currentIConnectionIdFeature = instance; _currentIConnectionIdFeature = feature;
} }
else if (typeof(TFeature) == typeof(IConnectionTransportFeature)) else if (typeof(TFeature) == typeof(IConnectionTransportFeature))
{ {
_currentIConnectionTransportFeature = instance; _currentIConnectionTransportFeature = feature;
} }
else if (typeof(TFeature) == typeof(IConnectionItemsFeature)) else if (typeof(TFeature) == typeof(IConnectionItemsFeature))
{ {
_currentIConnectionItemsFeature = instance; _currentIConnectionItemsFeature = feature;
} }
else if (typeof(TFeature) == typeof(IMemoryPoolFeature)) else if (typeof(TFeature) == typeof(IMemoryPoolFeature))
{ {
_currentIMemoryPoolFeature = instance; _currentIMemoryPoolFeature = feature;
} }
else if (typeof(TFeature) == typeof(IApplicationTransportFeature)) else if (typeof(TFeature) == typeof(IApplicationTransportFeature))
{ {
_currentIApplicationTransportFeature = instance; _currentIApplicationTransportFeature = feature;
} }
else if (typeof(TFeature) == typeof(ITransportSchedulerFeature)) else if (typeof(TFeature) == typeof(ITransportSchedulerFeature))
{ {
_currentITransportSchedulerFeature = instance; _currentITransportSchedulerFeature = feature;
} }
else if (typeof(TFeature) == typeof(IConnectionLifetimeFeature)) else if (typeof(TFeature) == typeof(IConnectionLifetimeFeature))
{ {
_currentIConnectionLifetimeFeature = instance; _currentIConnectionLifetimeFeature = feature;
} }
else if (typeof(TFeature) == typeof(IBytesWrittenFeature)) else if (typeof(TFeature) == typeof(IBytesWrittenFeature))
{ {
_currentIBytesWrittenFeature = instance; _currentIBytesWrittenFeature = feature;
} }
else else
{ {
ExtraFeatureSet(typeof(TFeature), instance); ExtraFeatureSet(typeof(TFeature), feature);
} }
} }
IEnumerator<KeyValuePair<Type, object>> IEnumerable<KeyValuePair<Type, object>>.GetEnumerator() => FastEnumerable().GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => FastEnumerable().GetEnumerator();
private IEnumerable<KeyValuePair<Type, object>> FastEnumerable() private IEnumerable<KeyValuePair<Type, object>> FastEnumerable()
{ {
if (_currentIHttpConnectionFeature != null) if (_currentIHttpConnectionFeature != null)
{ {
yield return new KeyValuePair<Type, object>(IHttpConnectionFeatureType, _currentIHttpConnectionFeature); yield return new KeyValuePair<Type, object>(IHttpConnectionFeatureType, _currentIHttpConnectionFeature);
} }
if (_currentIConnectionIdFeature != null) if (_currentIConnectionIdFeature != null)
{ {
yield return new KeyValuePair<Type, object>(IConnectionIdFeatureType, _currentIConnectionIdFeature); yield return new KeyValuePair<Type, object>(IConnectionIdFeatureType, _currentIConnectionIdFeature);
} }
if (_currentIConnectionTransportFeature != null) if (_currentIConnectionTransportFeature != null)
{ {
yield return new KeyValuePair<Type, object>(IConnectionTransportFeatureType, _currentIConnectionTransportFeature); yield return new KeyValuePair<Type, object>(IConnectionTransportFeatureType, _currentIConnectionTransportFeature);
} }
if (_currentIConnectionItemsFeature != null) if (_currentIConnectionItemsFeature != null)
{ {
yield return new KeyValuePair<Type, object>(IConnectionItemsFeatureType, _currentIConnectionItemsFeature); yield return new KeyValuePair<Type, object>(IConnectionItemsFeatureType, _currentIConnectionItemsFeature);
} }
if (_currentIMemoryPoolFeature != null) if (_currentIMemoryPoolFeature != null)
{ {
yield return new KeyValuePair<Type, object>(IMemoryPoolFeatureType, _currentIMemoryPoolFeature); yield return new KeyValuePair<Type, object>(IMemoryPoolFeatureType, _currentIMemoryPoolFeature);
} }
if (_currentIApplicationTransportFeature != null) if (_currentIApplicationTransportFeature != null)
{ {
yield return new KeyValuePair<Type, object>(IApplicationTransportFeatureType, _currentIApplicationTransportFeature); yield return new KeyValuePair<Type, object>(IApplicationTransportFeatureType, _currentIApplicationTransportFeature);
} }
if (_currentITransportSchedulerFeature != null) if (_currentITransportSchedulerFeature != null)
{ {
yield return new KeyValuePair<Type, object>(ITransportSchedulerFeatureType, _currentITransportSchedulerFeature); yield return new KeyValuePair<Type, object>(ITransportSchedulerFeatureType, _currentITransportSchedulerFeature);
} }
if (_currentIConnectionLifetimeFeature != null) if (_currentIConnectionLifetimeFeature != null)
{ {
yield return new KeyValuePair<Type, object>(IConnectionLifetimeFeatureType, _currentIConnectionLifetimeFeature); yield return new KeyValuePair<Type, object>(IConnectionLifetimeFeatureType, _currentIConnectionLifetimeFeature);
} }
if (_currentIBytesWrittenFeature != null) if (_currentIBytesWrittenFeature != null)
{ {
yield return new KeyValuePair<Type, object>(IBytesWrittenFeatureType, _currentIBytesWrittenFeature); yield return new KeyValuePair<Type, object>(IBytesWrittenFeatureType, _currentIBytesWrittenFeature);
@ -401,5 +332,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal
} }
} }
} }
IEnumerator<KeyValuePair<Type, object>> IEnumerable<KeyValuePair<Type, object>>.GetEnumerator() => FastEnumerable().GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => FastEnumerable().GetEnumerator();
} }
} }

View File

@ -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.Collections.Generic;
using System.IO.Pipelines; using System.IO.Pipelines;
using System.Net; using System.Net;

View File

@ -15,26 +15,31 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
const string httpHeadersGeneratedPath = "../../../../../src/Kestrel.Core/Internal/Http/HttpHeaders.Generated.cs"; const string httpHeadersGeneratedPath = "../../../../../src/Kestrel.Core/Internal/Http/HttpHeaders.Generated.cs";
const string httpProtocolGeneratedPath = "../../../../../src/Kestrel.Core/Internal/Http/HttpProtocol.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 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 testHttpHeadersGeneratedPath = Path.GetTempFileName();
var testHttpProtocolGeneratedPath = Path.GetTempFileName(); var testHttpProtocolGeneratedPath = Path.GetTempFileName();
var testHttpUtilitiesGeneratedPath = Path.GetTempFileName(); var testHttpUtilitiesGeneratedPath = Path.GetTempFileName();
var testTransportConnectionGeneratedPath = Path.GetTempFileName();
try try
{ {
var currentHttpHeadersGenerated = File.ReadAllText(httpHeadersGeneratedPath); var currentHttpHeadersGenerated = File.ReadAllText(httpHeadersGeneratedPath);
var currentHttpProtocolGenerated = File.ReadAllText(httpProtocolGeneratedPath); var currentHttpProtocolGenerated = File.ReadAllText(httpProtocolGeneratedPath);
var currentHttpUtilitiesGenerated = File.ReadAllText(httpUtilitiesGeneratedPath); 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 testHttpHeadersGenerated = File.ReadAllText(testHttpHeadersGeneratedPath);
var testHttpProtocolGenerated = File.ReadAllText(testHttpProtocolGeneratedPath); var testHttpProtocolGenerated = File.ReadAllText(testHttpProtocolGeneratedPath);
var testHttpUtilitiesGenerated = File.ReadAllText(testHttpUtilitiesGeneratedPath); var testHttpUtilitiesGenerated = File.ReadAllText(testHttpUtilitiesGeneratedPath);
var testTransportConnectionGenerated = File.ReadAllText(testTransportConnectionGeneratedPath);
Assert.Equal(currentHttpHeadersGenerated, testHttpHeadersGenerated, ignoreLineEndingDifferences: true); Assert.Equal(currentHttpHeadersGenerated, testHttpHeadersGenerated, ignoreLineEndingDifferences: true);
Assert.Equal(currentHttpProtocolGenerated, testHttpProtocolGenerated, ignoreLineEndingDifferences: true); Assert.Equal(currentHttpProtocolGenerated, testHttpProtocolGenerated, ignoreLineEndingDifferences: true);
Assert.Equal(currentHttpUtilitiesGenerated, testHttpUtilitiesGenerated, ignoreLineEndingDifferences: true); Assert.Equal(currentHttpUtilitiesGenerated, testHttpUtilitiesGenerated, ignoreLineEndingDifferences: true);
Assert.Equal(currentTransportConnectionGenerated, testTransportConnectionGenerated, ignoreLineEndingDifferences: true);
} }
finally finally
@ -42,6 +47,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
File.Delete(testHttpHeadersGeneratedPath); File.Delete(testHttpHeadersGeneratedPath);
File.Delete(testHttpProtocolGeneratedPath); File.Delete(testHttpProtocolGeneratedPath);
File.Delete(testHttpUtilitiesGeneratedPath); File.Delete(testHttpUtilitiesGeneratedPath);
File.Delete(testTransportConnectionGeneratedPath);
} }
} }
} }

View File

@ -11,6 +11,10 @@
<Content Include="..\shared\TestCertificates\*.pfx" CopyToOutputDirectory="PreserveNewest" /> <Content Include="..\shared\TestCertificates\*.pfx" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'net461'">
<ProjectReference Include="..\..\tools\CodeGenerator\CodeGenerator.csproj" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\src\Kestrel\Kestrel.csproj" /> <ProjectReference Include="..\..\src\Kestrel\Kestrel.csproj" />
<ProjectReference Include="..\..\src\Kestrel.Transport.Libuv\Kestrel.Transport.Libuv.csproj" /> <ProjectReference Include="..\..\src\Kestrel.Transport.Libuv\Kestrel.Transport.Libuv.csproj" />

View File

@ -16,10 +16,6 @@
<Content Include="..\shared\TestCertificates\*.pfx" CopyToOutputDirectory="PreserveNewest" /> <Content Include="..\shared\TestCertificates\*.pfx" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'net461'">
<ProjectReference Include="..\..\tools\CodeGenerator\CodeGenerator.csproj" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\src\Kestrel\Kestrel.csproj" /> <ProjectReference Include="..\..\src\Kestrel\Kestrel.csproj" />
<ProjectReference Include="..\..\src\Kestrel.Transport.Libuv\Kestrel.Transport.Libuv.csproj" /> <ProjectReference Include="..\..\src\Kestrel.Transport.Libuv\Kestrel.Transport.Libuv.csproj" />

View File

@ -15,10 +15,6 @@
<Content Include="..\shared\TestCertificates\*.pfx" CopyToOutputDirectory="PreserveNewest" /> <Content Include="..\shared\TestCertificates\*.pfx" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' != 'net461'">
<ProjectReference Include="..\..\tools\CodeGenerator\CodeGenerator.csproj" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\src\Kestrel\Kestrel.csproj" /> <ProjectReference Include="..\..\src\Kestrel\Kestrel.csproj" />
<ProjectReference Include="..\..\src\Kestrel.Transport.Sockets\Kestrel.Transport.Sockets.csproj" /> <ProjectReference Include="..\..\src\Kestrel.Transport.Sockets\Kestrel.Transport.Sockets.csproj" />

View File

@ -14,8 +14,8 @@
</ItemGroup> </ItemGroup>
<PropertyGroup> <PropertyGroup>
<StartWorkingDirectory>$(MSBuildThisFileDirectory)..\..\src\Kestrel.Core</StartWorkingDirectory> <StartWorkingDirectory>$(MSBuildThisFileDirectory)..\..\</StartWorkingDirectory>
<StartArguments>Internal/Http/HttpHeaders.Generated.cs Internal/Http/HttpProtocol.Generated.cs Internal/Infrastructure/HttpUtilities.Generated.cs</StartArguments> <StartArguments>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</StartArguments>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

View File

@ -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<KeyValuePair<Type, object>> 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<KeyValuePair<Type, object>>(2);
}}
for (var i = 0; i < MaybeExtra.Count; i++)
{{
if (MaybeExtra[i].Key == key)
{{
MaybeExtra[i] = new KeyValuePair<Type, object>(key, value);
return;
}}
}}
MaybeExtra.Add(new KeyValuePair<Type, object>(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>()
{{
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<TFeature>();
}}")}
return feature;
}}
void IFeatureCollection.Set<TFeature>(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<KeyValuePair<Type, object>> FastEnumerable()
{{{Each(features, feature => $@"
if (_current{feature.Name} != null)
{{
yield return new KeyValuePair<Type, object>({feature.Name}Type, _current{feature.Name});
}}")}
if (MaybeExtra != null)
{{
foreach (var item in MaybeExtra)
{{
yield return item;
}}
}}
}}
IEnumerator<KeyValuePair<Type, object>> IEnumerable<KeyValuePair<Type, object>>.GetEnumerator() => FastEnumerable().GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => FastEnumerable().GetEnumerator();
}}
}}
";
}
static string Each<T>(IEnumerable<T> values, Func<T, string> formatter)
{
return values.Any() ? values.Select(formatter).Aggregate((a, b) => a + b) : "";
}
private class KnownFeature
{
public string Name;
public int Index;
}
}
}

View File

@ -1,28 +1,13 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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; using System.Linq;
namespace CodeGenerator 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 public class HttpProtocolFeatureCollection
{ {
static string Each<T>(IEnumerable<T> values, Func<T, string> formatter) public static string GenerateFile()
{
return values.Select(formatter).Aggregate((a, b) => a + b);
}
public class KnownFeature
{
public string Name;
public int Index;
}
public static string GeneratedFile(string className)
{ {
var alwaysFeatures = new[] var alwaysFeatures = new[]
{ {
@ -65,14 +50,10 @@ namespace CodeGenerator
.Concat(commonFeatures) .Concat(commonFeatures)
.Concat(sometimesFeatures) .Concat(sometimesFeatures)
.Concat(rareFeatures) .Concat(rareFeatures)
.Select((type, index) => new KnownFeature .ToArray();
{
Name = type,
Index = index
});
// NOTE: This list MUST always match the set of feature interfaces implemented by HttpProtocol. // NOTE: This list MUST always match the set of feature interfaces implemented by HttpProtocol.
// See also: src/Kestrel/Http/HttpProtocol.FeatureCollection.cs // See also: src/Kestrel.Core/Internal/Http/HttpProtocol.FeatureCollection.cs
var implementedFeatures = new[] var implementedFeatures = new[]
{ {
"IHttpRequestFeature", "IHttpRequestFeature",
@ -86,114 +67,18 @@ namespace CodeGenerator
"IHttpBodyControlFeature", "IHttpBodyControlFeature",
}; };
return $@"// Copyright (c) .NET Foundation. All rights reserved. var usings = $@"
// 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 Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Http.Features.Authentication; 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 return FeatureCollectionGenerator.GenerateFile(
{{ namespaceName: "Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http",
public partial class {className} className: "HttpProtocol",
{{{Each(allFeatures, feature => $@" allFeatures: allFeatures,
private static readonly Type {feature.Name}Type = typeof({feature.Name});")} implementedFeatures: implementedFeatures,
{Each(allFeatures, feature => $@" extraUsings: usings,
private object _current{feature.Name};")} fallbackFeatures: "ConnectionFeatures");
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>(TFeature feature)
{{
_featureRevision++;{Each(allFeatures, feature => $@"
{(feature.Index != 0 ? "else " : "")}if (typeof(TFeature) == typeof({feature.Name}))
{{
_current{feature.Name} = feature;
}}")}
else
{{
ExtraFeatureSet(typeof(TFeature), feature);
}}
}}
TFeature IFeatureCollection.Get<TFeature>()
{{
TFeature feature = default;{Each(allFeatures, feature => $@"
{(feature.Index != 0 ? "else " : "")}if (typeof(TFeature) == typeof({feature.Name}))
{{
feature = (TFeature)_current{feature.Name};
}}")}
else if (MaybeExtra != null)
{{
feature = (TFeature)(ExtraFeatureGet(typeof(TFeature)));
}}
if (feature == null)
{{
feature = ConnectionFeatures.Get<TFeature>();
}}
return feature;
}}
private IEnumerable<KeyValuePair<Type, object>> FastEnumerable()
{{{Each(allFeatures, feature => $@"
if (_current{feature.Name} != null)
{{
yield return new KeyValuePair<Type, object>({feature.Name}Type, _current{feature.Name} as {feature.Name});
}}")}
if (MaybeExtra != null)
{{
foreach(var item in MaybeExtra)
{{
yield return item;
}}
}}
}}
}}
}}
";
} }
} }
} }

View File

@ -9,8 +9,6 @@ using System.Text;
namespace CodeGenerator 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 public class KnownHeaders
{ {
static string Each<T>(IEnumerable<T> values, Func<T, string> formatter) static string Each<T>(IEnumerable<T> values, Func<T, string> formatter)
@ -18,11 +16,6 @@ namespace CodeGenerator
return values.Any() ? values.Select(formatter).Aggregate((a, b) => a + b) : ""; return values.Any() ? values.Select(formatter).Aggregate((a, b) => a + b) : "";
} }
static string If(bool condition, Func<string> formatter)
{
return condition ? formatter() : "";
}
static string AppendSwitch(IEnumerable<IGrouping<int, KnownHeader>> values, string className) => static string AppendSwitch(IEnumerable<IGrouping<int, KnownHeader>> values, string className) =>
$@"var pUL = (ulong*)pUB; $@"var pUL = (ulong*)pUB;
var pUI = (uint*)pUB; var pUI = (uint*)pUB;

View File

@ -25,17 +25,27 @@ namespace CodeGenerator
Console.Error.WriteLine("Missing path to HttpUtilities.Generated.cs"); Console.Error.WriteLine("Missing path to HttpUtilities.Generated.cs");
return 1; 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; 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 knownHeadersContent = KnownHeaders.GeneratedFile();
var httpProtocolFeatureCollectionContent = HttpProtocolFeatureCollection.GeneratedFile("HttpProtocol"); var httpProtocolFeatureCollectionContent = HttpProtocolFeatureCollection.GenerateFile();
var httpUtilitiesContent = HttpUtilities.HttpUtilities.GeneratedFile(); var httpUtilitiesContent = HttpUtilities.HttpUtilities.GeneratedFile();
var transportConnectionFeatureCollectionContent = TransportConnectionFeatureCollection.GenerateFile();
var existingKnownHeaders = File.Exists(knownHeadersPath) ? File.ReadAllText(knownHeadersPath) : ""; var existingKnownHeaders = File.Exists(knownHeadersPath) ? File.ReadAllText(knownHeadersPath) : "";
if (!string.Equals(knownHeadersContent, existingKnownHeaders)) if (!string.Equals(knownHeadersContent, existingKnownHeaders))
@ -54,6 +64,12 @@ namespace CodeGenerator
{ {
File.WriteAllText(httpUtilitiesPath, httpUtilitiesContent); File.WriteAllText(httpUtilitiesPath, httpUtilitiesContent);
} }
var existingTransportConnectionFeatureCollection = File.Exists(transportConnectionFeatureCollectionPath) ? File.ReadAllText(transportConnectionFeatureCollectionPath) : "";
if (!string.Equals(transportConnectionFeatureCollectionContent, existingTransportConnectionFeatureCollection))
{
File.WriteAllText(transportConnectionFeatureCollectionPath, transportConnectionFeatureCollectionContent);
}
} }
} }
} }

View File

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