/// <summary> /// Configure Kestrel to use HTTPS. This does not use default certificates or other defaults specified via config or /// <see cref="KestrelServerOptions.ConfigureHttpsDefaults(Action{HttpsConnectionAdapterOptions})"/>. /// </summary> /// <param name="listenOptions">The <see cref="ListenOptions"/> to configure.</param> /// <param name="callbackOptions">Options for a per connection callback.</param> /// <returns>The <see cref="ListenOptions"/>.</returns> public static ListenOptions UseHttps(this ListenOptions listenOptions, TlsHandshakeCallbackOptions callbackOptions) { if (callbackOptions is null) { throw new ArgumentNullException(nameof(callbackOptions)); } if (callbackOptions.OnConnection is null) { throw new ArgumentException($"{nameof(TlsHandshakeCallbackOptions.OnConnection)} must not be null."); } var loggerFactory = listenOptions.KestrelServerOptions?.ApplicationServices.GetRequiredService <ILoggerFactory>() ?? NullLoggerFactory.Instance; listenOptions.IsTls = true; listenOptions.HttpsCallbackOptions = callbackOptions; listenOptions.Use(next => { // Set the list of protocols from listen options. // Set it inside Use delegate so Protocols and UseHttps can be called out of order. callbackOptions.HttpProtocols = listenOptions.Protocols; var middleware = new HttpsConnectionMiddleware(next, callbackOptions, loggerFactory); return(middleware.OnConnectionAsync); }); return(listenOptions); }
/// <summary> /// Configure Kestrel to use HTTPS. This does not use default certificates or other defaults specified via config or /// <see cref="KestrelServerOptions.ConfigureHttpsDefaults(Action{HttpsConnectionAdapterOptions})"/>. /// </summary> /// <param name="listenOptions">The <see cref="ListenOptions"/> to configure.</param> /// <param name="callbackOptions">Options for a per connection callback.</param> /// <returns>The <see cref="ListenOptions"/>.</returns> public static ListenOptions UseHttps(this ListenOptions listenOptions, TlsHandshakeCallbackOptions callbackOptions) { if (callbackOptions is null) { throw new ArgumentNullException(nameof(callbackOptions)); } if (callbackOptions.OnConnection is null) { throw new ArgumentException($"{nameof(TlsHandshakeCallbackOptions.OnConnection)} must not be null."); } if (listenOptions.Protocols.HasFlag(HttpProtocols.Http3)) { throw new NotSupportedException($"{nameof(UseHttps)} with {nameof(TlsHandshakeCallbackOptions)} is not supported with HTTP/3."); } var loggerFactory = listenOptions.KestrelServerOptions?.ApplicationServices.GetRequiredService <ILoggerFactory>() ?? NullLoggerFactory.Instance; listenOptions.IsTls = true; listenOptions.Use(next => { // Set the list of protocols from listen options callbackOptions.HttpProtocols = listenOptions.Protocols; var middleware = new HttpsConnectionMiddleware(next, callbackOptions, loggerFactory); return(middleware.OnConnectionAsync); }); return(listenOptions); }
// Adds endpoints from config to KestrelServerOptions.ConfigurationBackedListenOptions and configures some other options. // Any endpoints that were removed from the last time endpoints were loaded are returned. internal (List <ListenOptions>, List <ListenOptions>) Reload() { var endpointsToStop = Options.ConfigurationBackedListenOptions.ToList(); var endpointsToStart = new List <ListenOptions>(); Options.ConfigurationBackedListenOptions.Clear(); DefaultCertificateConfig = null; ConfigurationReader = new ConfigurationReader(Configuration); LoadDefaultCert(); foreach (var endpoint in ConfigurationReader.Endpoints) { var listenOptions = AddressBinder.ParseAddress(endpoint.Url, out var https); if (!https) { ConfigurationReader.ThrowIfContainsHttpsOnlyConfiguration(endpoint); } Options.ApplyEndpointDefaults(listenOptions); if (endpoint.Protocols.HasValue) { listenOptions.Protocols = endpoint.Protocols.Value; } else { // Ensure endpoint is reloaded if it used the default protocol and the protocol changed. // listenOptions.Protocols should already be set to this by ApplyEndpointDefaults. endpoint.Protocols = ConfigurationReader.EndpointDefaults.Protocols; } // Compare to UseHttps(httpsOptions => { }) var httpsOptions = new HttpsConnectionAdapterOptions(); if (https) { // Defaults Options.ApplyHttpsDefaults(httpsOptions); if (endpoint.SslProtocols.HasValue) { httpsOptions.SslProtocols = endpoint.SslProtocols.Value; } else { // Ensure endpoint is reloaded if it used the default protocol and the SslProtocols changed. endpoint.SslProtocols = ConfigurationReader.EndpointDefaults.SslProtocols; } if (endpoint.ClientCertificateMode.HasValue) { httpsOptions.ClientCertificateMode = endpoint.ClientCertificateMode.Value; } else { // Ensure endpoint is reloaded if it used the default mode and the ClientCertificateMode changed. endpoint.ClientCertificateMode = ConfigurationReader.EndpointDefaults.ClientCertificateMode; } // A cert specified directly on the endpoint overrides any defaults. httpsOptions.ServerCertificate = CertificateConfigLoader.LoadCertificate(endpoint.Certificate, endpoint.Name) ?? httpsOptions.ServerCertificate; if (httpsOptions.ServerCertificate == null && httpsOptions.ServerCertificateSelector == null) { // Fallback Options.ApplyDefaultCert(httpsOptions); // Ensure endpoint is reloaded if it used the default certificate and the certificate changed. endpoint.Certificate = DefaultCertificateConfig; } } // Now that defaults have been loaded, we can compare to the currently bound endpoints to see if the config changed. // There's no reason to rerun an EndpointConfigurations callback if nothing changed. var matchingBoundEndpoints = endpointsToStop.Where(o => o.EndpointConfig == endpoint).ToList(); if (matchingBoundEndpoints.Count > 0) { endpointsToStop.RemoveAll(o => o.EndpointConfig == endpoint); Options.ConfigurationBackedListenOptions.AddRange(matchingBoundEndpoints); continue; } if (EndpointConfigurations.TryGetValue(endpoint.Name, out var configureEndpoint)) { var endpointConfig = new EndpointConfiguration(https, listenOptions, httpsOptions, endpoint.ConfigSection); configureEndpoint(endpointConfig); } // EndpointDefaults or configureEndpoint may have added an https adapter. if (https && !listenOptions.IsTls) { if (endpoint.Sni.Count == 0) { if (httpsOptions.ServerCertificate == null && httpsOptions.ServerCertificateSelector == null) { throw new InvalidOperationException(CoreStrings.NoCertSpecifiedNoDevelopmentCertificateFound); } listenOptions.UseHttps(httpsOptions); } else { var sniOptionsSelector = new SniOptionsSelector(endpoint.Name, endpoint.Sni, CertificateConfigLoader, httpsOptions, listenOptions.Protocols, HttpsLogger); var tlsCallbackOptions = new TlsHandshakeCallbackOptions() { OnConnection = SniOptionsSelector.OptionsCallback, HandshakeTimeout = httpsOptions.HandshakeTimeout, OnConnectionState = sniOptionsSelector, }; listenOptions.UseHttps(tlsCallbackOptions); } } listenOptions.EndpointConfig = endpoint; endpointsToStart.Add(listenOptions); Options.ConfigurationBackedListenOptions.Add(listenOptions); } return(endpointsToStop, endpointsToStart); }