diff --git a/src/Servers/Kestrel/Core/src/HttpsConnectionAdapterOptions.cs b/src/Servers/Kestrel/Core/src/HttpsConnectionAdapterOptions.cs
index 1605d0a9ec9a..8435bfa998a6 100644
--- a/src/Servers/Kestrel/Core/src/HttpsConnectionAdapterOptions.cs
+++ b/src/Servers/Kestrel/Core/src/HttpsConnectionAdapterOptions.cs
@@ -72,6 +72,12 @@ public HttpsConnectionAdapterOptions()
///
public SslProtocols SslProtocols { get; set; }
+ ///
+ /// The protocols enabled on this endpoint.
+ ///
+ /// Defaults to HTTP/1.x only.
+ internal HttpProtocols HttpProtocols { get; set; }
+
///
/// Specifies whether the certificate revocation list is checked during authentication.
///
diff --git a/src/Servers/Kestrel/Core/src/Internal/Infrastructure/TransportManager.cs b/src/Servers/Kestrel/Core/src/Internal/Infrastructure/TransportManager.cs
index 87d2869f52e1..3760612366e5 100644
--- a/src/Servers/Kestrel/Core/src/Internal/Infrastructure/TransportManager.cs
+++ b/src/Servers/Kestrel/Core/src/Internal/Infrastructure/TransportManager.cs
@@ -77,7 +77,7 @@ public async Task BindAsync(EndPoint endPoint, MultiplexedConnectionDe
// The QUIC transport will check if TlsConnectionCallbackOptions is missing.
if (listenOptions.HttpsOptions != null)
{
- var sslServerAuthenticationOptions = HttpsConnectionMiddleware.CreateHttp3Options(listenOptions.HttpsOptions.Value);
+ var sslServerAuthenticationOptions = HttpsConnectionMiddleware.CreateHttp3Options(listenOptions.HttpsOptions);
features.Set(new TlsConnectionCallbackOptions
{
ApplicationProtocols = sslServerAuthenticationOptions.ApplicationProtocols ?? new List { SslApplicationProtocol.Http3 },
diff --git a/src/Servers/Kestrel/Core/src/ListenOptions.cs b/src/Servers/Kestrel/Core/src/ListenOptions.cs
index dba524d66aaf..b8aaf3c75d72 100644
--- a/src/Servers/Kestrel/Core/src/ListenOptions.cs
+++ b/src/Servers/Kestrel/Core/src/ListenOptions.cs
@@ -140,8 +140,7 @@ internal string Scheme
}
internal bool IsTls { get; set; }
- /// Should not be inspected until the configuration has been loaded.
- internal Lazy? HttpsOptions { get; set; }
+ internal HttpsConnectionAdapterOptions? HttpsOptions { get; set; }
internal TlsHandshakeCallbackOptions? HttpsCallbackOptions { get; set; }
///
diff --git a/src/Servers/Kestrel/Core/src/ListenOptionsHttpsExtensions.cs b/src/Servers/Kestrel/Core/src/ListenOptionsHttpsExtensions.cs
index 7f2744146ce8..175b8bed8163 100644
--- a/src/Servers/Kestrel/Core/src/ListenOptionsHttpsExtensions.cs
+++ b/src/Servers/Kestrel/Core/src/ListenOptionsHttpsExtensions.cs
@@ -163,24 +163,33 @@ public static ListenOptions UseHttps(this ListenOptions listenOptions, Action(() =>
+ var options = new HttpsConnectionAdapterOptions();
+ listenOptions.KestrelServerOptions.ApplyHttpsDefaults(options);
+ configureOptions(options);
+ listenOptions.KestrelServerOptions.ApplyDefaultCert(options);
+
+ if (options.ServerCertificate == null && options.ServerCertificateSelector == null)
{
- // We defer configuration of the https options until build time so that the IConfiguration will be available.
- // This is particularly important in docker containers, where the docker tools use IConfiguration to tell
- // us where the development certificates have been mounted.
+ throw new InvalidOperationException(CoreStrings.NoCertSpecifiedNoDevelopmentCertificateFound);
+ }
- var options = new HttpsConnectionAdapterOptions();
- listenOptions.KestrelServerOptions.ApplyHttpsDefaults(options);
- configureOptions(options);
- listenOptions.KestrelServerOptions.ApplyDefaultCert(options);
+ return listenOptions.UseHttps(options);
+ }
- if (options.ServerCertificate == null && options.ServerCertificateSelector == null)
- {
- throw new InvalidOperationException(CoreStrings.NoCertSpecifiedNoDevelopmentCertificateFound);
- }
+ // Use Https if a default cert is available
+ internal static bool TryUseHttps(this ListenOptions listenOptions)
+ {
+ var options = new HttpsConnectionAdapterOptions();
+ listenOptions.KestrelServerOptions.ApplyHttpsDefaults(options);
+ listenOptions.KestrelServerOptions.ApplyDefaultCert(options);
- return options;
- }));
+ if (options.ServerCertificate == null && options.ServerCertificateSelector == null)
+ {
+ return false;
+ }
+
+ listenOptions.UseHttps(options);
+ return true;
}
///
@@ -192,28 +201,16 @@ public static ListenOptions UseHttps(this ListenOptions listenOptions, ActionThe .
public static ListenOptions UseHttps(this ListenOptions listenOptions, HttpsConnectionAdapterOptions httpsOptions)
{
- return listenOptions.UseHttps(new Lazy(httpsOptions));
- }
+ var loggerFactory = listenOptions.KestrelServerOptions?.ApplicationServices.GetRequiredService() ?? NullLoggerFactory.Instance;
- ///
- /// Configure Kestrel to use HTTPS. This does not use default certificates or other defaults specified via config or
- /// .
- ///
- /// The to configure.
- /// Options to configure HTTPS.
- /// The .
- private static ListenOptions UseHttps(this ListenOptions listenOptions, Lazy lazyHttpsOptions)
- {
listenOptions.IsTls = true;
- listenOptions.HttpsOptions = lazyHttpsOptions;
+ listenOptions.HttpsOptions = httpsOptions;
- // NB: This lambda will only be invoked if either HTTP/1.* or HTTP/2 is being used
listenOptions.Use(next =>
{
- // Evaluate the HttpsConnectionAdapterOptions, now that the configuration, if any, has been loaded
- var httpsOptions = listenOptions.HttpsOptions.Value;
- var loggerFactory = listenOptions.KestrelServerOptions?.ApplicationServices.GetRequiredService() ?? NullLoggerFactory.Instance;
- var middleware = new HttpsConnectionMiddleware(next, httpsOptions, listenOptions.Protocols, loggerFactory);
+ // Set the list of protocols from listen options
+ httpsOptions.HttpProtocols = listenOptions.Protocols;
+ var middleware = new HttpsConnectionMiddleware(next, httpsOptions, loggerFactory);
return middleware.OnConnectionAsync;
});
@@ -273,7 +270,6 @@ public static ListenOptions UseHttps(this ListenOptions listenOptions, TlsHandsh
listenOptions.IsTls = true;
listenOptions.HttpsCallbackOptions = callbackOptions;
- // NB: This lambda will only be invoked if either HTTP/1.* or HTTP/2 is being used
listenOptions.Use(next =>
{
// Set the list of protocols from listen options.
diff --git a/src/Servers/Kestrel/Core/src/Middleware/HttpsConnectionMiddleware.cs b/src/Servers/Kestrel/Core/src/Middleware/HttpsConnectionMiddleware.cs
index 901818c2e164..16abe279d3e8 100644
--- a/src/Servers/Kestrel/Core/src/Middleware/HttpsConnectionMiddleware.cs
+++ b/src/Servers/Kestrel/Core/src/Middleware/HttpsConnectionMiddleware.cs
@@ -33,9 +33,6 @@ internal sealed class HttpsConnectionMiddleware
private readonly ILogger _logger;
private readonly Func _sslStreamFactory;
- // Internal for testing
- internal readonly HttpProtocols _httpProtocols;
-
// The following fields are only set by HttpsConnectionAdapterOptions ctor.
private readonly HttpsConnectionAdapterOptions? _options;
private readonly SslStreamCertificateContext? _serverCertificateContext;
@@ -45,16 +42,17 @@ internal sealed class HttpsConnectionMiddleware
// The following fields are only set by TlsHandshakeCallbackOptions ctor.
private readonly Func>? _tlsCallbackOptions;
private readonly object? _tlsCallbackOptionsState;
+ private readonly HttpProtocols _httpProtocols;
// Pool for cancellation tokens that cancel the handshake
private readonly CancellationTokenSourcePool _ctsPool = new();
- public HttpsConnectionMiddleware(ConnectionDelegate next, HttpsConnectionAdapterOptions options, HttpProtocols httpProtocols)
- : this(next, options, httpProtocols, loggerFactory: NullLoggerFactory.Instance)
+ public HttpsConnectionMiddleware(ConnectionDelegate next, HttpsConnectionAdapterOptions options)
+ : this(next, options, loggerFactory: NullLoggerFactory.Instance)
{
}
- public HttpsConnectionMiddleware(ConnectionDelegate next, HttpsConnectionAdapterOptions options, HttpProtocols httpProtocols, ILoggerFactory loggerFactory)
+ public HttpsConnectionMiddleware(ConnectionDelegate next, HttpsConnectionAdapterOptions options, ILoggerFactory loggerFactory)
{
ArgumentNullException.ThrowIfNull(options);
@@ -74,7 +72,7 @@ public HttpsConnectionMiddleware(ConnectionDelegate next, HttpsConnectionAdapter
//_sslStreamFactory = s => new SslStream(s);
_options = options;
- _httpProtocols = ValidateAndNormalizeHttpProtocols(httpProtocols, _logger);
+ _options.HttpProtocols = ValidateAndNormalizeHttpProtocols(_options.HttpProtocols, _logger);
// capture the certificate now so it can't be switched after validation
_serverCertificate = options.ServerCertificate;
@@ -322,7 +320,7 @@ private Task DoOptionsBasedHandshakeAsync(ConnectionContext context, SslStream s
CertificateRevocationCheckMode = _options.CheckCertificateRevocation ? X509RevocationMode.Online : X509RevocationMode.NoCheck,
};
- ConfigureAlpn(sslOptions, _httpProtocols);
+ ConfigureAlpn(sslOptions, _options.HttpProtocols);
_options.OnAuthenticate?.Invoke(context, sslOptions);
diff --git a/src/Servers/Kestrel/Kestrel/test/KestrelConfigurationLoaderTests.cs b/src/Servers/Kestrel/Kestrel/test/KestrelConfigurationLoaderTests.cs
index 53b707743058..36683b4c81c0 100644
--- a/src/Servers/Kestrel/Kestrel/test/KestrelConfigurationLoaderTests.cs
+++ b/src/Servers/Kestrel/Kestrel/test/KestrelConfigurationLoaderTests.cs
@@ -252,104 +252,6 @@ public void ConfigureEndpointDevelopmentCertificateGetsLoadedWhenPresent()
}
}
- [Fact]
- public void LoadDevelopmentCertificate_ConfigureFirst()
- {
- try
- {
- var serverOptions = CreateServerOptions();
- var certificate = new X509Certificate2(TestResources.GetCertPath("aspnetdevcert.pfx"), "testPassword", X509KeyStorageFlags.Exportable);
- var bytes = certificate.Export(X509ContentType.Pkcs12, "1234");
- var path = GetCertificatePath();
- Directory.CreateDirectory(Path.GetDirectoryName(path));
- File.WriteAllBytes(path, bytes);
-
- var config = new ConfigurationBuilder().AddInMemoryCollection(new[]
- {
- new KeyValuePair("Certificates:Development:Password", "1234"),
- }).Build();
-
- serverOptions.Configure(config);
-
- Assert.Null(serverOptions.DefaultCertificate);
-
- serverOptions.ConfigurationLoader.Load();
-
- Assert.NotNull(serverOptions.DefaultCertificate);
- Assert.Equal(serverOptions.DefaultCertificate.SerialNumber, certificate.SerialNumber);
-
- var ran1 = false;
- serverOptions.ListenAnyIP(4545, listenOptions =>
- {
- ran1 = true;
- listenOptions.UseHttps();
- });
- Assert.True(ran1);
-
- var listenOptions = serverOptions.CodeBackedListenOptions.Single();
- Assert.False(listenOptions.HttpsOptions.IsValueCreated);
- listenOptions.Build();
- Assert.True(listenOptions.HttpsOptions.IsValueCreated);
- Assert.Equal(listenOptions.HttpsOptions.Value.ServerCertificate?.SerialNumber, certificate.SerialNumber);
- }
- finally
- {
- if (File.Exists(GetCertificatePath()))
- {
- File.Delete(GetCertificatePath());
- }
- }
- }
-
- [Fact]
- public void LoadDevelopmentCertificate_UseHttpsFirst()
- {
- try
- {
- var serverOptions = CreateServerOptions();
- var certificate = new X509Certificate2(TestResources.GetCertPath("aspnetdevcert.pfx"), "testPassword", X509KeyStorageFlags.Exportable);
- var bytes = certificate.Export(X509ContentType.Pkcs12, "1234");
- var path = GetCertificatePath();
- Directory.CreateDirectory(Path.GetDirectoryName(path));
- File.WriteAllBytes(path, bytes);
-
- var ran1 = false;
- serverOptions.ListenAnyIP(4545, listenOptions =>
- {
- ran1 = true;
- listenOptions.UseHttps();
- });
- Assert.True(ran1);
-
- var config = new ConfigurationBuilder().AddInMemoryCollection(new[]
- {
- new KeyValuePair("Certificates:Development:Password", "1234"),
- }).Build();
-
- serverOptions.Configure(config);
-
- Assert.Null(serverOptions.DefaultCertificate);
-
- serverOptions.ConfigurationLoader.Load();
-
- Assert.NotNull(serverOptions.DefaultCertificate);
- Assert.Equal(serverOptions.DefaultCertificate.SerialNumber, certificate.SerialNumber);
-
- var listenOptions = serverOptions.CodeBackedListenOptions.Single();
- Assert.False(listenOptions.HttpsOptions.IsValueCreated);
- listenOptions.Build();
- Assert.True(listenOptions.HttpsOptions.IsValueCreated);
- Assert.Equal(listenOptions.HttpsOptions.Value.ServerCertificate?.SerialNumber, certificate.SerialNumber);
- }
- finally
- {
- if (File.Exists(GetCertificatePath()))
- {
- File.Delete(GetCertificatePath());
- }
- }
- }
-
[Fact]
public void ConfigureEndpoint_ThrowsWhen_The_PasswordIsMissing()
{
@@ -828,8 +730,6 @@ public void EndpointConfigureSection_CanSetSslProtocol()
});
});
- _ = serverOptions.CodeBackedListenOptions.Single().HttpsOptions.Value; // Force evaluation
-
Assert.True(ranDefault);
Assert.True(ran1);
Assert.True(ran2);
@@ -965,8 +865,6 @@ public void EndpointConfigureSection_CanSetClientCertificateMode()
});
});
- _ = serverOptions.CodeBackedListenOptions.Single().HttpsOptions.Value; // Force evaluation
-
Assert.True(ranDefault);
Assert.True(ran1);
Assert.True(ran2);
diff --git a/src/Servers/Kestrel/test/InMemory.FunctionalTests/HttpsConnectionMiddlewareTests.cs b/src/Servers/Kestrel/test/InMemory.FunctionalTests/HttpsConnectionMiddlewareTests.cs
index cdb836028951..68929b4f167a 100644
--- a/src/Servers/Kestrel/test/InMemory.FunctionalTests/HttpsConnectionMiddlewareTests.cs
+++ b/src/Servers/Kestrel/test/InMemory.FunctionalTests/HttpsConnectionMiddlewareTests.cs
@@ -228,8 +228,7 @@ void ConfigureListenOptions(ListenOptions listenOptions)
public void ThrowsWhenNoServerCertificateIsProvided()
{
Assert.Throws(() => new HttpsConnectionMiddleware(context => Task.CompletedTask,
- new HttpsConnectionAdapterOptions(),
- ListenOptions.DefaultHttpProtocols)
+ new HttpsConnectionAdapterOptions())
);
}
@@ -1269,8 +1268,7 @@ public void AcceptsCertificateWithoutExtensions(string testCertName)
new HttpsConnectionMiddleware(context => Task.CompletedTask, new HttpsConnectionAdapterOptions
{
ServerCertificate = cert,
- },
- ListenOptions.DefaultHttpProtocols);
+ });
}
[Theory]
@@ -1288,8 +1286,7 @@ public void ValidatesEnhancedKeyUsageOnCertificate(string testCertName)
new HttpsConnectionMiddleware(context => Task.CompletedTask, new HttpsConnectionAdapterOptions
{
ServerCertificate = cert,
- },
- ListenOptions.DefaultHttpProtocols);
+ });
}
[Theory]
@@ -1308,7 +1305,7 @@ public void ThrowsForCertificatesMissingServerEku(string testCertName)
new HttpsConnectionMiddleware(context => Task.CompletedTask, new HttpsConnectionAdapterOptions
{
ServerCertificate = cert,
- }, ListenOptions.DefaultHttpProtocols));
+ }));
Assert.Equal(CoreStrings.FormatInvalidServerCertificateEku(cert.Thumbprint), ex.Message);
}
@@ -1355,10 +1352,11 @@ public void Http1AndHttp2DowngradeToHttp1ForHttpsOnIncompatibleWindowsVersions()
var httpConnectionAdapterOptions = new HttpsConnectionAdapterOptions
{
ServerCertificate = _x509Certificate2,
+ HttpProtocols = HttpProtocols.Http1AndHttp2
};
- var middleware = new HttpsConnectionMiddleware(context => Task.CompletedTask, httpConnectionAdapterOptions, HttpProtocols.Http1AndHttp2);
+ new HttpsConnectionMiddleware(context => Task.CompletedTask, httpConnectionAdapterOptions);
- Assert.Equal(HttpProtocols.Http1, middleware._httpProtocols);
+ Assert.Equal(HttpProtocols.Http1, httpConnectionAdapterOptions.HttpProtocols);
}
[ConditionalFact]
@@ -1369,10 +1367,11 @@ public void Http1AndHttp2DoesNotDowngradeOnCompatibleWindowsVersions()
var httpConnectionAdapterOptions = new HttpsConnectionAdapterOptions
{
ServerCertificate = _x509Certificate2,
+ HttpProtocols = HttpProtocols.Http1AndHttp2
};
- var middleware = new HttpsConnectionMiddleware(context => Task.CompletedTask, httpConnectionAdapterOptions, HttpProtocols.Http1AndHttp2);
+ new HttpsConnectionMiddleware(context => Task.CompletedTask, httpConnectionAdapterOptions);
- Assert.Equal(HttpProtocols.Http1AndHttp2, middleware._httpProtocols);
+ Assert.Equal(HttpProtocols.Http1AndHttp2, httpConnectionAdapterOptions.HttpProtocols);
}
[ConditionalFact]
@@ -1383,9 +1382,10 @@ public void Http2ThrowsOnIncompatibleWindowsVersions()
var httpConnectionAdapterOptions = new HttpsConnectionAdapterOptions
{
ServerCertificate = _x509Certificate2,
+ HttpProtocols = HttpProtocols.Http2
};
- Assert.Throws(() => new HttpsConnectionMiddleware(context => Task.CompletedTask, httpConnectionAdapterOptions, HttpProtocols.Http2));
+ Assert.Throws(() => new HttpsConnectionMiddleware(context => Task.CompletedTask, httpConnectionAdapterOptions));
}
[ConditionalFact]
@@ -1396,10 +1396,11 @@ public void Http2DoesNotThrowOnCompatibleWindowsVersions()
var httpConnectionAdapterOptions = new HttpsConnectionAdapterOptions
{
ServerCertificate = _x509Certificate2,
+ HttpProtocols = HttpProtocols.Http2
};
// Does not throw
- new HttpsConnectionMiddleware(context => Task.CompletedTask, httpConnectionAdapterOptions, HttpProtocols.Http2);
+ new HttpsConnectionMiddleware(context => Task.CompletedTask, httpConnectionAdapterOptions);
}
private static async Task App(HttpContext httpContext)
diff --git a/src/Servers/Kestrel/test/InMemory.FunctionalTests/HttpsTests.cs b/src/Servers/Kestrel/test/InMemory.FunctionalTests/HttpsTests.cs
index fb599ec4870f..c78429ea1e9a 100644
--- a/src/Servers/Kestrel/test/InMemory.FunctionalTests/HttpsTests.cs
+++ b/src/Servers/Kestrel/test/InMemory.FunctionalTests/HttpsTests.cs
@@ -53,18 +53,14 @@ public void UseHttpsDefaultsToDefaultCert()
Assert.False(serverOptions.IsDevCertLoaded);
- var ranUseHttpsAction = false;
serverOptions.ListenLocalhost(5001, options =>
{
options.UseHttps(opt =>
{
// The default cert is applied after UseHttps.
Assert.Null(opt.ServerCertificate);
- ranUseHttpsAction = true;
});
});
- _ = serverOptions.CodeBackedListenOptions[1].HttpsOptions.Value; // Force evaluation
- Assert.True(ranUseHttpsAction);
Assert.False(serverOptions.IsDevCertLoaded);
}
@@ -110,20 +106,14 @@ public void ConfigureHttpsDefaultsNeverLoadsDefaultCert()
options.ServerCertificate = _x509Certificate2;
options.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
});
- var ranUseHttpsAction = false;
serverOptions.ListenLocalhost(5000, options =>
{
options.UseHttps(opt =>
{
Assert.Equal(_x509Certificate2, opt.ServerCertificate);
Assert.Equal(ClientCertificateMode.RequireCertificate, opt.ClientCertificateMode);
- ranUseHttpsAction = true;
});
});
-
- _ = serverOptions.CodeBackedListenOptions.Single().HttpsOptions.Value; // Force evaluation
- Assert.True(ranUseHttpsAction);
-
// Never lazy loaded
Assert.False(serverOptions.IsDevCertLoaded);
Assert.Null(serverOptions.DefaultCertificate);
@@ -143,7 +133,6 @@ public void ConfigureCertSelectorNeverLoadsDefaultCert()
};
options.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
});
- var ranUseHttpsAction = false;
serverOptions.ListenLocalhost(5000, options =>
{
options.UseHttps(opt =>
@@ -151,13 +140,8 @@ public void ConfigureCertSelectorNeverLoadsDefaultCert()
Assert.Null(opt.ServerCertificate);
Assert.NotNull(opt.ServerCertificateSelector);
Assert.Equal(ClientCertificateMode.RequireCertificate, opt.ClientCertificateMode);
- ranUseHttpsAction = true;
});
});
-
- _ = serverOptions.CodeBackedListenOptions.Single().HttpsOptions.Value; // Force evaluation
- Assert.True(ranUseHttpsAction);
-
// Never lazy loaded
Assert.False(serverOptions.IsDevCertLoaded);
Assert.Null(serverOptions.DefaultCertificate);
diff --git a/src/Servers/Kestrel/test/Interop.FunctionalTests/Http3/Http3TlsTests.cs b/src/Servers/Kestrel/test/Interop.FunctionalTests/Http3/Http3TlsTests.cs
index f3fb01d8e725..6226bf1660c0 100644
--- a/src/Servers/Kestrel/test/Interop.FunctionalTests/Http3/Http3TlsTests.cs
+++ b/src/Servers/Kestrel/test/Interop.FunctionalTests/Http3/Http3TlsTests.cs
@@ -5,14 +5,11 @@
using System.Net.Http;
using System.Net.Quic;
using System.Net.Security;
-using System.Reflection;
-using System.Security.Cryptography.X509Certificates;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Server.Kestrel.Https;
using Microsoft.AspNetCore.Testing;
-using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Xunit;
@@ -26,7 +23,6 @@ public class Http3TlsTests : LoggedTest
[MsQuicSupported]
public async Task ServerCertificateSelector_Invoked()
{
- var serverCertificateSelectorActionCalled = false;
var builder = CreateHostBuilder(async context =>
{
await context.Response.WriteAsync("Hello World");
@@ -39,7 +35,6 @@ public async Task ServerCertificateSelector_Invoked()
{
httpsOptions.ServerCertificateSelector = (context, host) =>
{
- serverCertificateSelectorActionCalled = true;
Assert.Null(context); // The context isn't available durring the quic handshake.
Assert.Equal("testhost", host);
return TestResources.GetTestCertificate();
@@ -64,8 +59,6 @@ public async Task ServerCertificateSelector_Invoked()
Assert.Equal(HttpVersion.Version30, response.Version);
Assert.Equal("Hello World", result);
- Assert.True(serverCertificateSelectorActionCalled);
-
await host.StopAsync().DefaultTimeout();
}
@@ -336,93 +329,6 @@ public async Task TlsHandshakeCallbackOptions_Invoked()
await host.StopAsync().DefaultTimeout();
}
- [ConditionalFact]
- [MsQuicSupported]
- public async Task LoadDevelopmentCertificateViaConfiguration()
- {
- var expectedCertificate = new X509Certificate2(TestResources.GetCertPath("aspnetdevcert.pfx"), "testPassword", X509KeyStorageFlags.Exportable);
- var bytes = expectedCertificate.Export(X509ContentType.Pkcs12, "1234");
- var path = GetCertificatePath();
- Directory.CreateDirectory(Path.GetDirectoryName(path));
- File.WriteAllBytes(path, bytes);
-
- var config = new ConfigurationBuilder().AddInMemoryCollection(new[]
- {
- new KeyValuePair("Certificates:Development:Password", "1234"),
- }).Build();
-
- var ranConfigureKestrelAction = false;
- var ranUseHttpsAction = false;
- var hostBuilder = CreateHostBuilder(async context =>
- {
- await context.Response.WriteAsync("Hello World");
- }, configureKestrel: kestrelOptions =>
- {
- ranConfigureKestrelAction = true;
- kestrelOptions.Configure(config);
-
- kestrelOptions.ListenAnyIP(0, listenOptions =>
- {
- listenOptions.Protocols = HttpProtocols.Http3;
- listenOptions.UseHttps(_ =>
- {
- ranUseHttpsAction = true;
- });
- });
- });
-
- Assert.False(ranConfigureKestrelAction);
- Assert.False(ranUseHttpsAction);
-
- using var host = hostBuilder.Build();
- await host.StartAsync().DefaultTimeout();
-
- Assert.True(ranConfigureKestrelAction);
- Assert.True(ranUseHttpsAction);
-
- var request = new HttpRequestMessage(HttpMethod.Get, $"https://127.0.0.1:{host.GetPort()}/");
- request.Version = HttpVersion.Version30;
- request.VersionPolicy = HttpVersionPolicy.RequestVersionExact;
- request.Headers.Host = "testhost";
-
- var ranCertificateValidation = false;
- var httpHandler = new SocketsHttpHandler();
- httpHandler.SslOptions = new SslClientAuthenticationOptions
- {
- RemoteCertificateValidationCallback = (object _sender, X509Certificate actualCertificate, X509Chain _chain, SslPolicyErrors _sslPolicyErrors) =>
- {
- ranCertificateValidation = true;
- Assert.Equal(expectedCertificate.GetSerialNumberString(), actualCertificate.GetSerialNumberString());
- return true;
- },
- TargetHost = "targethost",
- };
- using var client = new HttpMessageInvoker(httpHandler);
-
- var response = await client.SendAsync(request, CancellationToken.None).DefaultTimeout();
- response.EnsureSuccessStatusCode();
- var result = await response.Content.ReadAsStringAsync();
- Assert.Equal(HttpVersion.Version30, response.Version);
- Assert.Equal("Hello World", result);
-
- Assert.True(ranCertificateValidation);
-
- await host.StopAsync().DefaultTimeout();
- }
-
- ///
- /// This is something of a hack - we should actually be calling
- /// .
- ///
- private static string GetCertificatePath()
- {
- var appData = Environment.GetEnvironmentVariable("APPDATA");
- var home = Environment.GetEnvironmentVariable("HOME");
- var basePath = appData != null ? Path.Combine(appData, "ASP.NET", "https") : null;
- basePath = basePath ?? (home != null ? Path.Combine(home, ".aspnet", "https") : null);
- return Path.Combine(basePath, $"{typeof(Http3TlsTests).Assembly.GetName().Name}.pfx");
- }
-
private IHostBuilder CreateHostBuilder(RequestDelegate requestDelegate, HttpProtocols? protocol = null, Action configureKestrel = null)
{
return HttpHelpers.CreateHostBuilder(AddTestLogging, requestDelegate, protocol, configureKestrel);