// 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.Collections.Generic;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Server.Kestrel.Https;
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
using Microsoft.Extensions.Configuration;
namespace Microsoft.AspNetCore.Server.Kestrel.Core
{
///
/// Provides programmatic configuration of Kestrel-specific features.
///
public class KestrelServerOptions
{
///
/// Configures the endpoints that Kestrel should listen to.
///
///
/// If this list is empty, the server.urls setting (e.g. UseUrls) is used.
///
internal List ListenOptions { get; } = new List();
///
/// Gets or sets whether the Server header should be included in each response.
///
///
/// Defaults to true.
///
public bool AddServerHeader { get; set; } = true;
///
/// Gets or sets a value that determines how Kestrel should schedule user callbacks.
///
/// The default mode is
public SchedulingMode ApplicationSchedulingMode { get; set; } = SchedulingMode.Default;
///
/// Gets or sets a value that controls whether synchronous IO is allowed for the and
///
///
/// Defaults to true.
///
public bool AllowSynchronousIO { get; set; } = true;
///
/// Enables the Listen options callback to resolve and use services registered by the application during startup.
/// Typically initialized by UseKestrel()"/>.
///
public IServiceProvider ApplicationServices { get; set; }
///
/// Provides access to request limit options.
///
public KestrelServerLimits Limits { get; } = new KestrelServerLimits();
///
/// Provides a configuration source where endpoints will be loaded from on server start.
/// The default is null.
///
public KestrelConfigurationLoader ConfigurationLoader { get; set; }
///
/// A default configuration action for all endpoints. Use for Listen, configuration, the default url, and URLs.
///
private Action EndpointDefaults { get; set; } = _ => { };
///
/// A default configuration action for all https endpoints.
///
private Action HttpsDefaults { get; set; } = _ => { };
///
/// The default server certificate for https endpoints. This is applied before HttpsDefaults.
///
internal X509Certificate2 DefaultCertificate { get; set; }
///
/// Specifies a configuration Action to run for each newly created endpoint. Calling this again will replace
/// the prior action.
///
public void ConfigureEndpointDefaults(Action configureOptions)
{
EndpointDefaults = configureOptions ?? throw new ArgumentNullException(nameof(configureOptions));
}
internal void ApplyEndpointDefaults(ListenOptions listenOptions)
{
listenOptions.KestrelServerOptions = this;
EndpointDefaults(listenOptions);
}
///
/// Specifies a configuration Action to run for each newly created https endpoint. Calling this again will replace
/// the prior action.
///
public void ConfigureHttpsDefaults(Action configureOptions)
{
HttpsDefaults = configureOptions ?? throw new ArgumentNullException(nameof(configureOptions));
}
internal void ApplyHttpsDefaults(HttpsConnectionAdapterOptions httpsOptions)
{
httpsOptions.ServerCertificate = DefaultCertificate;
HttpsDefaults(httpsOptions);
}
///
/// Creates a configuration loader for setting up Kestrel.
///
public KestrelConfigurationLoader Configure()
{
var loader = new KestrelConfigurationLoader(this, new ConfigurationBuilder().Build());
ConfigurationLoader = loader;
return loader;
}
///
/// Creates a configuration loader for setting up Kestrel that takes an IConfiguration as input.
/// This configuration must be scoped to the configuration section for Kestrel.
///
public KestrelConfigurationLoader Configure(IConfiguration config)
{
var loader = new KestrelConfigurationLoader(this, config);
ConfigurationLoader = loader;
return loader;
}
///
/// Bind to given IP address and port.
///
public void Listen(IPAddress address, int port)
{
Listen(address, port, _ => { });
}
///
/// Bind to given IP address and port.
/// The callback configures endpoint-specific settings.
///
public void Listen(IPAddress address, int port, Action configure)
{
if (address == null)
{
throw new ArgumentNullException(nameof(address));
}
Listen(new IPEndPoint(address, port), configure);
}
///
/// Bind to given IP endpoint.
///
public void Listen(IPEndPoint endPoint)
{
Listen(endPoint, _ => { });
}
///
/// Bind to given IP address and port.
/// The callback configures endpoint-specific settings.
///
public void Listen(IPEndPoint endPoint, Action configure)
{
if (endPoint == null)
{
throw new ArgumentNullException(nameof(endPoint));
}
if (configure == null)
{
throw new ArgumentNullException(nameof(configure));
}
var listenOptions = new ListenOptions(endPoint);
ApplyEndpointDefaults(listenOptions);
configure(listenOptions);
ListenOptions.Add(listenOptions);
}
///
/// Listens on ::1 and 127.0.0.1 with the given port. Requesting a dynamic port by specifying 0 is not supported
/// for this type of endpoint.
///
public void ListenLocalhost(int port) => ListenLocalhost(port, options => { });
///
/// Listens on ::1 and 127.0.0.1 with the given port. Requesting a dynamic port by specifying 0 is not supported
/// for this type of endpoint.
///
public void ListenLocalhost(int port, Action configure)
{
if (configure == null)
{
throw new ArgumentNullException(nameof(configure));
}
var listenOptions = new LocalhostListenOptions(port);
ApplyEndpointDefaults(listenOptions);
configure(listenOptions);
ListenOptions.Add(listenOptions);
}
///
/// Listens on all IPs using IPv6 [::], or IPv4 0.0.0.0 if IPv6 is not supported.
///
public void ListenAnyIP(int port) => ListenAnyIP(port, options => { });
///
/// Listens on all IPs using IPv6 [::], or IPv4 0.0.0.0 if IPv6 is not supported.
///
public void ListenAnyIP(int port, Action configure)
{
if (configure == null)
{
throw new ArgumentNullException(nameof(configure));
}
var listenOptions = new AnyIPListenOptions(port);
ApplyEndpointDefaults(listenOptions);
configure(listenOptions);
ListenOptions.Add(listenOptions);
}
///
/// Bind to given Unix domain socket path.
///
public void ListenUnixSocket(string socketPath)
{
ListenUnixSocket(socketPath, _ => { });
}
///
/// Bind to given Unix domain socket path.
/// Specify callback to configure endpoint-specific settings.
///
public void ListenUnixSocket(string socketPath, Action configure)
{
if (socketPath == null)
{
throw new ArgumentNullException(nameof(socketPath));
}
if (socketPath.Length == 0 || socketPath[0] != '/')
{
throw new ArgumentException(CoreStrings.UnixSocketPathMustBeAbsolute, nameof(socketPath));
}
if (configure == null)
{
throw new ArgumentNullException(nameof(configure));
}
var listenOptions = new ListenOptions(socketPath);
ApplyEndpointDefaults(listenOptions);
configure(listenOptions);
ListenOptions.Add(listenOptions);
}
///
/// Open a socket file descriptor.
///
public void ListenHandle(ulong handle)
{
ListenHandle(handle, _ => { });
}
///
/// Open a socket file descriptor.
/// The callback configures endpoint-specific settings.
///
public void ListenHandle(ulong handle, Action configure)
{
if (configure == null)
{
throw new ArgumentNullException(nameof(configure));
}
var listenOptions = new ListenOptions(handle);
ApplyEndpointDefaults(listenOptions);
configure(listenOptions);
ListenOptions.Add(listenOptions);
}
}
}