diff --git a/src/Microsoft.AspNetCore.Server.Kestrel.Core/CoreStrings.resx b/src/Microsoft.AspNetCore.Server.Kestrel.Core/CoreStrings.resx
index e85918600f..c024ae82a9 100644
--- a/src/Microsoft.AspNetCore.Server.Kestrel.Core/CoreStrings.resx
+++ b/src/Microsoft.AspNetCore.Server.Kestrel.Core/CoreStrings.resx
@@ -306,4 +306,7 @@
Connection processing ended abnormally.
+
+ Cannot upgrade a non-upgradable request. Check IHttpUpgradeFeature.IsUpgradableRequest to determine if a request can be upgraded.
+
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/Http/Frame.FeatureCollection.cs b/src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/Http/Frame.FeatureCollection.cs
index bd1d8e2a56..429c517899 100644
--- a/src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/Http/Frame.FeatureCollection.cs
+++ b/src/Microsoft.AspNetCore.Server.Kestrel.Core/Internal/Http/Frame.FeatureCollection.cs
@@ -230,6 +230,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http
async Task IHttpUpgradeFeature.UpgradeAsync()
{
+ if (!((IHttpUpgradeFeature)this).IsUpgradableRequest)
+ {
+ throw new InvalidOperationException(CoreStrings.CannotUpgradeNonUpgradableRequest);
+ }
+
StatusCode = StatusCodes.Status101SwitchingProtocols;
ReasonPhrase = "Switching Protocols";
ResponseHeaders["Connection"] = "Upgrade";
diff --git a/src/Microsoft.AspNetCore.Server.Kestrel.Core/Properties/CoreStrings.Designer.cs b/src/Microsoft.AspNetCore.Server.Kestrel.Core/Properties/CoreStrings.Designer.cs
index 1a86007def..2971494a5b 100644
--- a/src/Microsoft.AspNetCore.Server.Kestrel.Core/Properties/CoreStrings.Designer.cs
+++ b/src/Microsoft.AspNetCore.Server.Kestrel.Core/Properties/CoreStrings.Designer.cs
@@ -892,6 +892,20 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
internal static string FormatRequestProcessingEndError()
=> GetString("RequestProcessingEndError");
+ ///
+ /// Cannot upgrade a non-upgradable request. Check IHttpUpgradeFeature.IsUpgradableRequest to determine if a request can be upgraded.
+ ///
+ internal static string CannotUpgradeNonUpgradableRequest
+ {
+ get => GetString("CannotUpgradeNonUpgradableRequest");
+ }
+
+ ///
+ /// Cannot upgrade a non-upgradable request. Check IHttpUpgradeFeature.IsUpgradableRequest to determine if a request can be upgraded.
+ ///
+ internal static string FormatCannotUpgradeNonUpgradableRequest()
+ => GetString("CannotUpgradeNonUpgradableRequest");
+
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);
diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/KeepAliveTimeoutTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/KeepAliveTimeoutTests.cs
index 707dd4f511..4e6d0f1304 100644
--- a/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/KeepAliveTimeoutTests.cs
+++ b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/KeepAliveTimeoutTests.cs
@@ -162,6 +162,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
await connection.Send(
"GET /upgrade HTTP/1.1",
"Host:",
+ "Connection: Upgrade",
"",
"");
await connection.Receive(
diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/ResponseTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/ResponseTests.cs
index 6b1d658aa8..39513f5002 100644
--- a/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/ResponseTests.cs
+++ b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/ResponseTests.cs
@@ -1775,6 +1775,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
await connection.Send(
"GET / HTTP/1.1",
"Host:",
+ "Connection: Upgrade",
"",
"");
await connection.ReceiveForcedEnd(
@@ -1789,7 +1790,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
await connection.Send(
"GET / HTTP/1.0",
- "Connection: keep-alive",
+ "Connection: keep-alive, Upgrade",
"",
"");
await connection.ReceiveForcedEnd(
diff --git a/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/UpgradeTests.cs b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/UpgradeTests.cs
index 1249524bc5..3696219f1c 100644
--- a/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/UpgradeTests.cs
+++ b/test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/UpgradeTests.cs
@@ -182,5 +182,41 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
"");
}
}
+
+ [Fact]
+ public async Task ThrowsWhenUpgradingNonUpgradableRequest()
+ {
+ var upgradeTcs = new TaskCompletionSource();
+ using (var server = new TestServer(async context =>
+ {
+ var feature = context.Features.Get();
+ Assert.False(feature.IsUpgradableRequest);
+ try
+ {
+ var stream = await feature.UpgradeAsync();
+ }
+ catch (Exception e)
+ {
+ upgradeTcs.TrySetException(e);
+ }
+ finally
+ {
+ upgradeTcs.TrySetResult(false);
+ }
+ }))
+ {
+ using (var connection = server.CreateConnection())
+ {
+ await connection.Send("GET / HTTP/1.1",
+ "Host:",
+ "",
+ "");
+ await connection.Receive("HTTP/1.1 200 OK");
+ }
+ }
+
+ var ex = await Assert.ThrowsAsync(async () => await upgradeTcs.Task).TimeoutAfter(TimeSpan.FromSeconds(15));
+ Assert.Equal(CoreStrings.CannotUpgradeNonUpgradableRequest, ex.Message);
+ }
}
}