From 4fd4fd9f4140b90734b179ea2fa9ddc9e89b0f69 Mon Sep 17 00:00:00 2001 From: "Chris Ross (ASP.NET)" Date: Mon, 22 Jan 2018 09:22:48 -0800 Subject: [PATCH] Mark HTTP/2 as not supported with an AppContext switch override. --- samples/Http2SampleApp/Program.cs | 4 +++- samples/Http2SampleApp/Startup.cs | 12 +++-------- src/Kestrel.Core/CoreStrings.resx | 3 +++ src/Kestrel.Core/ListenOptions.cs | 21 +++++++++++++++++-- .../Properties/CoreStrings.Designer.cs | 14 +++++++++++++ test/Kestrel.Core.Tests/ListenOptionsTests.cs | 11 ++++++++++ .../HttpProtocolSelectionTests.cs | 12 +++++++++-- 7 files changed, 63 insertions(+), 14 deletions(-) diff --git a/samples/Http2SampleApp/Program.cs b/samples/Http2SampleApp/Program.cs index bdb49b0a50..e95177afe5 100644 --- a/samples/Http2SampleApp/Program.cs +++ b/samples/Http2SampleApp/Program.cs @@ -1,4 +1,4 @@ -using System.Globalization; +using System; using System.IO; using System.Net; using Microsoft.AspNetCore.Hosting; @@ -13,6 +13,8 @@ namespace Http2SampleApp { public static void Main(string[] args) { + AppContext.SetSwitch("Switch.Microsoft.AspNetCore.Server.Kestrel.Experimental.Http2", isEnabled: true); + var hostBuilder = new WebHostBuilder() .ConfigureLogging((_, factory) => { diff --git a/samples/Http2SampleApp/Startup.cs b/samples/Http2SampleApp/Startup.cs index 6dce6b8a19..904e07cbb8 100644 --- a/samples/Http2SampleApp/Startup.cs +++ b/samples/Http2SampleApp/Startup.cs @@ -1,7 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; @@ -11,18 +7,16 @@ namespace Http2SampleApp { public class Startup { - // This method gets called by the runtime. Use this method to add services to the container. - // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 + public void ConfigureServices(IServiceCollection services) { } - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { - app.Run(async (context) => + app.Run(context => { - await context.Response.WriteAsync("Hello World!"); + return context.Response.WriteAsync("Hello World! " + context.Request.Protocol); }); } } diff --git a/src/Kestrel.Core/CoreStrings.resx b/src/Kestrel.Core/CoreStrings.resx index 2babfee801..e369a2d122 100644 --- a/src/Kestrel.Core/CoreStrings.resx +++ b/src/Kestrel.Core/CoreStrings.resx @@ -495,4 +495,7 @@ The endpoint {endpointName} specified multiple certificate sources. + + HTTP/2 support is experimental, see https://go.microsoft.com/fwlink/?linkid=866785 to enable it. + \ No newline at end of file diff --git a/src/Kestrel.Core/ListenOptions.cs b/src/Kestrel.Core/ListenOptions.cs index 08e728a35c..0ed675aac8 100644 --- a/src/Kestrel.Core/ListenOptions.cs +++ b/src/Kestrel.Core/ListenOptions.cs @@ -19,13 +19,18 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core /// public class ListenOptions : IEndPointInformation, IConnectionBuilder { + internal const string Http2ExperimentSwitch = "Switch.Microsoft.AspNetCore.Server.Kestrel.Experimental.Http2"; + private FileHandleType _handleType; + private HttpProtocols _protocols = HttpProtocols.Http1; + internal bool _isHttp2Supported; private readonly List> _components = new List>(); internal ListenOptions(IPEndPoint endPoint) { Type = ListenType.IPEndPoint; IPEndPoint = endPoint; + AppContext.TryGetSwitch(Http2ExperimentSwitch, out _isHttp2Supported); } internal ListenOptions(string socketPath) @@ -122,8 +127,20 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core /// /// The protocols enabled on this endpoint. /// - /// Defaults to HTTP/1.x only. - public HttpProtocols Protocols { get; set; } = HttpProtocols.Http1; + /// Defaults to HTTP/1.x only. HTTP/2 support is experimental, see + /// https://go.microsoft.com/fwlink/?linkid=866785 to enable it. + public HttpProtocols Protocols + { + get => _protocols; + set + { + if (!_isHttp2Supported && (value == HttpProtocols.Http1AndHttp2 || value == HttpProtocols.Http2)) + { + throw new NotSupportedException(CoreStrings.Http2NotSupported); + } + _protocols = value; + } + } /// /// Gets the that allows each connection diff --git a/src/Kestrel.Core/Properties/CoreStrings.Designer.cs b/src/Kestrel.Core/Properties/CoreStrings.Designer.cs index ff2732087f..1df67ddf4f 100644 --- a/src/Kestrel.Core/Properties/CoreStrings.Designer.cs +++ b/src/Kestrel.Core/Properties/CoreStrings.Designer.cs @@ -1774,6 +1774,20 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core internal static string FormatMultipleCertificateSources(object endpointName) => string.Format(CultureInfo.CurrentCulture, GetString("MultipleCertificateSources", "endpointName"), endpointName); + /// + /// HTTP/2 support is experimental, see https://go.microsoft.com/fwlink/?linkid=866785 to enable it. + /// + internal static string Http2NotSupported + { + get => GetString("Http2NotSupported"); + } + + /// + /// HTTP/2 support is experimental, see https://go.microsoft.com/fwlink/?linkid=866785 to enable it. + /// + internal static string FormatHttp2NotSupported() + => GetString("Http2NotSupported"); + private static string GetString(string name, params string[] formatterNames) { var value = _resourceManager.GetString(name); diff --git a/test/Kestrel.Core.Tests/ListenOptionsTests.cs b/test/Kestrel.Core.Tests/ListenOptionsTests.cs index 89d495876f..808d4e6cdd 100644 --- a/test/Kestrel.Core.Tests/ListenOptionsTests.cs +++ b/test/Kestrel.Core.Tests/ListenOptionsTests.cs @@ -1,6 +1,7 @@ // 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.Net; using Xunit; @@ -14,5 +15,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0)); Assert.Equal(HttpProtocols.Http1, listenOptions.Protocols); } + + [Fact] + public void Http2DisabledByDefault() + { + var listenOptions = new ListenOptions(new IPEndPoint(IPAddress.Loopback, 0)); + var ex = Assert.Throws(() => listenOptions.Protocols = HttpProtocols.Http1AndHttp2); + Assert.Equal(CoreStrings.Http2NotSupported, ex.Message); + ex = Assert.Throws(() => listenOptions.Protocols = HttpProtocols.Http2); + Assert.Equal(CoreStrings.Http2NotSupported, ex.Message); + } } } diff --git a/test/Kestrel.FunctionalTests/HttpProtocolSelectionTests.cs b/test/Kestrel.FunctionalTests/HttpProtocolSelectionTests.cs index c2dd742c1d..198ba040f3 100644 --- a/test/Kestrel.FunctionalTests/HttpProtocolSelectionTests.cs +++ b/test/Kestrel.FunctionalTests/HttpProtocolSelectionTests.cs @@ -48,7 +48,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests var builder = TransportSelector.GetWebHostBuilder() .UseKestrel(options => { - options.Listen(IPAddress.Loopback, 0, listenOptions => listenOptions.Protocols = serverProtocols); + options.Listen(IPAddress.Loopback, 0, listenOptions => + { + listenOptions._isHttp2Supported = true; + listenOptions.Protocols = serverProtocols; + }); }) .Configure(app => app.Run(context => Task.CompletedTask)); @@ -75,7 +79,11 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests var builder = TransportSelector.GetWebHostBuilder() .ConfigureLogging(loggingBuilder => loggingBuilder.AddProvider(loggerProvider.Object)) - .UseKestrel(options => options.Listen(IPAddress.Loopback, 0, listenOptions => listenOptions.Protocols = serverProtocols)) + .UseKestrel(options => options.Listen(IPAddress.Loopback, 0, listenOptions => + { + listenOptions._isHttp2Supported = true; + listenOptions.Protocols = serverProtocols; + })) .Configure(app => app.Run(context => Task.CompletedTask)); using (var host = builder.Build())