This commit is contained in:
parent
3a0a133a02
commit
dfaf37cbba
|
|
@ -1,6 +1,6 @@
|
|||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27110.0
|
||||
VisualStudioVersion = 15.0.27130.2010
|
||||
MinimumVisualStudioVersion = 15.0.26730.03
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7972A5D6-3385-4127-9277-428506DD44FF}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
|
|
@ -104,6 +104,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Protocols.Abstractions", "s
|
|||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{C2910A13-B2C2-46D8-81D8-7E166F4F5981}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
build\dependencies.props = build\dependencies.props
|
||||
build\repo.props = build\repo.props
|
||||
build\repo.targets = build\repo.targets
|
||||
EndProjectSection
|
||||
|
|
@ -119,7 +120,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kestrel.Transport.Libuv.Fun
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kestrel.Transport.Sockets.FunctionalTests", "test\Kestrel.Transport.Sockets.FunctionalTests\Kestrel.Transport.Sockets.FunctionalTests.csproj", "{9C7B6B5F-088A-436E-834B-6373EA36DEEE}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Http2SampleApp", "samples\Http2SampleApp\Http2SampleApp.csproj", "{7BC22A4A-15D2-44C2-AB45-049F0FB562FA}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Http2SampleApp", "samples\Http2SampleApp\Http2SampleApp.csproj", "{7BC22A4A-15D2-44C2-AB45-049F0FB562FA}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SystemdTestApp", "samples\SystemdTestApp\SystemdTestApp.csproj", "{A7994A41-CAF8-47A7-8975-F101F75B5BC1}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
|
|
@ -335,6 +338,18 @@ Global
|
|||
{7BC22A4A-15D2-44C2-AB45-049F0FB562FA}.Release|x64.Build.0 = Release|Any CPU
|
||||
{7BC22A4A-15D2-44C2-AB45-049F0FB562FA}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{7BC22A4A-15D2-44C2-AB45-049F0FB562FA}.Release|x86.Build.0 = Release|Any CPU
|
||||
{A7994A41-CAF8-47A7-8975-F101F75B5BC1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A7994A41-CAF8-47A7-8975-F101F75B5BC1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A7994A41-CAF8-47A7-8975-F101F75B5BC1}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{A7994A41-CAF8-47A7-8975-F101F75B5BC1}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{A7994A41-CAF8-47A7-8975-F101F75B5BC1}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{A7994A41-CAF8-47A7-8975-F101F75B5BC1}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{A7994A41-CAF8-47A7-8975-F101F75B5BC1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A7994A41-CAF8-47A7-8975-F101F75B5BC1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A7994A41-CAF8-47A7-8975-F101F75B5BC1}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{A7994A41-CAF8-47A7-8975-F101F75B5BC1}.Release|x64.Build.0 = Release|Any CPU
|
||||
{A7994A41-CAF8-47A7-8975-F101F75B5BC1}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{A7994A41-CAF8-47A7-8975-F101F75B5BC1}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
@ -359,6 +374,7 @@ Global
|
|||
{74032D79-8EA7-4483-BD82-C38370420FFF} = {D3273454-EA07-41D2-BF0B-FCC3675C2483}
|
||||
{9C7B6B5F-088A-436E-834B-6373EA36DEEE} = {D3273454-EA07-41D2-BF0B-FCC3675C2483}
|
||||
{7BC22A4A-15D2-44C2-AB45-049F0FB562FA} = {8A3D00B8-1CCF-4BE6-A060-11104CE2D9CE}
|
||||
{A7994A41-CAF8-47A7-8975-F101F75B5BC1} = {8A3D00B8-1CCF-4BE6-A060-11104CE2D9CE}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {2D10D020-6770-47CA-BB8D-2C23FE3AE071}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,9 @@
|
|||
<MicrosoftAspNetCoreHttpPackageVersion>2.1.0-preview1-27845</MicrosoftAspNetCoreHttpPackageVersion>
|
||||
<MicrosoftAspNetCoreTestingPackageVersion>2.1.0-preview1-27845</MicrosoftAspNetCoreTestingPackageVersion>
|
||||
<MicrosoftAspNetCoreWebUtilitiesPackageVersion>2.1.0-preview1-27845</MicrosoftAspNetCoreWebUtilitiesPackageVersion>
|
||||
<MicrosoftExtensionsConfigurationBinderPackageVersion>2.1.0-preview1-27845</MicrosoftExtensionsConfigurationBinderPackageVersion>
|
||||
<MicrosoftExtensionsConfigurationJsonPackageVersion>2.1.0-preview1-27845</MicrosoftExtensionsConfigurationJsonPackageVersion>
|
||||
<MicrosoftExtensionsDependencyInjectionPackageVersion>2.1.0-preview1-27845</MicrosoftExtensionsDependencyInjectionPackageVersion>
|
||||
<MicrosoftExtensionsLoggingAbstractionsPackageVersion>2.1.0-preview1-27845</MicrosoftExtensionsLoggingAbstractionsPackageVersion>
|
||||
<MicrosoftExtensionsLoggingConsolePackageVersion>2.1.0-preview1-27845</MicrosoftExtensionsLoggingConsolePackageVersion>
|
||||
<MicrosoftExtensionsLoggingPackageVersion>2.1.0-preview1-27845</MicrosoftExtensionsLoggingPackageVersion>
|
||||
|
|
|
|||
|
|
@ -12,11 +12,26 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="$(MicrosoftExtensionsConfigurationJsonPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsLoggingConsolePackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="../../test/shared/TestCertificates/testCert.pfx" CopyToOutputDirectory="PreserveNewest" />
|
||||
<Content Include="testCert.pfx">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Update="appsettings.Development.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="appsettings.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
<Content Update="appsettings.Production.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -3,13 +3,15 @@
|
|||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
|
@ -48,10 +50,31 @@ namespace SampleApp
|
|||
{
|
||||
factory.AddConsole();
|
||||
})
|
||||
.ConfigureAppConfiguration((hostingContext, config) =>
|
||||
{
|
||||
var env = hostingContext.HostingEnvironment;
|
||||
config.AddJsonFile("appsettings.json", optional: true)
|
||||
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
|
||||
})
|
||||
.UseKestrel((context, options) =>
|
||||
{
|
||||
if (context.HostingEnvironment.IsDevelopment())
|
||||
{
|
||||
ShowConfig(context.Configuration);
|
||||
}
|
||||
|
||||
var basePort = context.Configuration.GetValue<int?>("BASE_PORT") ?? 5000;
|
||||
|
||||
options.ConfigureEndpointDefaults(opt =>
|
||||
{
|
||||
opt.Protocols = HttpProtocols.Http1;
|
||||
});
|
||||
|
||||
options.ConfigureHttpsDefaults(httpsOptions =>
|
||||
{
|
||||
httpsOptions.SslProtocols = SslProtocols.Tls12;
|
||||
});
|
||||
|
||||
// Run callbacks on the transport thread
|
||||
options.ApplicationSchedulingMode = SchedulingMode.Inline;
|
||||
|
||||
|
|
@ -71,11 +94,34 @@ namespace SampleApp
|
|||
|
||||
options.ListenLocalhost(basePort + 2, listenOptions =>
|
||||
{
|
||||
listenOptions.UseHttps("testCert.pfx", "testPassword");
|
||||
// Use default dev cert
|
||||
listenOptions.UseHttps();
|
||||
});
|
||||
|
||||
options.ListenAnyIP(basePort + 3);
|
||||
|
||||
options.ListenAnyIP(basePort + 4, listenOptions =>
|
||||
{
|
||||
listenOptions.UseHttps(StoreName.My, "aspnet.test", allowInvalid: true);
|
||||
});
|
||||
|
||||
options
|
||||
.Configure()
|
||||
.Endpoint(IPAddress.Loopback, basePort + 5)
|
||||
.LocalhostEndpoint(basePort + 6)
|
||||
.Load();
|
||||
|
||||
options
|
||||
.Configure(context.Configuration.GetSection("Kestrel"))
|
||||
.Endpoint("NamedEndpoint", opt =>
|
||||
{
|
||||
opt.ListenOptions.Protocols = HttpProtocols.Http1;
|
||||
})
|
||||
.Endpoint("NamedHttpsEndpoint", opt =>
|
||||
{
|
||||
opt.HttpsOptions.SslProtocols = SslProtocols.Tls12;
|
||||
});
|
||||
|
||||
options.UseSystemd();
|
||||
|
||||
// The following section should be used to demo sockets
|
||||
|
|
@ -96,5 +142,14 @@ namespace SampleApp
|
|||
|
||||
return hostBuilder.Build().RunAsync();
|
||||
}
|
||||
|
||||
private static void ShowConfig(IConfiguration config)
|
||||
{
|
||||
foreach (var pair in config.GetChildren())
|
||||
{
|
||||
Console.WriteLine($"{pair.Path} - {pair.Value}");
|
||||
ShowConfig(pair);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"Kestrel": {
|
||||
"Endpoints": {
|
||||
"NamedEndpoint": { "Url": "http://localhost:6000" },
|
||||
"NamedHttpsEndpoint": {
|
||||
"Url": "https://localhost:6443",
|
||||
"Certificate": {
|
||||
"Subject": "aspnet.test",
|
||||
"Store": "My",
|
||||
"AllowInvalid": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"Kestrel": {
|
||||
"Endpoints": {
|
||||
"NamedEndpoint": { "Url": "http://*:6000" },
|
||||
"NamedHttpsEndpoint": {
|
||||
"Url": "https://*:6443",
|
||||
"Certificate": {
|
||||
"Path": "testCert.pfx",
|
||||
"Password": "testPassword"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"Kestrel": {
|
||||
"Endpoints": {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
// 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.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace SystemdTestApp
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
|
||||
{
|
||||
var logger = loggerFactory.CreateLogger("Default");
|
||||
|
||||
app.Run(async context =>
|
||||
{
|
||||
var connectionFeature = context.Connection;
|
||||
logger.LogDebug($"Peer: {connectionFeature.RemoteIpAddress?.ToString()}:{connectionFeature.RemotePort}"
|
||||
+ $"{Environment.NewLine}"
|
||||
+ $"Sock: {connectionFeature.LocalIpAddress?.ToString()}:{connectionFeature.LocalPort}");
|
||||
|
||||
var response = $"hello, world{Environment.NewLine}";
|
||||
context.Response.ContentLength = response.Length;
|
||||
context.Response.ContentType = "text/plain";
|
||||
await context.Response.WriteAsync(response);
|
||||
});
|
||||
}
|
||||
|
||||
public static Task Main(string[] args)
|
||||
{
|
||||
TaskScheduler.UnobservedTaskException += (sender, e) =>
|
||||
{
|
||||
Console.WriteLine("Unobserved exception: {0}", e.Exception);
|
||||
};
|
||||
|
||||
var hostBuilder = new WebHostBuilder()
|
||||
.ConfigureLogging((_, factory) =>
|
||||
{
|
||||
factory.AddConsole();
|
||||
})
|
||||
.UseKestrel((context, options) =>
|
||||
{
|
||||
var basePort = context.Configuration.GetValue<int?>("BASE_PORT") ?? 5000;
|
||||
|
||||
// Run callbacks on the transport thread
|
||||
options.ApplicationSchedulingMode = SchedulingMode.Inline;
|
||||
|
||||
options.Listen(IPAddress.Loopback, basePort, listenOptions =>
|
||||
{
|
||||
// Uncomment the following to enable Nagle's algorithm for this endpoint.
|
||||
//listenOptions.NoDelay = false;
|
||||
|
||||
listenOptions.UseConnectionLogging();
|
||||
});
|
||||
|
||||
options.Listen(IPAddress.Loopback, basePort + 1, listenOptions =>
|
||||
{
|
||||
listenOptions.UseHttps("testCert.pfx", "testPassword");
|
||||
listenOptions.UseConnectionLogging();
|
||||
});
|
||||
|
||||
options.UseSystemd();
|
||||
|
||||
// The following section should be used to demo sockets
|
||||
//options.ListenUnixSocket("/tmp/kestrel-test.sock");
|
||||
})
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.UseStartup<Startup>();
|
||||
|
||||
if (string.Equals(Process.GetCurrentProcess().Id.ToString(), Environment.GetEnvironmentVariable("LISTEN_PID")))
|
||||
{
|
||||
// Use libuv if activated by systemd, since that's currently the only transport that supports being passed a socket handle.
|
||||
hostBuilder.UseLibuv(options =>
|
||||
{
|
||||
// Uncomment the following line to change the default number of libuv threads for all endpoints.
|
||||
// options.ThreadCount = 4;
|
||||
});
|
||||
}
|
||||
|
||||
return hostBuilder.Build().RunAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netcoreapp2.1;netcoreapp2.0;net461</TargetFrameworks>
|
||||
<IsPackable>false</IsPackable>
|
||||
<NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Kestrel\Kestrel.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsLoggingConsolePackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="testCert.pfx">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Binary file not shown.
|
|
@ -480,4 +480,19 @@
|
|||
<data name="ServiceCertificateRequired" xml:space="preserve">
|
||||
<value>The server certificate parameter is required.</value>
|
||||
</data>
|
||||
<data name="BindingToDefaultAddresses" xml:space="preserve">
|
||||
<value>No listening endpoints were configured. Binding to {address0} and {address1} by default.</value>
|
||||
</data>
|
||||
<data name="CertNotFoundInStore" xml:space="preserve">
|
||||
<value>The requested certificate {subject} could not be found in {storeLocation}/{storeName} with AllowInvalid setting: {allowInvalid}.</value>
|
||||
</data>
|
||||
<data name="EndpointMissingUrl" xml:space="preserve">
|
||||
<value>The endpoint {endpointName} is missing the required 'Url' parameter.</value>
|
||||
</data>
|
||||
<data name="NoCertSpecifiedNoDevelopmentCertificateFound" xml:space="preserve">
|
||||
<value>Unable to configure HTTPS endpoint. No server certificate was specified and the default developer certificate could not be found. Try running 'dotnet developercertificates https -t' to setup a developer certificate for use with localhost. For information on configuring HTTPS see https://go.microsoft.com/fwlink/?linkid=848054</value>
|
||||
</data>
|
||||
<data name="MultipleCertificateSources" xml:space="preserve">
|
||||
<value>The endpoint {endpointName} specified multiple certificate sources.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
// 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 Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Https;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel
|
||||
{
|
||||
public class EndpointConfiguration
|
||||
{
|
||||
internal EndpointConfiguration(bool isHttps, ListenOptions listenOptions, HttpsConnectionAdapterOptions httpsOptions, IConfigurationSection configSection)
|
||||
{
|
||||
IsHttps = isHttps;
|
||||
ListenOptions = listenOptions ?? throw new ArgumentNullException(nameof(listenOptions));
|
||||
HttpsOptions = httpsOptions ?? throw new ArgumentNullException(nameof(httpsOptions));
|
||||
ConfigSection = configSection ?? throw new ArgumentNullException(nameof(configSection));
|
||||
}
|
||||
|
||||
public bool IsHttps { get; }
|
||||
public ListenOptions ListenOptions { get; }
|
||||
public HttpsConnectionAdapterOptions HttpsOptions { get; }
|
||||
public IConfigurationSection ConfigSection { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -14,7 +14,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
public List<ListenOptions> ListenOptions { get; set; }
|
||||
public KestrelServerOptions ServerOptions { get; set; }
|
||||
public ILogger Logger { get; set; }
|
||||
public IDefaultHttpsProvider DefaultHttpsProvider { get; set; }
|
||||
|
||||
public Func<ListenOptions, Task> CreateBinding { get; set; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ using System.Linq;
|
|||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Hosting.Server.Features;
|
||||
using Microsoft.AspNetCore.Protocols;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
|
||||
|
|
@ -20,7 +21,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
public static async Task BindAsync(IServerAddressesFeature addresses,
|
||||
KestrelServerOptions serverOptions,
|
||||
ILogger logger,
|
||||
IDefaultHttpsProvider defaultHttpsProvider,
|
||||
Func<ListenOptions, Task> createBinding)
|
||||
{
|
||||
var listenOptions = serverOptions.ListenOptions;
|
||||
|
|
@ -35,7 +35,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
ListenOptions = listenOptions,
|
||||
ServerOptions = serverOptions,
|
||||
Logger = logger,
|
||||
DefaultHttpsProvider = defaultHttpsProvider ?? UnconfiguredDefaultHttpsProvider.Instance,
|
||||
CreateBinding = createBinding
|
||||
};
|
||||
|
||||
|
|
@ -112,10 +111,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
context.ListenOptions.Add(endpoint);
|
||||
}
|
||||
|
||||
internal static ListenOptions ParseAddress(string address, KestrelServerOptions serverOptions, IDefaultHttpsProvider defaultHttpsProvider)
|
||||
internal static ListenOptions ParseAddress(string address, out bool https)
|
||||
{
|
||||
var parsedAddress = ServerAddress.FromUrl(address);
|
||||
var https = false;
|
||||
https = false;
|
||||
|
||||
if (parsedAddress.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
|
|
@ -151,12 +150,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
options = new AnyIPListenOptions(parsedAddress.Port);
|
||||
}
|
||||
|
||||
if (https)
|
||||
{
|
||||
options.KestrelServerOptions = serverOptions;
|
||||
defaultHttpsProvider.ConfigureHttps(options);
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
|
|
@ -169,10 +162,26 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
{
|
||||
public async Task BindAsync(AddressBindContext context)
|
||||
{
|
||||
context.Logger.LogDebug(CoreStrings.BindingToDefaultAddress, Constants.DefaultServerAddress);
|
||||
var httpDefault = ParseAddress(Constants.DefaultServerAddress, out var https);
|
||||
context.ServerOptions.ApplyEndpointDefaults(httpDefault);
|
||||
await httpDefault.BindAsync(context).ConfigureAwait(false);
|
||||
|
||||
await ParseAddress(Constants.DefaultServerAddress, context.ServerOptions, context.DefaultHttpsProvider)
|
||||
.BindAsync(context).ConfigureAwait(false);
|
||||
// Conditional https default, only if a cert is available
|
||||
var httpsDefault = ParseAddress(Constants.DefaultServerHttpsAddress, out https);
|
||||
context.ServerOptions.ApplyEndpointDefaults(httpsDefault);
|
||||
|
||||
if (httpsDefault.ConnectionAdapters.Any(f => f.IsHttps)
|
||||
|| httpsDefault.TryUseHttps())
|
||||
{
|
||||
await httpsDefault.BindAsync(context).ConfigureAwait(false);
|
||||
context.Logger.LogDebug(CoreStrings.BindingToDefaultAddresses,
|
||||
Constants.DefaultServerAddress, Constants.DefaultServerHttpsAddress);
|
||||
}
|
||||
else
|
||||
{
|
||||
// No default cert is available, do not bind to the https endpoint.
|
||||
context.Logger.LogDebug(CoreStrings.BindingToDefaultAddress, Constants.DefaultServerAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -242,27 +251,17 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
{
|
||||
foreach (var address in _addresses)
|
||||
{
|
||||
await ParseAddress(address, context.ServerOptions, context.DefaultHttpsProvider)
|
||||
.BindAsync(context).ConfigureAwait(false);
|
||||
var options = ParseAddress(address, out var https);
|
||||
context.ServerOptions.ApplyEndpointDefaults(options);
|
||||
|
||||
if (https && !options.ConnectionAdapters.Any(f => f.IsHttps))
|
||||
{
|
||||
options.UseHttps();
|
||||
}
|
||||
|
||||
await options.BindAsync(context).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class UnconfiguredDefaultHttpsProvider : IDefaultHttpsProvider
|
||||
{
|
||||
public static readonly UnconfiguredDefaultHttpsProvider Instance = new UnconfiguredDefaultHttpsProvider();
|
||||
|
||||
private UnconfiguredDefaultHttpsProvider()
|
||||
{
|
||||
}
|
||||
|
||||
public void ConfigureHttps(ListenOptions listenOptions)
|
||||
{
|
||||
// We have to throw here. If this is called, it's because the user asked for "https" binding but for some
|
||||
// reason didn't provide a certificate and didn't use the "DefaultHttpsProvider". This means if we no-op,
|
||||
// we'll silently downgrade to HTTP, which is bad.
|
||||
throw new InvalidOperationException(CoreStrings.UnableToConfigureHttpsBindings);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,99 @@
|
|||
// 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.Linq;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Https.Internal
|
||||
{
|
||||
public static class CertificateLoader
|
||||
{
|
||||
// See http://oid-info.com/get/1.3.6.1.5.5.7.3.1
|
||||
// Indicates that a certificate can be used as a SSL server certificate
|
||||
private const string ServerAuthenticationOid = "1.3.6.1.5.5.7.3.1";
|
||||
|
||||
public static X509Certificate2 LoadFromStoreCert(string subject, string storeName, StoreLocation storeLocation, bool allowInvalid)
|
||||
{
|
||||
using (var store = new X509Store(storeName, storeLocation))
|
||||
{
|
||||
X509Certificate2Collection storeCertificates = null;
|
||||
X509Certificate2 foundCertificate = null;
|
||||
|
||||
try
|
||||
{
|
||||
store.Open(OpenFlags.ReadOnly);
|
||||
storeCertificates = store.Certificates;
|
||||
var foundCertificates = storeCertificates.Find(X509FindType.FindBySubjectName, subject, !allowInvalid);
|
||||
foundCertificate = foundCertificates
|
||||
.OfType<X509Certificate2>()
|
||||
.Where(IsCertificateAllowedForServerAuth)
|
||||
.OrderByDescending(certificate => certificate.NotAfter)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (foundCertificate == null)
|
||||
{
|
||||
throw new InvalidOperationException(CoreStrings.FormatCertNotFoundInStore(subject, storeLocation, storeName, allowInvalid));
|
||||
}
|
||||
|
||||
return foundCertificate;
|
||||
}
|
||||
finally
|
||||
{
|
||||
DisposeCertificates(storeCertificates, except: foundCertificate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool IsCertificateAllowedForServerAuth(X509Certificate2 certificate)
|
||||
{
|
||||
/* If the Extended Key Usage extension is included, then we check that the serverAuth usage is included. (http://oid-info.com/get/1.3.6.1.5.5.7.3.1)
|
||||
* If the Extended Key Usage extension is not included, then we assume the certificate is allowed for all usages.
|
||||
*
|
||||
* See also https://blogs.msdn.microsoft.com/kaushal/2012/02/17/client-certificates-vs-server-certificates/
|
||||
*
|
||||
* From https://tools.ietf.org/html/rfc3280#section-4.2.1.13 "Certificate Extensions: Extended Key Usage"
|
||||
*
|
||||
* If the (Extended Key Usage) extension is present, then the certificate MUST only be used
|
||||
* for one of the purposes indicated. If multiple purposes are
|
||||
* indicated the application need not recognize all purposes indicated,
|
||||
* as long as the intended purpose is present. Certificate using
|
||||
* applications MAY require that a particular purpose be indicated in
|
||||
* order for the certificate to be acceptable to that application.
|
||||
*/
|
||||
|
||||
var hasEkuExtension = false;
|
||||
|
||||
foreach (var extension in certificate.Extensions.OfType<X509EnhancedKeyUsageExtension>())
|
||||
{
|
||||
hasEkuExtension = true;
|
||||
foreach (var oid in extension.EnhancedKeyUsages)
|
||||
{
|
||||
if (oid.Value.Equals(ServerAuthenticationOid, StringComparison.Ordinal))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return !hasEkuExtension;
|
||||
}
|
||||
|
||||
private static void DisposeCertificates(X509Certificate2Collection certificates, X509Certificate2 except)
|
||||
{
|
||||
if (certificates != null)
|
||||
{
|
||||
foreach (var certificate in certificates)
|
||||
{
|
||||
if (!certificate.Equals(except))
|
||||
{
|
||||
certificate.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
// 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.Linq;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
||||
{
|
||||
internal class ConfigurationReader
|
||||
{
|
||||
private IConfiguration _configuration;
|
||||
private IDictionary<string, CertificateConfig> _certificates;
|
||||
private IList<EndpointConfig> _endpoints;
|
||||
|
||||
public ConfigurationReader(IConfiguration configuration)
|
||||
{
|
||||
_configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
|
||||
}
|
||||
|
||||
public IDictionary<string, CertificateConfig> Certificates
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_certificates == null)
|
||||
{
|
||||
ReadCertificates();
|
||||
}
|
||||
|
||||
return _certificates;
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<EndpointConfig> Endpoints
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_endpoints == null)
|
||||
{
|
||||
ReadEndpoints();
|
||||
}
|
||||
|
||||
return _endpoints;
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadCertificates()
|
||||
{
|
||||
_certificates = new Dictionary<string, CertificateConfig>(0);
|
||||
|
||||
var certificatesConfig = _configuration.GetSection("Certificates").GetChildren();
|
||||
foreach (var certificateConfig in certificatesConfig)
|
||||
{
|
||||
_certificates.Add(certificateConfig.Key, new CertificateConfig(certificateConfig));
|
||||
}
|
||||
}
|
||||
|
||||
private void ReadEndpoints()
|
||||
{
|
||||
_endpoints = new List<EndpointConfig>();
|
||||
|
||||
var endpointsConfig = _configuration.GetSection("Endpoints").GetChildren();
|
||||
foreach (var endpointConfig in endpointsConfig)
|
||||
{
|
||||
// "EndpointName": {
|
||||
// "Url": "https://*:5463",
|
||||
// "Certificate": {
|
||||
// "Path": "testCert.pfx",
|
||||
// "Password": "testPassword"
|
||||
// }
|
||||
// }
|
||||
|
||||
var url = endpointConfig["Url"];
|
||||
if (string.IsNullOrEmpty(url))
|
||||
{
|
||||
throw new InvalidOperationException(CoreStrings.FormatEndpointMissingUrl(endpointConfig.Key));
|
||||
}
|
||||
|
||||
var endpoint = new EndpointConfig()
|
||||
{
|
||||
Name = endpointConfig.Key,
|
||||
Url = url,
|
||||
ConfigSection = endpointConfig,
|
||||
Certificate = new CertificateConfig(endpointConfig.GetSection("Certificate")),
|
||||
};
|
||||
_endpoints.Add(endpoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// "EndpointName": {
|
||||
// "Url": "https://*:5463",
|
||||
// "Certificate": {
|
||||
// "Path": "testCert.pfx",
|
||||
// "Password": "testPassword"
|
||||
// }
|
||||
// }
|
||||
internal class EndpointConfig
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Url { get; set; }
|
||||
public IConfigurationSection ConfigSection { get; set; }
|
||||
public CertificateConfig Certificate { get; set; }
|
||||
}
|
||||
|
||||
// "CertificateName": {
|
||||
// "Path": "testCert.pfx",
|
||||
// "Password": "testPassword"
|
||||
// }
|
||||
internal class CertificateConfig
|
||||
{
|
||||
public CertificateConfig(IConfigurationSection configSection)
|
||||
{
|
||||
ConfigSection = configSection;
|
||||
ConfigSection.Bind(this);
|
||||
}
|
||||
|
||||
public IConfigurationSection ConfigSection { get; }
|
||||
|
||||
// File
|
||||
public bool IsFileCert => !string.IsNullOrEmpty(Path);
|
||||
|
||||
public string Path { get; set; }
|
||||
|
||||
public string Password { get; set; }
|
||||
|
||||
// Cert store
|
||||
|
||||
public bool IsStoreCert => !string.IsNullOrEmpty(Subject);
|
||||
|
||||
public string Subject { get; set; }
|
||||
|
||||
public string Store { get; set; }
|
||||
|
||||
public string Location { get; set; }
|
||||
|
||||
public bool? AllowInvalid { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -19,10 +19,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https.Internal
|
|||
{
|
||||
public class HttpsConnectionAdapter : IConnectionAdapter
|
||||
{
|
||||
// See http://oid-info.com/get/1.3.6.1.5.5.7.3.1
|
||||
// Indicates that a certificate can be used as a SSL server certificate
|
||||
private const string ServerAuthenticationOid = "1.3.6.1.5.5.7.3.1";
|
||||
|
||||
private static readonly ClosedAdaptedConnection _closedAdaptedConnection = new ClosedAdaptedConnection();
|
||||
|
||||
private readonly HttpsConnectionAdapterOptions _options;
|
||||
|
|
@ -187,36 +183,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Https.Internal
|
|||
|
||||
private static void EnsureCertificateIsAllowedForServerAuth(X509Certificate2 certificate)
|
||||
{
|
||||
/* If the Extended Key Usage extension is included, then we check that the serverAuth usage is included. (http://oid-info.com/get/1.3.6.1.5.5.7.3.1)
|
||||
* If the Extended Key Usage extension is not included, then we assume the certificate is allowed for all usages.
|
||||
*
|
||||
* See also https://blogs.msdn.microsoft.com/kaushal/2012/02/17/client-certificates-vs-server-certificates/
|
||||
*
|
||||
* From https://tools.ietf.org/html/rfc3280#section-4.2.1.13 "Certificate Extensions: Extended Key Usage"
|
||||
*
|
||||
* If the (Extended Key Usage) extension is present, then the certificate MUST only be used
|
||||
* for one of the purposes indicated. If multiple purposes are
|
||||
* indicated the application need not recognize all purposes indicated,
|
||||
* as long as the intended purpose is present. Certificate using
|
||||
* applications MAY require that a particular purpose be indicated in
|
||||
* order for the certificate to be acceptable to that application.
|
||||
*/
|
||||
|
||||
var hasEkuExtension = false;
|
||||
|
||||
foreach (var extension in certificate.Extensions.OfType<X509EnhancedKeyUsageExtension>())
|
||||
{
|
||||
hasEkuExtension = true;
|
||||
foreach (var oid in extension.EnhancedKeyUsages)
|
||||
{
|
||||
if (oid.Value.Equals(ServerAuthenticationOid, StringComparison.Ordinal))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasEkuExtension)
|
||||
if (!CertificateLoader.IsCertificateAllowedForServerAuth(certificate))
|
||||
{
|
||||
throw new InvalidOperationException(CoreStrings.FormatInvalidServerCertificateEku(certificate.Thumbprint));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +0,0 @@
|
|||
// 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.
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
||||
{
|
||||
public interface IDefaultHttpsProvider
|
||||
{
|
||||
void ConfigureHttps(ListenOptions listenOptions);
|
||||
}
|
||||
}
|
||||
|
|
@ -10,10 +10,15 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
|
|||
public const int MaxExceptionDetailSize = 128;
|
||||
|
||||
/// <summary>
|
||||
/// The IPEndPoint Kestrel will bind to if nothing else is specified.
|
||||
/// The endpoint Kestrel will bind to if nothing else is specified.
|
||||
/// </summary>
|
||||
public static readonly string DefaultServerAddress = "http://localhost:5000";
|
||||
|
||||
/// <summary>
|
||||
/// The endpoint Kestrel will bind to if nothing else is specified and a default certificate is available.
|
||||
/// </summary>
|
||||
public static readonly string DefaultServerHttpsAddress = "https://localhost:5001";
|
||||
|
||||
/// <summary>
|
||||
/// Prefix of host name used to specify Unix sockets in the configuration.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -2,8 +2,12 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Hosting.Server.Features;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using Microsoft.AspNetCore.Certificates.Generation;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
||||
|
|
@ -20,6 +24,24 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal
|
|||
public void Configure(KestrelServerOptions options)
|
||||
{
|
||||
options.ApplicationServices = _services;
|
||||
UseDefaultDeveloperCertificate(options);
|
||||
}
|
||||
|
||||
private void UseDefaultDeveloperCertificate(KestrelServerOptions options)
|
||||
{
|
||||
var certificateManager = new CertificateManager();
|
||||
var certificate = certificateManager.ListCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, isValid: true)
|
||||
.FirstOrDefault();
|
||||
var logger = options.ApplicationServices.GetRequiredService<ILogger<KestrelServer>>();
|
||||
if (certificate != null)
|
||||
{
|
||||
logger.LocatedDevelopmentCertificate(certificate);
|
||||
options.DefaultCertificate = certificate;
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.UnableToLocateDevelopmentCertificate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Internal
|
|||
LoggerMessage.Define<string, string>(LogLevel.Debug, new EventId(0, nameof(LocatedDevelopmentCertificate)), "Using development certificate: {certificateSubjectName} (Thumbprint: {certificateThumbprint})");
|
||||
|
||||
private static readonly Action<ILogger, Exception> _unableToLocateDevelopmentCertificate =
|
||||
LoggerMessage.Define(LogLevel.Error, new EventId(1, nameof(UnableToLocateDevelopmentCertificate)), "Unable to locate an appropriate development https certificate.");
|
||||
LoggerMessage.Define(LogLevel.Debug, new EventId(1, nameof(UnableToLocateDevelopmentCertificate)), "Unable to locate an appropriate development https certificate.");
|
||||
|
||||
public static void LocatedDevelopmentCertificate(this ILogger logger, X509Certificate2 certificate) => _locatedDevelopmentCertificate(logger, certificate.Subject, certificate.Thumbprint, null);
|
||||
|
||||
|
|
@ -12,14 +12,17 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Certificates.Generation.Sources" PrivateAssets="All" Version="$(MicrosoftAspNetCoreCertificatesGenerationSourcesPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="$(MicrosoftAspNetCoreHostingAbstractionsPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.WebUtilities" Version="$(MicrosoftAspNetCoreWebUtilitiesPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Net.Http.Headers" Version="$(MicrosoftNetHttpHeadersPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="$(MicrosoftExtensionsConfigurationBinderPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="$(MicrosoftExtensionsLoggingAbstractionsPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="$(MicrosoftExtensionsOptionsPackageVersion)" />
|
||||
<PackageReference Include="System.Threading.Tasks.Extensions" Version="$(SystemThreadingTasksExtensionsPackageVersion)" />
|
||||
<PackageReference Include="System.Memory" Version="$(SystemMemoryPackageVersion)" />
|
||||
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="$(SystemRuntimeCompilerServicesUnsafePackageVersion)" />
|
||||
<PackageReference Include="System.Security.Cryptography.Cng" Version="$(SystemSecurityCryptographyCngPackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,297 @@
|
|||
// 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.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Https;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Https.Internal;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel
|
||||
{
|
||||
public class KestrelConfigurationLoader
|
||||
{
|
||||
internal KestrelConfigurationLoader(KestrelServerOptions options, IConfiguration configuration)
|
||||
{
|
||||
Options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
Configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
|
||||
}
|
||||
|
||||
public KestrelServerOptions Options { get; }
|
||||
public IConfiguration Configuration { get; }
|
||||
private IDictionary<string, Action<EndpointConfiguration>> EndpointConfigurations { get; }
|
||||
= new Dictionary<string, Action<EndpointConfiguration>>(0, StringComparer.OrdinalIgnoreCase);
|
||||
// Actions that will be delayed until Load so that they aren't applied if the configuration loader is replaced.
|
||||
private IList<Action> EndpointsToAdd { get; } = new List<Action>();
|
||||
|
||||
/// <summary>
|
||||
/// Specifies a configuration Action to run when an endpoint with the given name is loaded from configuration.
|
||||
/// </summary>
|
||||
public KestrelConfigurationLoader Endpoint(string name, Action<EndpointConfiguration> configureOptions)
|
||||
{
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(name));
|
||||
}
|
||||
|
||||
EndpointConfigurations[name] = configureOptions ?? throw new ArgumentNullException(nameof(configureOptions));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bind to given IP address and port.
|
||||
/// </summary>
|
||||
public KestrelConfigurationLoader Endpoint(IPAddress address, int port) => Endpoint(address, port, _ => { });
|
||||
|
||||
/// <summary>
|
||||
/// Bind to given IP address and port.
|
||||
/// </summary>
|
||||
public KestrelConfigurationLoader Endpoint(IPAddress address, int port, Action<ListenOptions> configure)
|
||||
{
|
||||
if (address == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(address));
|
||||
}
|
||||
|
||||
return Endpoint(new IPEndPoint(address, port), configure);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bind to given IP endpoint.
|
||||
/// </summary>
|
||||
public KestrelConfigurationLoader Endpoint(IPEndPoint endPoint) => Endpoint(endPoint, _ => { });
|
||||
|
||||
/// <summary>
|
||||
/// Bind to given IP address and port.
|
||||
/// </summary>
|
||||
public KestrelConfigurationLoader Endpoint(IPEndPoint endPoint, Action<ListenOptions> configure)
|
||||
{
|
||||
if (endPoint == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(endPoint));
|
||||
}
|
||||
if (configure == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configure));
|
||||
}
|
||||
|
||||
EndpointsToAdd.Add(() =>
|
||||
{
|
||||
Options.Listen(endPoint, configure);
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public KestrelConfigurationLoader LocalhostEndpoint(int port) => LocalhostEndpoint(port, options => { });
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public KestrelConfigurationLoader LocalhostEndpoint(int port, Action<ListenOptions> configure)
|
||||
{
|
||||
if (configure == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configure));
|
||||
}
|
||||
|
||||
EndpointsToAdd.Add(() =>
|
||||
{
|
||||
Options.ListenLocalhost(port, configure);
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Listens on all IPs using IPv6 [::], or IPv4 0.0.0.0 if IPv6 is not supported.
|
||||
/// </summary>
|
||||
public KestrelConfigurationLoader AnyIPEndpoint(int port) => AnyIPEndpoint(port, options => { });
|
||||
|
||||
/// <summary>
|
||||
/// Listens on all IPs using IPv6 [::], or IPv4 0.0.0.0 if IPv6 is not supported.
|
||||
/// </summary>
|
||||
public KestrelConfigurationLoader AnyIPEndpoint(int port, Action<ListenOptions> configure)
|
||||
{
|
||||
if (configure == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configure));
|
||||
}
|
||||
|
||||
EndpointsToAdd.Add(() =>
|
||||
{
|
||||
Options.ListenAnyIP(port, configure);
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bind to given Unix domain socket path.
|
||||
/// </summary>
|
||||
public KestrelConfigurationLoader UnixSocketEndpoint(string socketPath) => UnixSocketEndpoint(socketPath, _ => { });
|
||||
|
||||
/// <summary>
|
||||
/// Bind to given Unix domain socket path.
|
||||
/// </summary>
|
||||
public KestrelConfigurationLoader UnixSocketEndpoint(string socketPath, Action<ListenOptions> 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));
|
||||
}
|
||||
|
||||
EndpointsToAdd.Add(() =>
|
||||
{
|
||||
Options.ListenUnixSocket(socketPath, configure);
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open a socket file descriptor.
|
||||
/// </summary>
|
||||
public KestrelConfigurationLoader HandleEndpoint(ulong handle) => HandleEndpoint(handle, _ => { });
|
||||
|
||||
/// <summary>
|
||||
/// Open a socket file descriptor.
|
||||
/// </summary>
|
||||
public KestrelConfigurationLoader HandleEndpoint(ulong handle, Action<ListenOptions> configure)
|
||||
{
|
||||
if (configure == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configure));
|
||||
}
|
||||
|
||||
EndpointsToAdd.Add(() =>
|
||||
{
|
||||
Options.ListenHandle(handle, configure);
|
||||
});
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public void Load()
|
||||
{
|
||||
if (Options.ConfigurationLoader == null)
|
||||
{
|
||||
// The loader has already been run.
|
||||
return;
|
||||
}
|
||||
Options.ConfigurationLoader = null;
|
||||
|
||||
var configReader = new ConfigurationReader(Configuration);
|
||||
|
||||
LoadDefaultCert(configReader);
|
||||
|
||||
foreach (var endpoint in configReader.Endpoints)
|
||||
{
|
||||
var listenOptions = AddressBinder.ParseAddress(endpoint.Url, out var https);
|
||||
Options.ApplyEndpointDefaults(listenOptions);
|
||||
|
||||
// Compare to UseHttps(httpsOptions => { })
|
||||
var httpsOptions = new HttpsConnectionAdapterOptions();
|
||||
if (https)
|
||||
{
|
||||
// Defaults
|
||||
Options.ApplyHttpsDefaults(httpsOptions);
|
||||
|
||||
// Specified
|
||||
httpsOptions.ServerCertificate = LoadCertificate(endpoint.Certificate, endpoint.Name)
|
||||
?? httpsOptions.ServerCertificate;
|
||||
}
|
||||
|
||||
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.ConnectionAdapters.Any(f => f.IsHttps))
|
||||
{
|
||||
if (httpsOptions.ServerCertificate == null)
|
||||
{
|
||||
throw new InvalidOperationException(CoreStrings.NoCertSpecifiedNoDevelopmentCertificateFound);
|
||||
}
|
||||
|
||||
listenOptions.UseHttps(httpsOptions);
|
||||
}
|
||||
|
||||
Options.ListenOptions.Add(listenOptions);
|
||||
}
|
||||
|
||||
foreach (var action in EndpointsToAdd)
|
||||
{
|
||||
action();
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadDefaultCert(ConfigurationReader configReader)
|
||||
{
|
||||
if (configReader.Certificates.TryGetValue("Default", out var defaultCertConfig))
|
||||
{
|
||||
var defaultCert = LoadCertificate(defaultCertConfig, "Default");
|
||||
if (defaultCert != null)
|
||||
{
|
||||
Options.DefaultCertificate = defaultCert;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private X509Certificate2 LoadCertificate(CertificateConfig certInfo, string endpointName)
|
||||
{
|
||||
if (certInfo.IsFileCert && certInfo.IsStoreCert)
|
||||
{
|
||||
throw new InvalidOperationException(CoreStrings.FormatMultipleCertificateSources(endpointName));
|
||||
}
|
||||
else if (certInfo.IsFileCert)
|
||||
{
|
||||
var env = Options.ApplicationServices.GetRequiredService<IHostingEnvironment>();
|
||||
return new X509Certificate2(Path.Combine(env.ContentRootPath, certInfo.Path), certInfo.Password);
|
||||
}
|
||||
else if (certInfo.IsStoreCert)
|
||||
{
|
||||
return LoadFromStoreCert(certInfo);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static X509Certificate2 LoadFromStoreCert(CertificateConfig certInfo)
|
||||
{
|
||||
var subject = certInfo.Subject;
|
||||
var storeName = certInfo.Store;
|
||||
var location = certInfo.Location;
|
||||
var storeLocation = StoreLocation.CurrentUser;
|
||||
if (!string.IsNullOrEmpty(location))
|
||||
{
|
||||
storeLocation = (StoreLocation)Enum.Parse(typeof(StoreLocation), location, ignoreCase: true);
|
||||
}
|
||||
var allowInvalid = certInfo.AllowInvalid ?? false;
|
||||
|
||||
return CertificateLoader.LoadFromStoreCert(subject, storeName, storeLocation, allowInvalid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -22,7 +22,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
private readonly List<ITransport> _transports = new List<ITransport>();
|
||||
private readonly Heartbeat _heartbeat;
|
||||
private readonly IServerAddressesFeature _serverAddresses;
|
||||
private readonly IDefaultHttpsProvider _defaultHttpsProvider;
|
||||
private readonly ITransportFactory _transportFactory;
|
||||
|
||||
private bool _hasStarted;
|
||||
|
|
@ -34,12 +33,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
{
|
||||
}
|
||||
|
||||
public KestrelServer(IOptions<KestrelServerOptions> options, ITransportFactory transportFactory, ILoggerFactory loggerFactory, IDefaultHttpsProvider defaultHttpsProvider)
|
||||
: this(transportFactory, CreateServiceContext(options, loggerFactory))
|
||||
{
|
||||
_defaultHttpsProvider = defaultHttpsProvider;
|
||||
}
|
||||
|
||||
// For testing
|
||||
internal KestrelServer(ITransportFactory transportFactory, ServiceContext serviceContext)
|
||||
{
|
||||
|
|
@ -159,7 +152,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
await transport.BindAsync().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await AddressBinder.BindAsync(_serverAddresses, Options, Trace, _defaultHttpsProvider, OnBind).ConfigureAwait(false);
|
||||
await AddressBinder.BindAsync(_serverAddresses, Options, Trace, OnBind).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
@ -224,6 +217,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
|
||||
private void ValidateOptions()
|
||||
{
|
||||
Options.ConfigurationLoader?.Load();
|
||||
|
||||
if (Options.Limits.MaxRequestBufferSize.HasValue &&
|
||||
Options.Limits.MaxRequestBufferSize < Options.Limits.MaxRequestLineSize)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,8 +4,11 @@
|
|||
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
|
||||
{
|
||||
|
|
@ -55,6 +58,78 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
/// </summary>
|
||||
public KestrelServerLimits Limits { get; } = new KestrelServerLimits();
|
||||
|
||||
/// <summary>
|
||||
/// Provides a configuration source where endpoints will be loaded from on server start.
|
||||
/// The default is null.
|
||||
/// </summary>
|
||||
public KestrelConfigurationLoader ConfigurationLoader { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// A default configuration action for all endpoints. Use for Listen, configuration, the default url, and URLs.
|
||||
/// </summary>
|
||||
private Action<ListenOptions> EndpointDefaults { get; set; } = _ => { };
|
||||
|
||||
/// <summary>
|
||||
/// A default configuration action for all https endpoints.
|
||||
/// </summary>
|
||||
private Action<HttpsConnectionAdapterOptions> HttpsDefaults { get; set; } = _ => { };
|
||||
|
||||
/// <summary>
|
||||
/// The default server certificate for https endpoints. This is applied before HttpsDefaults.
|
||||
/// </summary>
|
||||
internal X509Certificate2 DefaultCertificate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Specifies a configuration Action to run for each newly created endpoint. Calling this again will replace
|
||||
/// the prior action.
|
||||
/// </summary>
|
||||
public void ConfigureEndpointDefaults(Action<ListenOptions> configureOptions)
|
||||
{
|
||||
EndpointDefaults = configureOptions ?? throw new ArgumentNullException(nameof(configureOptions));
|
||||
}
|
||||
|
||||
internal void ApplyEndpointDefaults(ListenOptions listenOptions)
|
||||
{
|
||||
listenOptions.KestrelServerOptions = this;
|
||||
EndpointDefaults(listenOptions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies a configuration Action to run for each newly created https endpoint. Calling this again will replace
|
||||
/// the prior action.
|
||||
/// </summary>
|
||||
public void ConfigureHttpsDefaults(Action<HttpsConnectionAdapterOptions> configureOptions)
|
||||
{
|
||||
HttpsDefaults = configureOptions ?? throw new ArgumentNullException(nameof(configureOptions));
|
||||
}
|
||||
|
||||
internal void ApplyHttpsDefaults(HttpsConnectionAdapterOptions httpsOptions)
|
||||
{
|
||||
httpsOptions.ServerCertificate = DefaultCertificate;
|
||||
HttpsDefaults(httpsOptions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a configuration loader for setting up Kestrel.
|
||||
/// </summary>
|
||||
public KestrelConfigurationLoader Configure()
|
||||
{
|
||||
var loader = new KestrelConfigurationLoader(this, new ConfigurationBuilder().Build());
|
||||
ConfigurationLoader = loader;
|
||||
return loader;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public KestrelConfigurationLoader Configure(IConfiguration config)
|
||||
{
|
||||
var loader = new KestrelConfigurationLoader(this, config);
|
||||
ConfigurationLoader = loader;
|
||||
return loader;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bind to given IP address and port.
|
||||
/// </summary>
|
||||
|
|
@ -100,13 +175,22 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
throw new ArgumentNullException(nameof(configure));
|
||||
}
|
||||
|
||||
var listenOptions = new ListenOptions(endPoint) { KestrelServerOptions = this };
|
||||
var listenOptions = new ListenOptions(endPoint);
|
||||
ApplyEndpointDefaults(listenOptions);
|
||||
configure(listenOptions);
|
||||
ListenOptions.Add(listenOptions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public void ListenLocalhost(int port) => ListenLocalhost(port, options => { });
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public void ListenLocalhost(int port, Action<ListenOptions> configure)
|
||||
{
|
||||
if (configure == null)
|
||||
|
|
@ -114,16 +198,20 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
throw new ArgumentNullException(nameof(configure));
|
||||
}
|
||||
|
||||
var listenOptions = new LocalhostListenOptions(port)
|
||||
{
|
||||
KestrelServerOptions = this,
|
||||
};
|
||||
var listenOptions = new LocalhostListenOptions(port);
|
||||
ApplyEndpointDefaults(listenOptions);
|
||||
configure(listenOptions);
|
||||
ListenOptions.Add(listenOptions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Listens on all IPs using IPv6 [::], or IPv4 0.0.0.0 if IPv6 is not supported.
|
||||
/// </summary>
|
||||
public void ListenAnyIP(int port) => ListenAnyIP(port, options => { });
|
||||
|
||||
/// <summary>
|
||||
/// Listens on all IPs using IPv6 [::], or IPv4 0.0.0.0 if IPv6 is not supported.
|
||||
/// </summary>
|
||||
public void ListenAnyIP(int port, Action<ListenOptions> configure)
|
||||
{
|
||||
if (configure == null)
|
||||
|
|
@ -131,10 +219,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
throw new ArgumentNullException(nameof(configure));
|
||||
}
|
||||
|
||||
var listenOptions = new AnyIPListenOptions(port)
|
||||
{
|
||||
KestrelServerOptions = this,
|
||||
};
|
||||
var listenOptions = new AnyIPListenOptions(port);
|
||||
ApplyEndpointDefaults(listenOptions);
|
||||
configure(listenOptions);
|
||||
ListenOptions.Add(listenOptions);
|
||||
}
|
||||
|
|
@ -166,7 +252,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
throw new ArgumentNullException(nameof(configure));
|
||||
}
|
||||
|
||||
var listenOptions = new ListenOptions(socketPath) { KestrelServerOptions = this };
|
||||
var listenOptions = new ListenOptions(socketPath);
|
||||
ApplyEndpointDefaults(listenOptions);
|
||||
configure(listenOptions);
|
||||
ListenOptions.Add(listenOptions);
|
||||
}
|
||||
|
|
@ -190,7 +277,8 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
throw new ArgumentNullException(nameof(configure));
|
||||
}
|
||||
|
||||
var listenOptions = new ListenOptions(handle) { KestrelServerOptions = this };
|
||||
var listenOptions = new ListenOptions(handle);
|
||||
ApplyEndpointDefaults(listenOptions);
|
||||
configure(listenOptions);
|
||||
ListenOptions.Add(listenOptions);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
// 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.Security.Cryptography.X509Certificates;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Https;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Https.Internal;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
|
@ -16,18 +18,21 @@ namespace Microsoft.AspNetCore.Hosting
|
|||
/// </summary>
|
||||
public static class ListenOptionsHttpsExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Configure Kestrel to use HTTPS with the default certificate if available.
|
||||
/// This will throw if no default certificate is configured.
|
||||
/// </summary>
|
||||
/// <param name="listenOptions">The <see cref="ListenOptions"/> to configure.</param>
|
||||
/// <returns>The <see cref="ListenOptions"/>.</returns>
|
||||
public static ListenOptions UseHttps(this ListenOptions listenOptions) => listenOptions.UseHttps(_ => { });
|
||||
|
||||
/// <summary>
|
||||
/// Configure Kestrel to use HTTPS.
|
||||
/// </summary>
|
||||
/// <param name="listenOptions">
|
||||
/// The <see cref="ListenOptions"/> to configure.
|
||||
/// </param>
|
||||
/// <param name="fileName">
|
||||
/// The name of a certificate file, relative to the directory that contains the application content files.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The <see cref="ListenOptions"/>.
|
||||
/// </returns>
|
||||
/// <param name="listenOptions">The <see cref="ListenOptions"/> to configure.</param>
|
||||
/// <param name="fileName">The name of a certificate file, relative to the directory that contains the application
|
||||
/// content files.</param>
|
||||
/// <returns>The <see cref="ListenOptions"/>.</returns>
|
||||
public static ListenOptions UseHttps(this ListenOptions listenOptions, string fileName)
|
||||
{
|
||||
var env = listenOptions.KestrelServerOptions.ApplicationServices.GetRequiredService<IHostingEnvironment>();
|
||||
|
|
@ -37,18 +42,11 @@ namespace Microsoft.AspNetCore.Hosting
|
|||
/// <summary>
|
||||
/// Configure Kestrel to use HTTPS.
|
||||
/// </summary>
|
||||
/// <param name="listenOptions">
|
||||
/// The <see cref="ListenOptions"/> to configure.
|
||||
/// </param>
|
||||
/// <param name="fileName">
|
||||
/// The name of a certificate file, relative to the directory that contains the application content files.
|
||||
/// </param>
|
||||
/// <param name="password">
|
||||
/// The password required to access the X.509 certificate data.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The <see cref="ListenOptions"/>.
|
||||
/// </returns>
|
||||
/// <param name="listenOptions">The <see cref="ListenOptions"/> to configure.</param>
|
||||
/// <param name="fileName">The name of a certificate file, relative to the directory that contains the application
|
||||
/// content files.</param>
|
||||
/// <param name="password">The password required to access the X.509 certificate data.</param>
|
||||
/// <returns>The <see cref="ListenOptions"/>.</returns>
|
||||
public static ListenOptions UseHttps(this ListenOptions listenOptions, string fileName, string password)
|
||||
{
|
||||
var env = listenOptions.KestrelServerOptions.ApplicationServices.GetRequiredService<IHostingEnvironment>();
|
||||
|
|
@ -58,32 +56,157 @@ namespace Microsoft.AspNetCore.Hosting
|
|||
/// <summary>
|
||||
/// Configure Kestrel to use HTTPS.
|
||||
/// </summary>
|
||||
/// <param name="listenOptions">
|
||||
/// The <see cref="ListenOptions"/> to configure.
|
||||
/// </param>
|
||||
/// <param name="serverCertificate">
|
||||
/// The X.509 certificate.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The <see cref="ListenOptions"/>.
|
||||
/// </returns>
|
||||
public static ListenOptions UseHttps(this ListenOptions listenOptions, X509Certificate2 serverCertificate)
|
||||
/// <param name="listenOptions">The <see cref="ListenOptions"/> to configure.</param>
|
||||
/// <param name="fileName">The name of a certificate file, relative to the directory that contains the application content files.</param>
|
||||
/// <param name="password">The password required to access the X.509 certificate data.</param>
|
||||
/// <param name="configureOptions">An Action to configure the <see cref="HttpsConnectionAdapterOptions"/>.</param>
|
||||
/// <returns>The <see cref="ListenOptions"/>.</returns>
|
||||
public static ListenOptions UseHttps(this ListenOptions listenOptions, string fileName, string password,
|
||||
Action<HttpsConnectionAdapterOptions> configureOptions)
|
||||
{
|
||||
return listenOptions.UseHttps(new HttpsConnectionAdapterOptions { ServerCertificate = serverCertificate });
|
||||
var env = listenOptions.KestrelServerOptions.ApplicationServices.GetRequiredService<IHostingEnvironment>();
|
||||
return listenOptions.UseHttps(new X509Certificate2(Path.Combine(env.ContentRootPath, fileName), password), configureOptions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configure Kestrel to use HTTPS.
|
||||
/// </summary>
|
||||
/// <param name="listenOptions">
|
||||
/// The <see cref="ListenOptions"/> to configure.
|
||||
/// </param>
|
||||
/// <param name="httpsOptions">
|
||||
/// Options to configure HTTPS.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// The <see cref="ListenOptions"/>.
|
||||
/// </returns>
|
||||
/// <param name="listenOptions">The <see cref="ListenOptions"/> to configure.</param>
|
||||
/// <param name="storeName">The certificate store to load the certificate from.</param>
|
||||
/// <param name="subject">The subject name for the certificate to load.</param>
|
||||
/// <returns>The <see cref="ListenOptions"/>.</returns>
|
||||
public static ListenOptions UseHttps(this ListenOptions listenOptions, StoreName storeName, string subject)
|
||||
=> listenOptions.UseHttps(storeName, subject, allowInvalid: false);
|
||||
|
||||
/// <summary>
|
||||
/// Configure Kestrel to use HTTPS.
|
||||
/// </summary>
|
||||
/// <param name="listenOptions">The <see cref="ListenOptions"/> to configure.</param>
|
||||
/// <param name="storeName">The certificate store to load the certificate from.</param>
|
||||
/// <param name="subject">The subject name for the certificate to load.</param>
|
||||
/// <param name="allowInvalid">Indicates if invalid certificates should be considered, such as self-signed certificates.</param>
|
||||
/// <returns>The <see cref="ListenOptions"/>.</returns>
|
||||
public static ListenOptions UseHttps(this ListenOptions listenOptions, StoreName storeName, string subject, bool allowInvalid)
|
||||
=> listenOptions.UseHttps(storeName, subject, allowInvalid, StoreLocation.CurrentUser);
|
||||
|
||||
/// <summary>
|
||||
/// Configure Kestrel to use HTTPS.
|
||||
/// </summary>
|
||||
/// <param name="listenOptions">The <see cref="ListenOptions"/> to configure.</param>
|
||||
/// <param name="storeName">The certificate store to load the certificate from.</param>
|
||||
/// <param name="subject">The subject name for the certificate to load.</param>
|
||||
/// <param name="allowInvalid">Indicates if invalid certificates should be considered, such as self-signed certificates.</param>
|
||||
/// <param name="location">The store location to load the certificate from.</param>
|
||||
/// <returns>The <see cref="ListenOptions"/>.</returns>
|
||||
public static ListenOptions UseHttps(this ListenOptions listenOptions, StoreName storeName, string subject, bool allowInvalid, StoreLocation location)
|
||||
=> listenOptions.UseHttps(storeName, subject, allowInvalid, location, configureOptions: _ => { });
|
||||
|
||||
/// <summary>
|
||||
/// Configure Kestrel to use HTTPS.
|
||||
/// </summary>
|
||||
/// <param name="listenOptions">The <see cref="ListenOptions"/> to configure.</param>
|
||||
/// <param name="storeName">The certificate store to load the certificate from.</param>
|
||||
/// <param name="subject">The subject name for the certificate to load.</param>
|
||||
/// <param name="allowInvalid">Indicates if invalid certificates should be considered, such as self-signed certificates.</param>
|
||||
/// <param name="location">The store location to load the certificate from.</param>
|
||||
/// <param name="configureOptions">An Action to configure the <see cref="HttpsConnectionAdapterOptions"/>.</param>
|
||||
/// <returns>The <see cref="ListenOptions"/>.</returns>
|
||||
public static ListenOptions UseHttps(this ListenOptions listenOptions, StoreName storeName, string subject, bool allowInvalid, StoreLocation location,
|
||||
Action<HttpsConnectionAdapterOptions> configureOptions)
|
||||
{
|
||||
return listenOptions.UseHttps(CertificateLoader.LoadFromStoreCert(subject, storeName.ToString(), location, allowInvalid), configureOptions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configure Kestrel to use HTTPS.
|
||||
/// </summary>
|
||||
/// <param name="listenOptions"> The <see cref="ListenOptions"/> to configure.</param>
|
||||
/// <param name="serverCertificate">The X.509 certificate.</param>
|
||||
/// <returns>The <see cref="ListenOptions"/>.</returns>
|
||||
public static ListenOptions UseHttps(this ListenOptions listenOptions, X509Certificate2 serverCertificate)
|
||||
{
|
||||
if (serverCertificate == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(serverCertificate));
|
||||
}
|
||||
|
||||
return listenOptions.UseHttps(options =>
|
||||
{
|
||||
options.ServerCertificate = serverCertificate;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configure Kestrel to use HTTPS.
|
||||
/// </summary>
|
||||
/// <param name="listenOptions">The <see cref="ListenOptions"/> to configure.</param>
|
||||
/// <param name="serverCertificate">The X.509 certificate.</param>
|
||||
/// <param name="configureOptions">An Action to configure the <see cref="HttpsConnectionAdapterOptions"/>.</param>
|
||||
/// <returns>The <see cref="ListenOptions"/>.</returns>
|
||||
public static ListenOptions UseHttps(this ListenOptions listenOptions, X509Certificate2 serverCertificate,
|
||||
Action<HttpsConnectionAdapterOptions> configureOptions)
|
||||
{
|
||||
if (serverCertificate == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(serverCertificate));
|
||||
}
|
||||
|
||||
if (configureOptions == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configureOptions));
|
||||
}
|
||||
|
||||
return listenOptions.UseHttps(options =>
|
||||
{
|
||||
options.ServerCertificate = serverCertificate;
|
||||
configureOptions(options);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configure Kestrel to use HTTPS.
|
||||
/// </summary>
|
||||
/// <param name="listenOptions">The <see cref="ListenOptions"/> to configure.</param>
|
||||
/// <param name="configureOptions">An action to configure options for HTTPS.</param>
|
||||
/// <returns>The <see cref="ListenOptions"/>.</returns>
|
||||
public static ListenOptions UseHttps(this ListenOptions listenOptions, Action<HttpsConnectionAdapterOptions> configureOptions)
|
||||
{
|
||||
if (configureOptions == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configureOptions));
|
||||
}
|
||||
|
||||
var options = new HttpsConnectionAdapterOptions();
|
||||
listenOptions.KestrelServerOptions.ApplyHttpsDefaults(options);
|
||||
configureOptions(options);
|
||||
|
||||
if (options.ServerCertificate == null)
|
||||
{
|
||||
throw new InvalidOperationException(CoreStrings.NoCertSpecifiedNoDevelopmentCertificateFound);
|
||||
}
|
||||
return listenOptions.UseHttps(options);
|
||||
}
|
||||
|
||||
// Use Https if a default cert is available
|
||||
internal static bool TryUseHttps(this ListenOptions listenOptions)
|
||||
{
|
||||
var options = new HttpsConnectionAdapterOptions();
|
||||
listenOptions.KestrelServerOptions.ApplyHttpsDefaults(options);
|
||||
|
||||
if (options.ServerCertificate == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
listenOptions.UseHttps(options);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configure Kestrel to use HTTPS.
|
||||
/// </summary>
|
||||
/// <param name="listenOptions">The <see cref="ListenOptions"/> to configure.</param>
|
||||
/// <param name="httpsOptions">Options to configure HTTPS.</param>
|
||||
/// <returns>The <see cref="ListenOptions"/>.</returns>
|
||||
public static ListenOptions UseHttps(this ListenOptions listenOptions, HttpsConnectionAdapterOptions httpsOptions)
|
||||
{
|
||||
var loggerFactory = listenOptions.KestrelServerOptions.ApplicationServices.GetRequiredService<ILoggerFactory>();
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Server.Kestrel.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv.FunctionalTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.FunctionalTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Server.Kestrel.Core.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
|
|
|
|||
|
|
@ -1704,6 +1704,76 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core
|
|||
internal static string FormatServiceCertificateRequired()
|
||||
=> GetString("ServiceCertificateRequired");
|
||||
|
||||
/// <summary>
|
||||
/// No listening endpoints were configured. Binding to {address0} and {address1} by default.
|
||||
/// </summary>
|
||||
internal static string BindingToDefaultAddresses
|
||||
{
|
||||
get => GetString("BindingToDefaultAddresses");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// No listening endpoints were configured. Binding to {address0} and {address1} by default.
|
||||
/// </summary>
|
||||
internal static string FormatBindingToDefaultAddresses(object address0, object address1)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("BindingToDefaultAddresses", "address0", "address1"), address0, address1);
|
||||
|
||||
/// <summary>
|
||||
/// The requested certificate {subject} could not be found in {storeLocation}/{storeName} with AllowInvalid setting: {allowInvalid}.
|
||||
/// </summary>
|
||||
internal static string CertNotFoundInStore
|
||||
{
|
||||
get => GetString("CertNotFoundInStore");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The requested certificate {subject} could not be found in {storeLocation}/{storeName} with AllowInvalid setting: {allowInvalid}.
|
||||
/// </summary>
|
||||
internal static string FormatCertNotFoundInStore(object subject, object storeLocation, object storeName, object allowInvalid)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("CertNotFoundInStore", "subject", "storeLocation", "storeName", "allowInvalid"), subject, storeLocation, storeName, allowInvalid);
|
||||
|
||||
/// <summary>
|
||||
/// The endpoint {endpointName} is missing the required 'Url' parameter.
|
||||
/// </summary>
|
||||
internal static string EndpointMissingUrl
|
||||
{
|
||||
get => GetString("EndpointMissingUrl");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The endpoint {endpointName} is missing the required 'Url' parameter.
|
||||
/// </summary>
|
||||
internal static string FormatEndpointMissingUrl(object endpointName)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("EndpointMissingUrl", "endpointName"), endpointName);
|
||||
|
||||
/// <summary>
|
||||
/// Unable to configure HTTPS endpoint. No server certificate was specified and the default developer certificate could not be found. Try running 'dotnet developercertificates https -t' to setup a developer certificate for use with localhost. For information on configuring HTTPS see https://go.microsoft.com/fwlink/?linkid=848054
|
||||
/// </summary>
|
||||
internal static string NoCertSpecifiedNoDevelopmentCertificateFound
|
||||
{
|
||||
get => GetString("NoCertSpecifiedNoDevelopmentCertificateFound");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unable to configure HTTPS endpoint. No server certificate was specified and the default developer certificate could not be found. Try running 'dotnet developercertificates https -t' to setup a developer certificate for use with localhost. For information on configuring HTTPS see https://go.microsoft.com/fwlink/?linkid=848054
|
||||
/// </summary>
|
||||
internal static string FormatNoCertSpecifiedNoDevelopmentCertificateFound()
|
||||
=> GetString("NoCertSpecifiedNoDevelopmentCertificateFound");
|
||||
|
||||
/// <summary>
|
||||
/// The endpoint {endpointName} specified multiple certificate sources.
|
||||
/// </summary>
|
||||
internal static string MultipleCertificateSources
|
||||
{
|
||||
get => GetString("MultipleCertificateSources");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The endpoint {endpointName} specified multiple certificate sources.
|
||||
/// </summary>
|
||||
internal static string FormatMultipleCertificateSources(object endpointName)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("MultipleCertificateSources", "endpointName"), endpointName);
|
||||
|
||||
private static string GetString(string name, params string[] formatterNames)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
|
|
|
|||
|
|
@ -1,42 +0,0 @@
|
|||
// 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.Linq;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using Microsoft.AspNetCore.Certificates.Generation;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Internal
|
||||
{
|
||||
public class DefaultHttpsProvider : IDefaultHttpsProvider
|
||||
{
|
||||
private static readonly CertificateManager _certificateManager = new CertificateManager();
|
||||
|
||||
private readonly ILogger<DefaultHttpsProvider> _logger;
|
||||
|
||||
public DefaultHttpsProvider(ILogger<DefaultHttpsProvider> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public void ConfigureHttps(ListenOptions listenOptions)
|
||||
{
|
||||
var certificate = _certificateManager.ListCertificates(CertificatePurpose.HTTPS, StoreName.My, StoreLocation.CurrentUser, isValid: true)
|
||||
.FirstOrDefault();
|
||||
if (certificate != null)
|
||||
{
|
||||
_logger.LocatedDevelopmentCertificate(certificate);
|
||||
listenOptions.UseHttps(certificate);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.UnableToLocateDevelopmentCertificate();
|
||||
throw new InvalidOperationException(KestrelStrings.HttpsUrlProvidedButNoDevelopmentCertificateFound);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -12,8 +12,6 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="$(MicrosoftAspNetCoreHostingPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Certificates.Generation.Sources" PrivateAssets="All" Version="$(MicrosoftAspNetCoreCertificatesGenerationSourcesPackageVersion)" />
|
||||
<PackageReference Include="System.Security.Cryptography.Cng" Version="$(SystemSecurityCryptographyCngPackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -1,123 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="HttpsUrlProvidedButNoDevelopmentCertificateFound" xml:space="preserve">
|
||||
<value>Unable to configure HTTPS endpoint. Try running 'dotnet developercertificates https -t' to setup a developer certificate for use with localhost. For information on configuring HTTPS see https://go.microsoft.com/fwlink/?linkid=848054</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
// <auto-generated />
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel
|
||||
{
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Resources;
|
||||
|
||||
internal static class KestrelStrings
|
||||
{
|
||||
private static readonly ResourceManager _resourceManager
|
||||
= new ResourceManager("Microsoft.AspNetCore.Server.Kestrel.KestrelStrings", typeof(KestrelStrings).GetTypeInfo().Assembly);
|
||||
|
||||
/// <summary>
|
||||
/// Unable to configure HTTPS endpoint. Try running 'dotnet developercertificates https -t' to setup a developer certificate for use with localhost. For information on configuring HTTPS see https://go.microsoft.com/fwlink/?linkid=848054
|
||||
/// </summary>
|
||||
internal static string HttpsUrlProvidedButNoDevelopmentCertificateFound
|
||||
{
|
||||
get => GetString("HttpsUrlProvidedButNoDevelopmentCertificateFound");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unable to configure HTTPS endpoint. Try running 'dotnet developercertificates https -t' to setup a developer certificate for use with localhost. For information on configuring HTTPS see https://go.microsoft.com/fwlink/?linkid=848054
|
||||
/// </summary>
|
||||
internal static string FormatHttpsUrlProvidedButNoDevelopmentCertificateFound()
|
||||
=> GetString("HttpsUrlProvidedButNoDevelopmentCertificateFound");
|
||||
|
||||
private static string GetString(string name, params string[] formatterNames)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
|
||||
System.Diagnostics.Debug.Assert(value != null);
|
||||
|
||||
if (formatterNames != null)
|
||||
{
|
||||
for (var i = 0; i < formatterNames.Length; i++)
|
||||
{
|
||||
value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}");
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -34,7 +34,6 @@ namespace Microsoft.AspNetCore.Hosting
|
|||
|
||||
services.AddTransient<IConfigureOptions<KestrelServerOptions>, KestrelServerOptionsSetup>();
|
||||
services.AddSingleton<IServer, KestrelServer>();
|
||||
services.AddSingleton<IDefaultHttpsProvider, DefaultHttpsProvider>();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -54,46 +54,50 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
public void ParseAddressDefaultsToAnyIPOnInvalidIPAddress(string host)
|
||||
{
|
||||
var options = new KestrelServerOptions();
|
||||
var listenOptions = AddressBinder.ParseAddress($"http://{host}", options, Mock.Of<IDefaultHttpsProvider>());
|
||||
var listenOptions = AddressBinder.ParseAddress($"http://{host}", out var https);
|
||||
Assert.IsType<AnyIPListenOptions>(listenOptions);
|
||||
Assert.Equal(ListenType.IPEndPoint, listenOptions.Type);
|
||||
Assert.Equal(IPAddress.IPv6Any, listenOptions.IPEndPoint.Address);
|
||||
Assert.Equal(80, listenOptions.IPEndPoint.Port);
|
||||
Assert.False(https);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParseAddressLocalhost()
|
||||
{
|
||||
var options = new KestrelServerOptions();
|
||||
var listenOptions = AddressBinder.ParseAddress("http://localhost", options, Mock.Of<IDefaultHttpsProvider>());
|
||||
var listenOptions = AddressBinder.ParseAddress("http://localhost", out var https);
|
||||
Assert.IsType<LocalhostListenOptions>(listenOptions);
|
||||
Assert.Equal(ListenType.IPEndPoint, listenOptions.Type);
|
||||
Assert.Equal(IPAddress.Loopback, listenOptions.IPEndPoint.Address);
|
||||
Assert.Equal(80, listenOptions.IPEndPoint.Port);
|
||||
Assert.False(https);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParseAddressUnixPipe()
|
||||
{
|
||||
var options = new KestrelServerOptions();
|
||||
var listenOptions = AddressBinder.ParseAddress("http://unix:/tmp/kestrel-test.sock", options, Mock.Of<IDefaultHttpsProvider>());
|
||||
var listenOptions = AddressBinder.ParseAddress("http://unix:/tmp/kestrel-test.sock", out var https);
|
||||
Assert.Equal(ListenType.SocketPath, listenOptions.Type);
|
||||
Assert.Equal("/tmp/kestrel-test.sock", listenOptions.SocketPath);
|
||||
Assert.False(https);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("http://10.10.10.10:5000/", "10.10.10.10", 5000)]
|
||||
[InlineData("http://[::1]:5000", "::1", 5000)]
|
||||
[InlineData("http://[::1]", "::1", 80)]
|
||||
[InlineData("http://127.0.0.1", "127.0.0.1", 80)]
|
||||
[InlineData("https://127.0.0.1", "127.0.0.1", 443)]
|
||||
public void ParseAddressIP(string address, string ip, int port)
|
||||
[InlineData("http://10.10.10.10:5000/", "10.10.10.10", 5000, false)]
|
||||
[InlineData("http://[::1]:5000", "::1", 5000, false)]
|
||||
[InlineData("http://[::1]", "::1", 80, false)]
|
||||
[InlineData("http://127.0.0.1", "127.0.0.1", 80, false)]
|
||||
[InlineData("https://127.0.0.1", "127.0.0.1", 443, true)]
|
||||
public void ParseAddressIP(string address, string ip, int port, bool isHttps)
|
||||
{
|
||||
var options = new KestrelServerOptions();
|
||||
var listenOptions = AddressBinder.ParseAddress(address, options, Mock.Of<IDefaultHttpsProvider>());
|
||||
var listenOptions = AddressBinder.ParseAddress(address, out var https);
|
||||
Assert.Equal(ListenType.IPEndPoint, listenOptions.Type);
|
||||
Assert.Equal(IPAddress.Parse(ip), listenOptions.IPEndPoint.Address);
|
||||
Assert.Equal(port, listenOptions.IPEndPoint.Port);
|
||||
Assert.Equal(isHttps, https);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -107,7 +111,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
AddressBinder.BindAsync(addresses,
|
||||
options,
|
||||
NullLogger.Instance,
|
||||
Mock.Of<IDefaultHttpsProvider>(),
|
||||
endpoint => throw new AddressInUseException("already in use")));
|
||||
}
|
||||
|
||||
|
|
@ -128,7 +131,6 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
await AddressBinder.BindAsync(addresses,
|
||||
options,
|
||||
logger,
|
||||
Mock.Of<IDefaultHttpsProvider>(),
|
||||
endpoint =>
|
||||
{
|
||||
if (endpoint.IPEndPoint.Address == IPAddress.IPv6Any)
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Testing" Version="$(MicrosoftAspNetCoreTestingPackageVersion)" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http" Version="$(MicrosoftAspNetCoreHttpPackageVersion)" />
|
||||
|
|
|
|||
|
|
@ -29,5 +29,40 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
|
||||
Assert.True(options.AllowSynchronousIO);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConfigureEndpointDefaultsAppliesToNewEndpoints()
|
||||
{
|
||||
var options = new KestrelServerOptions();
|
||||
options.ListenLocalhost(5000);
|
||||
|
||||
Assert.True(options.ListenOptions[0].NoDelay);
|
||||
|
||||
options.ConfigureEndpointDefaults(opt =>
|
||||
{
|
||||
opt.NoDelay = false;
|
||||
});
|
||||
|
||||
options.Listen(new IPEndPoint(IPAddress.Loopback, 5000), opt =>
|
||||
{
|
||||
// ConfigureEndpointDefaults runs before this callback
|
||||
Assert.False(opt.NoDelay);
|
||||
});
|
||||
Assert.False(options.ListenOptions[1].NoDelay);
|
||||
|
||||
options.ListenLocalhost(5000, opt =>
|
||||
{
|
||||
Assert.False(opt.NoDelay);
|
||||
opt.NoDelay = true; // Can be overriden
|
||||
});
|
||||
Assert.True(options.ListenOptions[2].NoDelay);
|
||||
|
||||
|
||||
options.ListenAnyIP(5000, opt =>
|
||||
{
|
||||
Assert.False(opt.NoDelay);
|
||||
});
|
||||
Assert.False(options.ListenOptions[3].NoDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,9 +9,9 @@ using System.Threading.Tasks;
|
|||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting.Server;
|
||||
using Microsoft.AspNetCore.Hosting.Server.Features;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.Internal;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Moq;
|
||||
|
|
@ -21,12 +21,21 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
{
|
||||
public class KestrelServerTests
|
||||
{
|
||||
private KestrelServerOptions CreateServerOptions()
|
||||
{
|
||||
var serverOptions = new KestrelServerOptions();
|
||||
serverOptions.ApplicationServices = new ServiceCollection()
|
||||
.AddLogging()
|
||||
.BuildServiceProvider();
|
||||
return serverOptions;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StartWithInvalidAddressThrows()
|
||||
{
|
||||
var testLogger = new TestApplicationErrorLogger { ThrowOnCriticalErrors = false };
|
||||
|
||||
using (var server = CreateServer(new KestrelServerOptions(), testLogger))
|
||||
using (var server = CreateServer(CreateServerOptions(), testLogger))
|
||||
{
|
||||
server.Features.Get<IServerAddressesFeature>().Addresses.Add("http:/asdf");
|
||||
|
||||
|
|
@ -40,34 +49,35 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
[Fact]
|
||||
public void StartWithHttpsAddressConfiguresHttpsEndpoints()
|
||||
{
|
||||
var mockDefaultHttpsProvider = new Mock<IDefaultHttpsProvider>();
|
||||
|
||||
using (var server = CreateServer(new KestrelServerOptions(), mockDefaultHttpsProvider.Object))
|
||||
var options = CreateServerOptions();
|
||||
options.DefaultCertificate = TestResources.GetTestCertificate();
|
||||
using (var server = CreateServer(options))
|
||||
{
|
||||
server.Features.Get<IServerAddressesFeature>().Addresses.Add("https://127.0.0.1:0");
|
||||
|
||||
StartDummyApplication(server);
|
||||
|
||||
mockDefaultHttpsProvider.Verify(provider => provider.ConfigureHttps(It.IsAny<ListenOptions>()), Times.Once);
|
||||
Assert.True(server.Options.ListenOptions.Any());
|
||||
Assert.Contains(server.Options.ListenOptions[0].ConnectionAdapters, adapter => adapter.IsHttps);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void KestrelServerThrowsUsefulExceptionIfDefaultHttpsProviderNotAdded()
|
||||
{
|
||||
using (var server = CreateServer(new KestrelServerOptions(), defaultHttpsProvider: null, throwOnCriticalErrors: false))
|
||||
using (var server = CreateServer(CreateServerOptions(), throwOnCriticalErrors: false))
|
||||
{
|
||||
server.Features.Get<IServerAddressesFeature>().Addresses.Add("https://127.0.0.1:0");
|
||||
|
||||
var ex = Assert.Throws<InvalidOperationException>(() => StartDummyApplication(server));
|
||||
Assert.Equal(CoreStrings.UnableToConfigureHttpsBindings, ex.Message);
|
||||
Assert.Equal(CoreStrings.NoCertSpecifiedNoDevelopmentCertificateFound, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void KestrelServerDoesNotThrowIfNoDefaultHttpsProviderButNoHttpUrls()
|
||||
{
|
||||
using (var server = CreateServer(new KestrelServerOptions(), defaultHttpsProvider: null))
|
||||
using (var server = CreateServer(CreateServerOptions()))
|
||||
{
|
||||
server.Features.Get<IServerAddressesFeature>().Addresses.Add("http://127.0.0.1:0");
|
||||
|
||||
|
|
@ -78,12 +88,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
[Fact]
|
||||
public void KestrelServerDoesNotThrowIfNoDefaultHttpsProviderButManualListenOptions()
|
||||
{
|
||||
var mockDefaultHttpsProvider = new Mock<IDefaultHttpsProvider>();
|
||||
|
||||
var serverOptions = new KestrelServerOptions();
|
||||
var serverOptions = CreateServerOptions();
|
||||
serverOptions.Listen(new IPEndPoint(IPAddress.Loopback, 0));
|
||||
|
||||
using (var server = CreateServer(serverOptions, defaultHttpsProvider: null))
|
||||
using (var server = CreateServer(serverOptions))
|
||||
{
|
||||
server.Features.Get<IServerAddressesFeature>().Addresses.Add("https://127.0.0.1:0");
|
||||
|
||||
|
|
@ -322,9 +330,9 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Tests
|
|||
return new KestrelServer(Options.Create(options), new MockTransportFactory(), new LoggerFactory(new[] { new KestrelTestLoggerProvider(testLogger) }));
|
||||
}
|
||||
|
||||
private static KestrelServer CreateServer(KestrelServerOptions options, IDefaultHttpsProvider defaultHttpsProvider, bool throwOnCriticalErrors = true)
|
||||
private static KestrelServer CreateServer(KestrelServerOptions options, bool throwOnCriticalErrors = true)
|
||||
{
|
||||
return new KestrelServer(Options.Create(options), new MockTransportFactory(), new LoggerFactory(new[] { new KestrelTestLoggerProvider(throwOnCriticalErrors) }), defaultHttpsProvider);
|
||||
return new KestrelServer(Options.Create(options), new MockTransportFactory(), new LoggerFactory(new[] { new KestrelTestLoggerProvider(throwOnCriticalErrors) }));
|
||||
}
|
||||
|
||||
private static void StartDummyApplication(IServer server)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ using System.Linq;
|
|||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
|
|
@ -17,6 +18,7 @@ using Microsoft.AspNetCore.Http;
|
|||
using Microsoft.AspNetCore.Http.Extensions;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Https;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Microsoft.AspNetCore.Testing.xunit;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
|
@ -189,6 +191,40 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
private Task RegisterAddresses_StaticPort_Success(string addressInput, string[] testUrls) =>
|
||||
RunTestWithStaticPort(port => RegisterAddresses_Success($"{addressInput}:{port}", testUrls, port));
|
||||
|
||||
[Fact]
|
||||
public async Task RegisterHttpAddress_UpradedToHttpsByConfigureEndpointDefaults()
|
||||
{
|
||||
var hostBuilder = TransportSelector.GetWebHostBuilder()
|
||||
.UseKestrel(serverOptions =>
|
||||
{
|
||||
serverOptions.ConfigureEndpointDefaults(listenOptions =>
|
||||
{
|
||||
listenOptions.UseHttps(TestResources.GetTestCertificate());
|
||||
});
|
||||
})
|
||||
.ConfigureLogging(_configureLoggingDelegate)
|
||||
.UseUrls("http://127.0.0.1:0")
|
||||
.Configure(app =>
|
||||
{
|
||||
var serverAddresses = app.ServerFeatures.Get<IServerAddressesFeature>();
|
||||
app.Run(context =>
|
||||
{
|
||||
Assert.Single(serverAddresses.Addresses);
|
||||
return context.Response.WriteAsync(serverAddresses.Addresses.First());
|
||||
});
|
||||
});
|
||||
|
||||
using (var host = hostBuilder.Build())
|
||||
{
|
||||
host.Start();
|
||||
|
||||
var expectedUrl = $"https://127.0.0.1:{host.GetPort()}";
|
||||
var response = await HttpClientSlim.GetStringAsync(expectedUrl, validateCertificate: false);
|
||||
|
||||
Assert.Equal(expectedUrl, response);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RunTestWithStaticPort(Func<int, Task> test)
|
||||
{
|
||||
var retryCount = 0;
|
||||
|
|
@ -361,13 +397,40 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
return RegisterDefaultServerAddresses_Success(new[] { "http://127.0.0.1:5000", "http://[::1]:5000" });
|
||||
}
|
||||
|
||||
private async Task RegisterDefaultServerAddresses_Success(IEnumerable<string> addresses)
|
||||
[ConditionalFact]
|
||||
[PortSupportedCondition(5000)]
|
||||
[PortSupportedCondition(5001)]
|
||||
public Task DefaultsServerAddress_BindsToIPv4WithHttps()
|
||||
{
|
||||
return RegisterDefaultServerAddresses_Success(
|
||||
new[] { "http://127.0.0.1:5000", "https://127.0.0.1:5001" }, mockHttps: true);
|
||||
}
|
||||
|
||||
[ConditionalFact]
|
||||
[IPv6SupportedCondition]
|
||||
[PortSupportedCondition(5000)]
|
||||
[PortSupportedCondition(5001)]
|
||||
public Task DefaultsServerAddress_BindsToIPv6WithHttps()
|
||||
{
|
||||
return RegisterDefaultServerAddresses_Success(new[] {
|
||||
"http://127.0.0.1:5000", "http://[::1]:5000",
|
||||
"https://127.0.0.1:5001", "https://[::1]:5001"},
|
||||
mockHttps: true);
|
||||
}
|
||||
|
||||
private async Task RegisterDefaultServerAddresses_Success(IEnumerable<string> addresses, bool mockHttps = false)
|
||||
{
|
||||
var testLogger = new TestApplicationErrorLogger();
|
||||
|
||||
var hostBuilder = TransportSelector.GetWebHostBuilder()
|
||||
.ConfigureLogging(_configureLoggingDelegate)
|
||||
.UseKestrel()
|
||||
.UseKestrel(options =>
|
||||
{
|
||||
if (mockHttps)
|
||||
{
|
||||
options.DefaultCertificate = new X509Certificate2(TestResources.TestCertificatePath, "testPassword");
|
||||
}
|
||||
})
|
||||
.ConfigureLogging(builder => builder
|
||||
.AddProvider(new KestrelTestLoggerProvider(testLogger))
|
||||
.SetMinimumLevel(LogLevel.Debug))
|
||||
|
|
@ -378,13 +441,19 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
host.Start();
|
||||
|
||||
Assert.Equal(5000, host.GetPort());
|
||||
|
||||
if (mockHttps)
|
||||
{
|
||||
Assert.Contains(5001, host.GetPorts());
|
||||
}
|
||||
|
||||
Assert.Single(testLogger.Messages, log => log.LogLevel == LogLevel.Debug &&
|
||||
string.Equals(CoreStrings.FormatBindingToDefaultAddress(Constants.DefaultServerAddress),
|
||||
log.Message, StringComparison.Ordinal));
|
||||
(string.Equals(CoreStrings.FormatBindingToDefaultAddresses(Constants.DefaultServerAddress, Constants.DefaultServerHttpsAddress), log.Message, StringComparison.Ordinal)
|
||||
|| string.Equals(CoreStrings.FormatBindingToDefaultAddress(Constants.DefaultServerAddress), log.Message, StringComparison.Ordinal)));
|
||||
|
||||
foreach (var address in addresses)
|
||||
{
|
||||
Assert.Equal(new Uri(address).ToString(), await HttpClientSlim.GetStringAsync(address));
|
||||
Assert.Equal(new Uri(address).ToString(), await HttpClientSlim.GetStringAsync(address, validateCertificate: false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -933,7 +1002,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
}
|
||||
}
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
|
||||
private class PortSupportedConditionAttribute : Attribute, ITestCondition
|
||||
{
|
||||
private readonly int _port;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,64 @@
|
|||
// 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.Linq;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Https.Internal;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
||||
{
|
||||
public class CertificateLoaderTests
|
||||
{
|
||||
private readonly ITestOutputHelper _output;
|
||||
|
||||
public CertificateLoaderTests(ITestOutputHelper output)
|
||||
{
|
||||
_output = output;
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("no_extensions.pfx")]
|
||||
public void IsCertificateAllowedForServerAuth_AllowWithNoExtensions(string testCertName)
|
||||
{
|
||||
var certPath = TestResources.GetCertPath(testCertName);
|
||||
_output.WriteLine("Loading " + certPath);
|
||||
var cert = new X509Certificate2(certPath, "testPassword");
|
||||
Assert.Empty(cert.Extensions.OfType<X509EnhancedKeyUsageExtension>());
|
||||
|
||||
Assert.True(CertificateLoader.IsCertificateAllowedForServerAuth(cert));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("eku.server.pfx")]
|
||||
[InlineData("eku.multiple_usages.pfx")]
|
||||
public void IsCertificateAllowedForServerAuth_ValidatesEnhancedKeyUsageOnCertificate(string testCertName)
|
||||
{
|
||||
var certPath = TestResources.GetCertPath(testCertName);
|
||||
_output.WriteLine("Loading " + certPath);
|
||||
var cert = new X509Certificate2(certPath, "testPassword");
|
||||
Assert.NotEmpty(cert.Extensions);
|
||||
var eku = Assert.Single(cert.Extensions.OfType<X509EnhancedKeyUsageExtension>());
|
||||
Assert.NotEmpty(eku.EnhancedKeyUsages);
|
||||
|
||||
Assert.True(CertificateLoader.IsCertificateAllowedForServerAuth(cert));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("eku.code_signing.pfx")]
|
||||
[InlineData("eku.client.pfx")]
|
||||
public void IsCertificateAllowedForServerAuth_RejectsCertificatesMissingServerEku(string testCertName)
|
||||
{
|
||||
var certPath = TestResources.GetCertPath(testCertName);
|
||||
_output.WriteLine("Loading " + certPath);
|
||||
var cert = new X509Certificate2(certPath, "testPassword");
|
||||
Assert.NotEmpty(cert.Extensions);
|
||||
var eku = Assert.Single(cert.Extensions.OfType<X509EnhancedKeyUsageExtension>());
|
||||
Assert.NotEmpty(eku.EnhancedKeyUsages);
|
||||
|
||||
Assert.False(CertificateLoader.IsCertificateAllowedForServerAuth(cert));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Security;
|
||||
using System.Net.Sockets;
|
||||
|
|
@ -14,8 +13,11 @@ using System.Threading.Tasks;
|
|||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Https;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Https.Internal;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Internal;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
|
@ -26,6 +28,61 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
{
|
||||
public class HttpsTests
|
||||
{
|
||||
private KestrelServerOptions CreateServerOptions()
|
||||
{
|
||||
var serverOptions = new KestrelServerOptions();
|
||||
serverOptions.ApplicationServices = new ServiceCollection()
|
||||
.AddLogging()
|
||||
.BuildServiceProvider();
|
||||
return serverOptions;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void UseHttpsDefaultsToDefaultCert()
|
||||
{
|
||||
var serverOptions = CreateServerOptions();
|
||||
var defaultCert = new X509Certificate2(TestResources.TestCertificatePath, "testPassword");
|
||||
serverOptions.DefaultCertificate = defaultCert;
|
||||
|
||||
serverOptions.ListenLocalhost(5000, options =>
|
||||
{
|
||||
options.UseHttps();
|
||||
});
|
||||
|
||||
serverOptions.ListenLocalhost(5001, options =>
|
||||
{
|
||||
options.UseHttps(opt =>
|
||||
{
|
||||
Assert.Equal(defaultCert, opt.ServerCertificate);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConfigureHttpsDefaultsOverridesDefaultCert()
|
||||
{
|
||||
var serverOptions = CreateServerOptions();
|
||||
var defaultCert = new X509Certificate2(TestResources.TestCertificatePath, "testPassword");
|
||||
serverOptions.DefaultCertificate = defaultCert;
|
||||
serverOptions.ConfigureHttpsDefaults(options =>
|
||||
{
|
||||
Assert.Equal(defaultCert, options.ServerCertificate);
|
||||
options.ServerCertificate = null;
|
||||
options.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
|
||||
});
|
||||
serverOptions.ListenLocalhost(5000, options =>
|
||||
{
|
||||
options.UseHttps(opt =>
|
||||
{
|
||||
Assert.Null(opt.ServerCertificate);
|
||||
Assert.Equal(ClientCertificateMode.RequireCertificate, opt.ClientCertificateMode);
|
||||
|
||||
// So UseHttps won't throw
|
||||
opt.ServerCertificate = defaultCert;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EmptyRequestLoggedAsInformation()
|
||||
{
|
||||
|
|
@ -270,10 +327,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
{
|
||||
options.Listen(new IPEndPoint(IPAddress.Loopback, 0), listenOptions =>
|
||||
{
|
||||
listenOptions.UseHttps(new HttpsConnectionAdapterOptions
|
||||
listenOptions.UseHttps(o =>
|
||||
{
|
||||
ServerCertificate = new X509Certificate2(TestResources.TestCertificatePath, "testPassword"),
|
||||
HandshakeTimeout = TimeSpan.FromSeconds(1)
|
||||
o.ServerCertificate = new X509Certificate2(TestResources.TestCertificatePath, "testPassword");
|
||||
o.HandshakeTimeout = TimeSpan.FromSeconds(1);
|
||||
});
|
||||
});
|
||||
})
|
||||
|
|
|
|||
|
|
@ -0,0 +1,177 @@
|
|||
// 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.Linq;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Tests
|
||||
{
|
||||
public class ConfigurationReaderTests
|
||||
{
|
||||
[Fact]
|
||||
public void ReadCertificatesWhenNoCertificatsSection_ReturnsEmptyCollection()
|
||||
{
|
||||
var config = new ConfigurationBuilder().AddInMemoryCollection().Build();
|
||||
var reader = new ConfigurationReader(config);
|
||||
var certificates = reader.Certificates;
|
||||
Assert.NotNull(certificates);
|
||||
Assert.False(certificates.Any());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadCertificatesWhenEmptyCertificatsSection_ReturnsEmptyCollection()
|
||||
{
|
||||
var config = new ConfigurationBuilder().AddInMemoryCollection(new[]
|
||||
{
|
||||
new KeyValuePair<string, string>("Certificates", ""),
|
||||
}).Build();
|
||||
var reader = new ConfigurationReader(config);
|
||||
var certificates = reader.Certificates;
|
||||
Assert.NotNull(certificates);
|
||||
Assert.False(certificates.Any());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadCertificatsSection_ReturnsCollection()
|
||||
{
|
||||
var config = new ConfigurationBuilder().AddInMemoryCollection(new[]
|
||||
{
|
||||
new KeyValuePair<string, string>("Certificates:FileCert:Path", "/path/cert.pfx"),
|
||||
new KeyValuePair<string, string>("Certificates:FileCert:Password", "certpassword"),
|
||||
new KeyValuePair<string, string>("Certificates:StoreCert:Subject", "certsubject"),
|
||||
new KeyValuePair<string, string>("Certificates:StoreCert:Store", "certstore"),
|
||||
new KeyValuePair<string, string>("Certificates:StoreCert:Location", "cetlocation"),
|
||||
new KeyValuePair<string, string>("Certificates:StoreCert:AllowInvalid", "true"),
|
||||
}).Build();
|
||||
var reader = new ConfigurationReader(config);
|
||||
var certificates = reader.Certificates;
|
||||
Assert.NotNull(certificates);
|
||||
Assert.Equal(2, certificates.Count);
|
||||
|
||||
var fileCert = certificates["FileCert"];
|
||||
Assert.True(fileCert.IsFileCert);
|
||||
Assert.False(fileCert.IsStoreCert);
|
||||
Assert.Equal("/path/cert.pfx", fileCert.Path);
|
||||
Assert.Equal("certpassword", fileCert.Password);
|
||||
|
||||
var storeCert = certificates["StoreCert"];
|
||||
Assert.False(storeCert.IsFileCert);
|
||||
Assert.True(storeCert.IsStoreCert);
|
||||
Assert.Equal("certsubject", storeCert.Subject);
|
||||
Assert.Equal("certstore", storeCert.Store);
|
||||
Assert.Equal("cetlocation", storeCert.Location);
|
||||
Assert.True(storeCert.AllowInvalid);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadEndpointsWhenNoEndpointsSection_ReturnsEmptyCollection()
|
||||
{
|
||||
var config = new ConfigurationBuilder().AddInMemoryCollection().Build();
|
||||
var reader = new ConfigurationReader(config);
|
||||
var endpoints = reader.Endpoints;
|
||||
Assert.NotNull(endpoints);
|
||||
Assert.False(endpoints.Any());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadEndpointsWhenEmptyEndpointsSection_ReturnsEmptyCollection()
|
||||
{
|
||||
var config = new ConfigurationBuilder().AddInMemoryCollection(new[]
|
||||
{
|
||||
new KeyValuePair<string, string>("Endpoints", ""),
|
||||
}).Build();
|
||||
var reader = new ConfigurationReader(config);
|
||||
var endpoints = reader.Endpoints;
|
||||
Assert.NotNull(endpoints);
|
||||
Assert.False(endpoints.Any());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadEndpointWithMissingUrl_Throws()
|
||||
{
|
||||
var config = new ConfigurationBuilder().AddInMemoryCollection(new[]
|
||||
{
|
||||
new KeyValuePair<string, string>("Endpoints:End1", ""),
|
||||
}).Build();
|
||||
var reader = new ConfigurationReader(config);
|
||||
Assert.Throws<InvalidOperationException>(() => reader.Endpoints);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadEndpointWithEmptyUrl_Throws()
|
||||
{
|
||||
var config = new ConfigurationBuilder().AddInMemoryCollection(new[]
|
||||
{
|
||||
new KeyValuePair<string, string>("Endpoints:End1:Url", ""),
|
||||
}).Build();
|
||||
var reader = new ConfigurationReader(config);
|
||||
Assert.Throws<InvalidOperationException>(() => reader.Endpoints);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReadEndpointsSection_ReturnsCollection()
|
||||
{
|
||||
var config = new ConfigurationBuilder().AddInMemoryCollection(new[]
|
||||
{
|
||||
new KeyValuePair<string, string>("Endpoints:End1:Url", "http://*:5001"),
|
||||
new KeyValuePair<string, string>("Endpoints:End2:Url", "https://*:5002"),
|
||||
new KeyValuePair<string, string>("Endpoints:End3:Url", "https://*:5003"),
|
||||
new KeyValuePair<string, string>("Endpoints:End3:Certificate:Path", "/path/cert.pfx"),
|
||||
new KeyValuePair<string, string>("Endpoints:End3:Certificate:Password", "certpassword"),
|
||||
new KeyValuePair<string, string>("Endpoints:End4:Url", "https://*:5004"),
|
||||
new KeyValuePair<string, string>("Endpoints:End4:Certificate:Subject", "certsubject"),
|
||||
new KeyValuePair<string, string>("Endpoints:End4:Certificate:Store", "certstore"),
|
||||
new KeyValuePair<string, string>("Endpoints:End4:Certificate:Location", "cetlocation"),
|
||||
new KeyValuePair<string, string>("Endpoints:End4:Certificate:AllowInvalid", "true"),
|
||||
}).Build();
|
||||
var reader = new ConfigurationReader(config);
|
||||
var endpoints = reader.Endpoints;
|
||||
Assert.NotNull(endpoints);
|
||||
Assert.Equal(4, endpoints.Count());
|
||||
|
||||
var end1 = endpoints.First();
|
||||
Assert.Equal("End1", end1.Name);
|
||||
Assert.Equal("http://*:5001", end1.Url);
|
||||
Assert.NotNull(end1.ConfigSection);
|
||||
Assert.NotNull(end1.Certificate);
|
||||
Assert.False(end1.Certificate.ConfigSection.Exists());
|
||||
|
||||
var end2 = endpoints.Skip(1).First();
|
||||
Assert.Equal("End2", end2.Name);
|
||||
Assert.Equal("https://*:5002", end2.Url);
|
||||
Assert.NotNull(end2.ConfigSection);
|
||||
Assert.NotNull(end2.Certificate);
|
||||
Assert.False(end2.Certificate.ConfigSection.Exists());
|
||||
|
||||
var end3 = endpoints.Skip(2).First();
|
||||
Assert.Equal("End3", end3.Name);
|
||||
Assert.Equal("https://*:5003", end3.Url);
|
||||
Assert.NotNull(end3.ConfigSection);
|
||||
Assert.NotNull(end3.Certificate);
|
||||
Assert.True(end3.Certificate.ConfigSection.Exists());
|
||||
var cert3 = end3.Certificate;
|
||||
Assert.True(cert3.IsFileCert);
|
||||
Assert.False(cert3.IsStoreCert);
|
||||
Assert.Equal("/path/cert.pfx", cert3.Path);
|
||||
Assert.Equal("certpassword", cert3.Password);
|
||||
|
||||
var end4 = endpoints.Skip(3).First();
|
||||
Assert.Equal("End4", end4.Name);
|
||||
Assert.Equal("https://*:5004", end4.Url);
|
||||
Assert.NotNull(end4.ConfigSection);
|
||||
Assert.NotNull(end4.Certificate);
|
||||
Assert.True(end4.Certificate.ConfigSection.Exists());
|
||||
var cert4 = end4.Certificate;
|
||||
Assert.False(cert4.IsFileCert);
|
||||
Assert.True(cert4.IsStoreCert);
|
||||
Assert.Equal("certsubject", cert4.Subject);
|
||||
Assert.Equal("certstore", cert4.Store);
|
||||
Assert.Equal("cetlocation", cert4.Location);
|
||||
Assert.True(cert4.AllowInvalid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,8 +6,14 @@
|
|||
<TargetFrameworks>$(StandardTestTfms)</TargetFrameworks>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Include="..\shared\**\*.cs" />
|
||||
<Content Include="..\shared\TestCertificates\*.pfx" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Kestrel\Kestrel.csproj" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Testing" Version="$(MicrosoftAspNetCoreTestingPackageVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,213 @@
|
|||
// 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.Linq;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Core;
|
||||
using Microsoft.AspNetCore.Server.Kestrel.Https;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Server.Kestrel.Tests
|
||||
{
|
||||
public class KestrelConfigurationBuilderTests
|
||||
{
|
||||
private KestrelServerOptions CreateServerOptions()
|
||||
{
|
||||
var serverOptions = new KestrelServerOptions();
|
||||
serverOptions.ApplicationServices = new ServiceCollection()
|
||||
.AddLogging()
|
||||
.BuildServiceProvider();
|
||||
return serverOptions;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConfigureNamedEndpoint_OnlyRunForMatchingConfig()
|
||||
{
|
||||
var found = false;
|
||||
var serverOptions = CreateServerOptions();
|
||||
var config = new ConfigurationBuilder().AddInMemoryCollection(new[]
|
||||
{
|
||||
new KeyValuePair<string, string>("Endpoints:Found:Url", "http://*:5001"),
|
||||
}).Build();
|
||||
serverOptions.Configure(config)
|
||||
.Endpoint("Found", endpointOptions => found = true)
|
||||
.Endpoint("NotFound", endpointOptions => throw new NotImplementedException())
|
||||
.Load();
|
||||
|
||||
Assert.Single(serverOptions.ListenOptions);
|
||||
Assert.Equal(5001, serverOptions.ListenOptions[0].IPEndPoint.Port);
|
||||
|
||||
Assert.True(found);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConfigureEndpoint_OnlyRunWhenBuildIsCalled()
|
||||
{
|
||||
var run = false;
|
||||
var serverOptions = CreateServerOptions();
|
||||
serverOptions.Configure()
|
||||
.LocalhostEndpoint(5001, endpointOptions => run = true);
|
||||
|
||||
Assert.Empty(serverOptions.ListenOptions);
|
||||
|
||||
serverOptions.ConfigurationLoader.Load();
|
||||
|
||||
Assert.Single(serverOptions.ListenOptions);
|
||||
Assert.Equal(5001, serverOptions.ListenOptions[0].IPEndPoint.Port);
|
||||
|
||||
Assert.True(run);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CallBuildTwice_OnlyRunsOnce()
|
||||
{
|
||||
var serverOptions = CreateServerOptions();
|
||||
var builder = serverOptions.Configure()
|
||||
.LocalhostEndpoint(5001);
|
||||
|
||||
Assert.Empty(serverOptions.ListenOptions);
|
||||
Assert.Equal(builder, serverOptions.ConfigurationLoader);
|
||||
|
||||
builder.Load();
|
||||
|
||||
Assert.Single(serverOptions.ListenOptions);
|
||||
Assert.Equal(5001, serverOptions.ListenOptions[0].IPEndPoint.Port);
|
||||
Assert.Null(serverOptions.ConfigurationLoader);
|
||||
|
||||
builder.Load();
|
||||
|
||||
Assert.Single(serverOptions.ListenOptions);
|
||||
Assert.Equal(5001, serverOptions.ListenOptions[0].IPEndPoint.Port);
|
||||
Assert.Null(serverOptions.ConfigurationLoader);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Configure_IsReplacable()
|
||||
{
|
||||
var run1 = false;
|
||||
var serverOptions = CreateServerOptions();
|
||||
var config1 = new ConfigurationBuilder().AddInMemoryCollection(new[]
|
||||
{
|
||||
new KeyValuePair<string, string>("Endpoints:End1:Url", "http://*:5001"),
|
||||
}).Build();
|
||||
serverOptions.Configure(config1)
|
||||
.LocalhostEndpoint(5001, endpointOptions => run1 = true);
|
||||
|
||||
Assert.Empty(serverOptions.ListenOptions);
|
||||
Assert.False(run1);
|
||||
|
||||
var run2 = false;
|
||||
var config2 = new ConfigurationBuilder().AddInMemoryCollection(new[]
|
||||
{
|
||||
new KeyValuePair<string, string>("Endpoints:End2:Url", "http://*:5002"),
|
||||
}).Build();
|
||||
serverOptions.Configure(config2)
|
||||
.LocalhostEndpoint(5003, endpointOptions => run2 = true);
|
||||
|
||||
serverOptions.ConfigurationLoader.Load();
|
||||
|
||||
Assert.Equal(2, serverOptions.ListenOptions.Count);
|
||||
Assert.Equal(5002, serverOptions.ListenOptions[0].IPEndPoint.Port);
|
||||
Assert.Equal(5003, serverOptions.ListenOptions[1].IPEndPoint.Port);
|
||||
|
||||
Assert.False(run1);
|
||||
Assert.True(run2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConfigureDefaultsAppliesToNewConfigureEndpoints()
|
||||
{
|
||||
var serverOptions = CreateServerOptions();
|
||||
|
||||
serverOptions.ConfigureEndpointDefaults(opt =>
|
||||
{
|
||||
opt.NoDelay = false;
|
||||
});
|
||||
|
||||
serverOptions.ConfigureHttpsDefaults(opt =>
|
||||
{
|
||||
opt.ServerCertificate = new X509Certificate2(TestResources.TestCertificatePath, "testPassword");
|
||||
opt.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
|
||||
});
|
||||
|
||||
var ran1 = false;
|
||||
var ran2 = false;
|
||||
var config = new ConfigurationBuilder().AddInMemoryCollection(new[]
|
||||
{
|
||||
new KeyValuePair<string, string>("Endpoints:End1:Url", "https://*:5001"),
|
||||
}).Build();
|
||||
serverOptions.Configure(config)
|
||||
.Endpoint("End1", opt =>
|
||||
{
|
||||
ran1 = true;
|
||||
Assert.True(opt.IsHttps);
|
||||
Assert.NotNull(opt.HttpsOptions.ServerCertificate);
|
||||
Assert.Equal(ClientCertificateMode.RequireCertificate, opt.HttpsOptions.ClientCertificateMode);
|
||||
Assert.False(opt.ListenOptions.NoDelay);
|
||||
})
|
||||
.LocalhostEndpoint(5002, opt =>
|
||||
{
|
||||
ran2 = true;
|
||||
Assert.False(opt.NoDelay);
|
||||
})
|
||||
.Load();
|
||||
|
||||
Assert.True(ran1);
|
||||
Assert.True(ran2);
|
||||
|
||||
Assert.NotNull(serverOptions.ListenOptions[0].ConnectionAdapters.Where(adapter => adapter.IsHttps).SingleOrDefault());
|
||||
Assert.Null(serverOptions.ListenOptions[1].ConnectionAdapters.Where(adapter => adapter.IsHttps).SingleOrDefault());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ConfigureEndpointDefaultCanEnableHttps()
|
||||
{
|
||||
var serverOptions = CreateServerOptions();
|
||||
|
||||
serverOptions.ConfigureEndpointDefaults(opt =>
|
||||
{
|
||||
opt.NoDelay = false;
|
||||
opt.UseHttps(new X509Certificate2(TestResources.TestCertificatePath, "testPassword"));
|
||||
});
|
||||
|
||||
serverOptions.ConfigureHttpsDefaults(opt =>
|
||||
{
|
||||
opt.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
|
||||
});
|
||||
|
||||
var ran1 = false;
|
||||
var ran2 = false;
|
||||
var config = new ConfigurationBuilder().AddInMemoryCollection(new[]
|
||||
{
|
||||
new KeyValuePair<string, string>("Endpoints:End1:Url", "https://*:5001"),
|
||||
}).Build();
|
||||
serverOptions.Configure(config)
|
||||
.Endpoint("End1", opt =>
|
||||
{
|
||||
ran1 = true;
|
||||
Assert.True(opt.IsHttps);
|
||||
Assert.Equal(ClientCertificateMode.RequireCertificate, opt.HttpsOptions.ClientCertificateMode);
|
||||
Assert.False(opt.ListenOptions.NoDelay);
|
||||
})
|
||||
.LocalhostEndpoint(5002, opt =>
|
||||
{
|
||||
ran2 = true;
|
||||
Assert.False(opt.NoDelay);
|
||||
})
|
||||
.Load();
|
||||
|
||||
Assert.True(ran1);
|
||||
Assert.True(ran2);
|
||||
|
||||
// You only get Https once per endpoint.
|
||||
Assert.NotNull(serverOptions.ListenOptions[0].ConnectionAdapters.Where(adapter => adapter.IsHttps).SingleOrDefault());
|
||||
Assert.NotNull(serverOptions.ListenOptions[1].ConnectionAdapters.Where(adapter => adapter.IsHttps).SingleOrDefault());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,10 +3,10 @@
|
|||
set -e
|
||||
|
||||
cd /publish
|
||||
systemd-socket-activate -l 8080 -E ASPNETCORE_BASE_PORT=7000 dotnet SampleApp.dll &
|
||||
systemd-socket-activate -l 8080 -E ASPNETCORE_BASE_PORT=7000 dotnet SystemdTestApp.dll &
|
||||
socat TCP-LISTEN:8081,fork TCP-CONNECT:127.0.0.1:7000 &
|
||||
socat TCP-LISTEN:8082,fork TCP-CONNECT:127.0.0.1:7001 &
|
||||
systemd-socket-activate -l /tmp/activate-kestrel.sock -E ASPNETCORE_BASE_PORT=7100 dotnet SampleApp.dll &
|
||||
systemd-socket-activate -l /tmp/activate-kestrel.sock -E ASPNETCORE_BASE_PORT=7100 dotnet SystemdTestApp.dll &
|
||||
socat TCP-LISTEN:8083,fork UNIX-CLIENT:/tmp/activate-kestrel.sock &
|
||||
socat TCP-LISTEN:8084,fork TCP-CONNECT:127.0.0.1:7100 &
|
||||
socat TCP-LISTEN:8085,fork TCP-CONNECT:127.0.0.1:7101 &
|
||||
|
|
|
|||
|
|
@ -4,14 +4,14 @@ set -e
|
|||
|
||||
scriptDir=$(dirname "${BASH_SOURCE[0]}")
|
||||
PATH="$HOME/.dotnet/:$PATH"
|
||||
dotnet publish -f netcoreapp2.0 ./samples/SampleApp/
|
||||
cp -R ./samples/SampleApp/bin/Debug/netcoreapp2.0/publish/ $scriptDir
|
||||
dotnet publish -f netcoreapp2.0 ./samples/SystemdTestApp/
|
||||
cp -R ./samples/SystemdTestApp/bin/Debug/netcoreapp2.0/publish/ $scriptDir
|
||||
cp -R ~/.dotnet/ $scriptDir
|
||||
|
||||
image=$(docker build -qf $scriptDir/Dockerfile $scriptDir)
|
||||
container=$(docker run -Pd $image)
|
||||
|
||||
# Try to connect to SampleApp once a second up to 10 times via all available ports.
|
||||
# Try to connect to SystemdTestApp once a second up to 10 times via all available ports.
|
||||
for i in {1..10}; do
|
||||
curl -f http://$(docker port $container 8080/tcp) \
|
||||
&& curl -f http://$(docker port $container 8081/tcp) \
|
||||
|
|
|
|||
|
|
@ -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.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace Microsoft.AspNetCore.Testing
|
||||
{
|
||||
|
|
@ -11,5 +12,10 @@ namespace Microsoft.AspNetCore.Testing
|
|||
|
||||
public static string TestCertificatePath { get; } = Path.Combine(_baseDir, "testCert.pfx");
|
||||
public static string GetCertPath(string name) => Path.Combine(_baseDir, name);
|
||||
|
||||
public static X509Certificate2 GetTestCertificate()
|
||||
{
|
||||
return new X509Certificate2(TestCertificatePath, "testPassword");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue