Skip to content
This repository was archived by the owner on Dec 18, 2018. It is now read-only.

Create a direct way to configure endpoints on KestrelEngine #1280

Merged
merged 1 commit into from
Jan 7, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
language: csharp
sudo: required
dist: trusty
services:
- docker
addons:
apt:
packages:
Expand Down Expand Up @@ -30,3 +32,4 @@ before_install:
- if test "$TRAVIS_OS_NAME" == "osx"; then brew update; brew install openssl; ln -s /usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib /usr/local/lib/; ln -s /usr/local/opt/openssl/lib/libssl.1.0.0.dylib /usr/local/lib/; fi
script:
- ./build.sh --quiet verify
- if test "$TRAVIS_OS_NAME" != "osx"; then bash test/Microsoft.AspNetCore.Server.Kestrel.FunctionalTests/SystemdActivation/docker.sh; fi
7 changes: 5 additions & 2 deletions samples/LargeResponseApp/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.IO;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
Expand Down Expand Up @@ -40,8 +41,10 @@ public void Configure(IApplicationBuilder app)
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseKestrel()
.UseUrls("http://localhost:5001/")
.UseKestrel(options =>
{
options.Listen(IPAddress.Loopback, 5001);
})
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.Build();
Expand Down
42 changes: 26 additions & 16 deletions samples/SampleApp/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.IO;
using System.Net;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
Expand Down Expand Up @@ -33,24 +34,33 @@ public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)

public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseKestrel(options =>
var hostBuilder = new WebHostBuilder().UseKestrel(options =>
{
options.Listen(IPAddress.Loopback, 5000, listenOptions =>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For a realistic sample the IP and port need to come from command line / env / config. We need to make that possible in one or two lines. Even if it means having a Listen overload that takes a string for IP:port that understands IPv4, IPv6, and *.
@shirhatti
options.Listen(config["IPAndPort"], listenOptions => ...

Note IPEndpoint does not implement Parse so you can't just punt this over there.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that the VS 2017 self-host profile now sets the ASPNETCORE_URLS environment variable by default so apps are unlikely to start from VS on localhost:5000 anymore. /cc: @BillHiebert

{
// Uncomment the following to enable Nagle's algorithm for this endpoint.
//listenOptions.NoDelay = false;

listenOptions.UseConnectionLogging();
});
options.Listen(IPAddress.Loopback, 5001, listenOptions =>
{
// options.ThreadCount = 4;
options.NoDelay = true;
options.UseHttps("testCert.pfx", "testPassword");
options.UseConnectionLogging();
})
.UseUrls("http://localhost:5000", "https://localhost:5001")
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>()
.Build();

// The following section should be used to demo sockets
//var addresses = application.GetAddresses();
//addresses.Clear();
//addresses.Add("http://unix:/tmp/kestrel-test.sock");
listenOptions.UseHttps("testCert.pfx", "testPassword");
listenOptions.UseConnectionLogging();
});

options.UseSystemd();

// The following section should be used to demo sockets
//options.ListenUnixSocket("/tmp/kestrel-test.sock");

// Uncomment the following line to change the default number of libuv threads for all endpoints.
//options.ThreadCount = 4;
})
.UseContentRoot(Directory.GetCurrentDirectory())
.UseStartup<Startup>();

var host = hostBuilder.Build();
host.Run();
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
// 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.IO;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.Kestrel.Adapter;
using Microsoft.AspNetCore.Server.Kestrel.Https.Internal;
using Microsoft.Extensions.Logging;

namespace Microsoft.AspNetCore.Server.Kestrel.Https
{
public class HttpsConnectionAdapter : IConnectionAdapter
{
private static readonly ClosedAdaptedConnection _closedAdaptedConnection = new ClosedAdaptedConnection();

private readonly HttpsConnectionAdapterOptions _options;
private readonly ILogger _logger;

public HttpsConnectionAdapter(HttpsConnectionAdapterOptions options)
: this(options, loggerFactory: null)
{
}

public HttpsConnectionAdapter(HttpsConnectionAdapterOptions options, ILoggerFactory loggerFactory)
{
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
if (options.ServerCertificate == null)
{
throw new ArgumentException("The server certificate parameter is required.");
}

_options = options;
_logger = loggerFactory?.CreateLogger(nameof(HttpsConnectionAdapter));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When is the logger factory null?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the HttpsConnectionAdapter is initialized directly using the other ctor.

}

public async Task<IAdaptedConnection> OnConnectionAsync(ConnectionAdapterContext context)
{
SslStream sslStream;
bool certificateRequired;

if (_options.ClientCertificateMode == ClientCertificateMode.NoCertificate)
{
sslStream = new SslStream(context.ConnectionStream);
certificateRequired = false;
}
else
{
sslStream = new SslStream(context.ConnectionStream, leaveInnerStreamOpen: false,
userCertificateValidationCallback: (sender, certificate, chain, sslPolicyErrors) =>
{
if (certificate == null)
{
return _options.ClientCertificateMode != ClientCertificateMode.RequireCertificate;
}

if (_options.ClientCertificateValidation == null)
{
if (sslPolicyErrors != SslPolicyErrors.None)
{
return false;
}
}

var certificate2 = ConvertToX509Certificate2(certificate);
if (certificate2 == null)
{
return false;
}

if (_options.ClientCertificateValidation != null)
{
if (!_options.ClientCertificateValidation(certificate2, chain, sslPolicyErrors))
{
return false;
}
}

return true;
});

certificateRequired = true;
}

try
{
await sslStream.AuthenticateAsServerAsync(_options.ServerCertificate, certificateRequired,
_options.SslProtocols, _options.CheckCertificateRevocation);
}
catch (IOException ex)
{
_logger?.LogInformation(1, ex, "Failed to authenticate HTTPS connection.");
sslStream.Dispose();
return _closedAdaptedConnection;
}

return new HttpsAdaptedConnection(sslStream);
}

private static X509Certificate2 ConvertToX509Certificate2(X509Certificate certificate)
{
if (certificate == null)
{
return null;
}

X509Certificate2 certificate2 = certificate as X509Certificate2;
if (certificate2 != null)
{
return certificate2;
}

#if NETSTANDARD1_3
// conversion X509Certificate to X509Certificate2 not supported
// https://github.com/dotnet/corefx/issues/4510
return null;
#else
return new X509Certificate2(certificate);
#endif
}

private class HttpsAdaptedConnection : IAdaptedConnection
{
private readonly SslStream _sslStream;

public HttpsAdaptedConnection(SslStream sslStream)
{
_sslStream = sslStream;
}

public Stream ConnectionStream => _sslStream;

public void PrepareRequest(IFeatureCollection requestFeatures)
{
var clientCertificate = ConvertToX509Certificate2(_sslStream.RemoteCertificate);
if (clientCertificate != null)
{
requestFeatures.Set<ITlsConnectionFeature>(new TlsConnectionFeature { ClientCertificate = clientCertificate });
}

requestFeatures.Get<IHttpRequestFeature>().Scheme = "https";
}
}

private class ClosedAdaptedConnection : IAdaptedConnection
{
public Stream ConnectionStream { get; } = new ClosedStream();

public void PrepareRequest(IFeatureCollection requestFeatures)
{
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@

namespace Microsoft.AspNetCore.Server.Kestrel.Https
{
public class HttpsConnectionFilterOptions
public class HttpsConnectionAdapterOptions
{
public HttpsConnectionFilterOptions()
public HttpsConnectionAdapterOptions()
{
ClientCertificateMode = ClientCertificateMode.NoCertificate;
SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls11;
Expand Down
Loading