diff --git a/src/Kestrel.Core/Internal/Http/Http1Connection.FeatureCollection.cs b/src/Kestrel.Core/Internal/Http/Http1Connection.FeatureCollection.cs index 037f9d7737..0a5c1dd247 100644 --- a/src/Kestrel.Core/Internal/Http/Http1Connection.FeatureCollection.cs +++ b/src/Kestrel.Core/Internal/Http/Http1Connection.FeatureCollection.cs @@ -11,42 +11,9 @@ using Microsoft.AspNetCore.Server.Kestrel.Core.Features; namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http { - public partial class Http1Connection : IHttpUpgradeFeature, - IHttpMinRequestBodyDataRateFeature, + public partial class Http1Connection : IHttpMinRequestBodyDataRateFeature, IHttpMinResponseDataRateFeature { - bool IHttpUpgradeFeature.IsUpgradableRequest => IsUpgradableRequest; - - async Task IHttpUpgradeFeature.UpgradeAsync() - { - if (!((IHttpUpgradeFeature)this).IsUpgradableRequest) - { - throw new InvalidOperationException(CoreStrings.CannotUpgradeNonUpgradableRequest); - } - - if (IsUpgraded) - { - throw new InvalidOperationException(CoreStrings.UpgradeCannotBeCalledMultipleTimes); - } - - if (!ServiceContext.ConnectionManager.UpgradedConnectionCount.TryLockOne()) - { - throw new InvalidOperationException(CoreStrings.UpgradedConnectionLimitReached); - } - - IsUpgraded = true; - - ConnectionFeatures.Get()?.ReleaseConnection(); - - StatusCode = StatusCodes.Status101SwitchingProtocols; - ReasonPhrase = "Switching Protocols"; - ResponseHeaders["Connection"] = "Upgrade"; - - await FlushAsync(default(CancellationToken)); - - return _streams.Upgrade(); - } - MinDataRate IHttpMinRequestBodyDataRateFeature.MinDataRate { get => MinRequestBodyDataRate; diff --git a/src/Kestrel.Core/Internal/Http/Http1Connection.cs b/src/Kestrel.Core/Internal/Http/Http1Connection.cs index 145e1abfa0..649560ce78 100644 --- a/src/Kestrel.Core/Internal/Http/Http1Connection.cs +++ b/src/Kestrel.Core/Internal/Http/Http1Connection.cs @@ -61,8 +61,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http public bool RequestTimedOut => _requestTimedOut; - public override bool IsUpgradableRequest => _upgradeAvailable; - public MinDataRate MinRequestBodyDataRate { get; set; } public MinDataRate MinResponseDataRate { get; set; } diff --git a/src/Kestrel.Core/Internal/Http/HttpProtocol.FeatureCollection.cs b/src/Kestrel.Core/Internal/Http/HttpProtocol.FeatureCollection.cs index e947e708f0..cf40e3a9c0 100644 --- a/src/Kestrel.Core/Internal/Http/HttpProtocol.FeatureCollection.cs +++ b/src/Kestrel.Core/Internal/Http/HttpProtocol.FeatureCollection.cs @@ -15,6 +15,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http { public partial class HttpProtocol : IHttpRequestFeature, IHttpResponseFeature, + IHttpUpgradeFeature, IHttpConnectionFeature, IHttpRequestLifetimeFeature, IHttpRequestIdentifierFeature, @@ -123,6 +124,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http bool IHttpResponseFeature.HasStarted => HasResponseStarted; + bool IHttpUpgradeFeature.IsUpgradableRequest => IsUpgradableRequest; IPAddress IHttpConnectionFeature.RemoteIpAddress { @@ -192,7 +194,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http protected void ResetHttp1Features() { - _currentIHttpUpgradeFeature = this; _currentIHttpMinRequestBodyDataRateFeature = this; _currentIHttpMinResponseDataRateFeature = this; } @@ -213,6 +214,36 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http OnCompleted(callback, state); } + async Task IHttpUpgradeFeature.UpgradeAsync() + { + if (!IsUpgradableRequest) + { + throw new InvalidOperationException(CoreStrings.CannotUpgradeNonUpgradableRequest); + } + + if (IsUpgraded) + { + throw new InvalidOperationException(CoreStrings.UpgradeCannotBeCalledMultipleTimes); + } + + if (!ServiceContext.ConnectionManager.UpgradedConnectionCount.TryLockOne()) + { + throw new InvalidOperationException(CoreStrings.UpgradedConnectionLimitReached); + } + + IsUpgraded = true; + + ConnectionFeatures.Get()?.ReleaseConnection(); + + StatusCode = StatusCodes.Status101SwitchingProtocols; + ReasonPhrase = "Switching Protocols"; + ResponseHeaders["Connection"] = "Upgrade"; + + await FlushAsync(); + + return _streams.Upgrade(); + } + void IHttpRequestLifetimeFeature.Abort() { ApplicationAbort(); diff --git a/src/Kestrel.Core/Internal/Http/HttpProtocol.Generated.cs b/src/Kestrel.Core/Internal/Http/HttpProtocol.Generated.cs index c913c6adce..4b67300ba3 100644 --- a/src/Kestrel.Core/Internal/Http/HttpProtocol.Generated.cs +++ b/src/Kestrel.Core/Internal/Http/HttpProtocol.Generated.cs @@ -67,6 +67,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http { _currentIHttpRequestFeature = this; _currentIHttpResponseFeature = this; + _currentIHttpUpgradeFeature = this; _currentIHttpRequestIdentifierFeature = this; _currentIHttpRequestLifetimeFeature = this; _currentIHttpConnectionFeature = this; @@ -77,7 +78,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http _currentIHttpAuthenticationFeature = null; _currentIQueryFeature = null; _currentIFormFeature = null; - _currentIHttpUpgradeFeature = null; _currentIHttp2StreamIdFeature = null; _currentIHttpResponseTrailersFeature = null; _currentIResponseCookiesFeature = null; diff --git a/src/Kestrel.Core/Internal/Http/HttpProtocol.cs b/src/Kestrel.Core/Internal/Http/HttpProtocol.cs index 7cb6844173..3309701e0a 100644 --- a/src/Kestrel.Core/Internal/Http/HttpProtocol.cs +++ b/src/Kestrel.Core/Internal/Http/HttpProtocol.cs @@ -50,7 +50,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http // Keep-alive is default for HTTP/1.1 and HTTP/2; parsing and errors will change its value // volatile, see: https://msdn.microsoft.com/en-us/library/x13ttww7.aspx protected volatile bool _keepAlive = true; - protected bool _upgradeAvailable; private bool _canHaveBody; private bool _autoChunk; private Exception _applicationException; @@ -116,7 +115,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http } } - public abstract bool IsUpgradableRequest { get; } + public bool IsUpgradableRequest { get; private set; } public bool IsUpgraded { get; set; } public IPAddress RemoteIpAddress { get; set; } public int RemotePort { get; set; } @@ -532,7 +531,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http _keepAlive = false; } - _upgradeAvailable = messageBody.RequestUpgrade; + IsUpgradableRequest = messageBody.RequestUpgrade; InitializeStreams(messageBody); diff --git a/src/Kestrel.Core/Internal/Http2/Http2Stream.cs b/src/Kestrel.Core/Internal/Http2/Http2Stream.cs index 13c23e91bc..30d00c969f 100644 --- a/src/Kestrel.Core/Internal/Http2/Http2Stream.cs +++ b/src/Kestrel.Core/Internal/Http2/Http2Stream.cs @@ -68,8 +68,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http2 internal bool RstStreamReceived => (_completionState & StreamCompletionFlags.RstStreamReceived) == StreamCompletionFlags.RstStreamReceived; internal bool IsDraining => (_completionState & StreamCompletionFlags.Draining) == StreamCompletionFlags.Draining; - public override bool IsUpgradableRequest => false; - protected override void OnReset() { ResetHttp2Features(); diff --git a/test/Kestrel.Core.Tests/HttpProtocolFeatureCollectionTests.cs b/test/Kestrel.Core.Tests/HttpProtocolFeatureCollectionTests.cs index b53866bbfd..001d3952b3 100644 --- a/test/Kestrel.Core.Tests/HttpProtocolFeatureCollectionTests.cs +++ b/test/Kestrel.Core.Tests/HttpProtocolFeatureCollectionTests.cs @@ -19,22 +19,30 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests public class HttpProtocolFeatureCollectionTests { private readonly TestHttp1Connection _http1Connection; - private readonly HttpConnectionContext _http1ConnectionContext; + private readonly HttpConnectionContext _httpConnectionContext; private readonly IFeatureCollection _collection; + private readonly IFeatureCollection _http2Collection; public HttpProtocolFeatureCollectionTests() { - _http1ConnectionContext = new HttpConnectionContext + var context = new Http2StreamContext { ServiceContext = new TestServiceContext(), ConnectionFeatures = new FeatureCollection(), TimeoutControl = Mock.Of(), Transport = Mock.Of(), + ServerPeerSettings = new Http2PeerSettings(), + ClientPeerSettings = new Http2PeerSettings(), }; - _http1Connection = new TestHttp1Connection(_http1ConnectionContext); + _httpConnectionContext = context; + _http1Connection = new TestHttp1Connection(context); _http1Connection.Reset(); _collection = _http1Connection; + + var http2Stream = new Http2Stream(context); + http2Stream.Reset(); + _http2Collection = http2Stream; } [Fact] @@ -143,24 +151,22 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests [Fact] public void Http2StreamFeatureCollectionDoesNotIncludeMinRateFeatures() { - var http2Stream = new Http2Stream(new Http2StreamContext - { - ServiceContext = new TestServiceContext(), - ConnectionFeatures = new FeatureCollection(), - TimeoutControl = Mock.Of(), - Transport = Mock.Of(), - ServerPeerSettings = new Http2PeerSettings(), - ClientPeerSettings = new Http2PeerSettings(), - }); - var http2StreamCollection = (IFeatureCollection)http2Stream; - - Assert.Null(http2StreamCollection.Get()); - Assert.Null(http2StreamCollection.Get()); + Assert.Null(_http2Collection.Get()); + Assert.Null(_http2Collection.Get()); Assert.NotNull(_collection.Get()); Assert.NotNull(_collection.Get()); } + [Fact] + public void Http2StreamFeatureCollectionDoesIncludeUpgradeFeature() + { + var upgradeFeature = _http2Collection.Get(); + + Assert.NotNull(upgradeFeature); + Assert.False(upgradeFeature.IsUpgradableRequest); + } + private void CompareGenericGetterToIndexer() { Assert.Same(_collection.Get(), _collection[typeof(IHttpRequestFeature)]); @@ -213,6 +219,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests return featureCount; } - private Http1Connection CreateHttp1Connection() => new TestHttp1Connection(_http1ConnectionContext); + private Http1Connection CreateHttp1Connection() => new TestHttp1Connection(_httpConnectionContext); } } diff --git a/tools/CodeGenerator/HttpProtocolFeatureCollection.cs b/tools/CodeGenerator/HttpProtocolFeatureCollection.cs index 790ceb8fab..98be2c3fea 100644 --- a/tools/CodeGenerator/HttpProtocolFeatureCollection.cs +++ b/tools/CodeGenerator/HttpProtocolFeatureCollection.cs @@ -59,6 +59,7 @@ namespace CodeGenerator { "IHttpRequestFeature", "IHttpResponseFeature", + "IHttpUpgradeFeature", "IHttpRequestIdentifierFeature", "IHttpRequestLifetimeFeature", "IHttpConnectionFeature",