ASP.NET Core 2.1.0-preview2: Improvements to the Kestrel HTTP server

Change default transport to Sockets

Building off the improvements to the managed sockets implementation in .NET Core we have changed the default transport in Kestrel from libuv to sockets. As a consequence, the Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv package is no longer part of the Microsoft.AspNetCore.App metapackage.

How to switch back to libuv

To continue using libuv as your transport, you will need to add reference to the libuv package and modify your application to use libuv as it’s transport. Alternatively, you can reference the Microsoft.AspNetCore.All metapackage which includes a transitive dependency on the libuv package.

dotnet add package Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv -v 2.1.0-preview2-final
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseLibuv()
        .UseStartup<Startup>();

SNI support

Server Name Indication (SNI) is extension to the TLS protocol that allows clients to send the desired hostname unencrypted as part of the TLS handshake. As a consequence, you can host multiple domains on the same IP/Port and use the hostname to respond with the correct certificate. In 2.1.0-preview2, Kestrel has added a ServerCertificateSelector callback which is invoked once per connection to allow you select the right certificate. If you specify a ServerCertificateSelector, the selector will always take precedence over any specified server certificate.

SNI support requires running on netcoreapp2.1. On netcoreapp2.0 and net461 the callback will be invoked but the name will always be null. The name will also be null if the client did not provide this optional parameter.

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .UseKestrel((context, options) =>
        {
            options.ListenAnyIP(5005, listenOptions =>
            {
                listenOptions.UseHttps(httpsOptions =>
                {
                    var localhostCert = CertificateLoader.LoadFromStoreCert("localhost", "My", StoreLocation.CurrentUser, allowInvalid: true);
                    var exampleCert = CertificateLoader.LoadFromStoreCert("example.com", "My", StoreLocation.CurrentUser, allowInvalid: true);
                    var subExampleCert = CertificateLoader.LoadFromStoreCert("sub.example.com", "My", StoreLocation.CurrentUser, allowInvalid: true);
                    var certs = new Dictionary<string, X509Certificate2>(StringComparer.OrdinalIgnoreCase);
                    certs["localhost"] = localhostCert;
                    certs["example.com"] = exampleCert;
                    certs["sub.example.com"] = subExampleCert;

                    httpsOptions.ServerCertificateSelector = (features, name) =>
                    {
                        if (name != null && certs.TryGetValue(name, out var cert))
                        {
                            return cert;
                        }

                       return exampleCert;
                    };
                });
           });
        })
        .UseStartup<Startup>();

Host filtering middleware

While Kestrel supports configuration based on prefixes such as https://contoso.com:5000, it largely ignores the host name. Localhost is a special case used for binding to loopback addresses. Any host other than an explicit IP address binds to all public IP addresses. None of this information is used to validate request Host headers. In 2.1.0-preview2, we introduced a new HostFiltering middleware(Microsoft.AspNetCore.HostFiltering) that we recommend you use in conjunction with Kestrel to validate Host headers. The host filtering middleware is already included as part of the default WebHost. To configure the middleware, use a semicolon separated list of hostnames in your appSettings.json file.

{
    "AllowedHosts": "localhost;127.0.0.1;[::1]"
}

Alternatively, you can configure it directly from code.

services.AddHostFiltering(options =>
{
    var allowedHosts = new List<String>{
        "localhost",
        "127.0.0.1",
        "[::1]"
    };
    options.AllowedHosts = allowedHosts;
});