diff --git a/.gitattributes b/.gitattributes
index c2f0f84273..5e91d85057 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -13,7 +13,7 @@
*.png binary
*.gif binary
-*.cs text=auto diff=csharp
+*.cs text=auto diff=csharp
*.vb text=auto
*.resx text=auto
*.c text=auto
diff --git a/KestrelHttpServer.sln b/KestrelHttpServer.sln
index c9679e5676..baa0ee92f4 100644
--- a/KestrelHttpServer.sln
+++ b/KestrelHttpServer.sln
@@ -2,26 +2,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
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
- .appveyor.yml = .appveyor.yml
- .gitattributes = .gitattributes
- .gitignore = .gitignore
- .travis.yml = .travis.yml
- build.cmd = build.cmd
- build.ps1 = build.ps1
- build.sh = build.sh
- CONTRIBUTING.md = CONTRIBUTING.md
- Directory.Build.props = Directory.Build.props
- Directory.Build.targets = Directory.Build.targets
- LICENSE.txt = LICENSE.txt
- NuGet.Config = NuGet.Config
- NuGetPackageVerifier.json = NuGetPackageVerifier.json
- README.md = README.md
- ToProjectReferences.ps1 = ToProjectReferences.ps1
- version.xml = version.xml
- EndProjectSection
-EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{2D5D5227-4DBD-499A-96B1-76A36B03B750}"
ProjectSection(SolutionItems) = preProject
src\Directory.Build.props = src\Directory.Build.props
@@ -136,6 +116,28 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kestrel.Transport.Libuv.Bin
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kestrel.H2Spec.FunctionalTests", "test\Kestrel.H2Spec.FunctionalTests\Kestrel.H2Spec.FunctionalTests.csproj", "{C4123E55-5760-4557-B89B-39E1258FD7F9}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{B19F67B8-7635-42C3-B5BF-00D1CC47FA64}"
+ ProjectSection(SolutionItems) = preProject
+ .gitattributes = .gitattributes
+ .gitignore = .gitignore
+ build.cmd = build.cmd
+ build.sh = build.sh
+ CONTRIBUTING.md = CONTRIBUTING.md
+ Directory.Build.props = Directory.Build.props
+ Directory.Build.targets = Directory.Build.targets
+ korebuild-lock.txt = korebuild-lock.txt
+ korebuild.json = korebuild.json
+ LICENSE.txt = LICENSE.txt
+ NuGet.config = NuGet.config
+ NuGetPackageVerifier.json = NuGetPackageVerifier.json
+ README.md = README.md
+ run.cmd = run.cmd
+ run.ps1 = run.ps1
+ run.sh = run.sh
+ ToProjectReferences.ps1 = ToProjectReferences.ps1
+ version.props = version.props
+ EndProjectSection
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
diff --git a/test/Kestrel.H2Spec.FunctionalTests/H2SpecTests.cs b/test/Kestrel.H2Spec.FunctionalTests/H2SpecTests.cs
index 45e66cbf47..c2560335d3 100644
--- a/test/Kestrel.H2Spec.FunctionalTests/H2SpecTests.cs
+++ b/test/Kestrel.H2Spec.FunctionalTests/H2SpecTests.cs
@@ -11,6 +11,7 @@ using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core;
+using Microsoft.AspNetCore.Testing;
using Microsoft.AspNetCore.Testing.xunit;
using Microsoft.Extensions.Logging.Testing;
using Xunit;
@@ -23,8 +24,6 @@ namespace H2Spec.FunctionalTests
SkipReason = "Missing Windows ALPN support: https://en.wikipedia.org/wiki/Application-Layer_Protocol_Negotiation#Support")]
public class H2SpecTests : LoggedTest
{
- private static readonly string _testCertPath = Path.Combine(Directory.GetCurrentDirectory(), "shared", "TestCertificates", "testCert.pfx");
-
[ConditionalTheory]
[MemberData(nameof(H2SpecTestCases))]
public async Task RunIndividualTestCase(H2SpecTestCase testCase)
@@ -37,7 +36,7 @@ namespace H2Spec.FunctionalTests
listenOptions.Protocols = HttpProtocols.Http2;
if (testCase.Https)
{
- listenOptions.UseHttps(_testCertPath, "testPassword");
+ listenOptions.UseHttps(TestResources.GetTestCertificate());
}
});
})
diff --git a/test/Kestrel.H2Spec.FunctionalTests/Kestrel.H2Spec.FunctionalTests.csproj b/test/Kestrel.H2Spec.FunctionalTests/Kestrel.H2Spec.FunctionalTests.csproj
index 5b45607b50..06e4138c78 100644
--- a/test/Kestrel.H2Spec.FunctionalTests/Kestrel.H2Spec.FunctionalTests.csproj
+++ b/test/Kestrel.H2Spec.FunctionalTests/Kestrel.H2Spec.FunctionalTests.csproj
@@ -11,6 +11,7 @@
+
diff --git a/test/Kestrel.InMemory.FunctionalTests/HttpsTests.cs b/test/Kestrel.InMemory.FunctionalTests/HttpsTests.cs
index da2df11053..5928f5d661 100644
--- a/test/Kestrel.InMemory.FunctionalTests/HttpsTests.cs
+++ b/test/Kestrel.InMemory.FunctionalTests/HttpsTests.cs
@@ -40,7 +40,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
public void UseHttpsDefaultsToDefaultCert()
{
var serverOptions = CreateServerOptions();
- var defaultCert = new X509Certificate2(TestResources.TestCertificatePath, "testPassword");
+ var defaultCert = TestResources.GetTestCertificate();
serverOptions.DefaultCertificate = defaultCert;
serverOptions.ListenLocalhost(5000, options =>
@@ -65,7 +65,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
public void ConfigureHttpsDefaultsNeverLoadsDefaultCert()
{
var serverOptions = CreateServerOptions();
- var testCert = new X509Certificate2(TestResources.TestCertificatePath, "testPassword");
+ var testCert = TestResources.GetTestCertificate();
serverOptions.ConfigureHttpsDefaults(options =>
{
Assert.Null(options.ServerCertificate);
@@ -89,7 +89,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
public void ConfigureCertSelectorNeverLoadsDefaultCert()
{
var serverOptions = CreateServerOptions();
- var testCert = new X509Certificate2(TestResources.TestCertificatePath, "testPassword");
+ var testCert = TestResources.GetTestCertificate();
serverOptions.ConfigureHttpsDefaults(options =>
{
Assert.Null(options.ServerCertificate);
@@ -124,7 +124,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
new TestServiceContext(LoggerFactory),
listenOptions =>
{
- listenOptions.UseHttps(TestResources.TestCertificatePath, "testPassword");
+ listenOptions.UseHttps(TestResources.GetTestCertificate());
}))
{
using (var connection = server.CreateConnection())
@@ -151,7 +151,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
new TestServiceContext(LoggerFactory),
listenOptions =>
{
- listenOptions.UseHttps(TestResources.TestCertificatePath, "testPassword");
+ listenOptions.UseHttps(TestResources.GetTestCertificate());
}))
{
using (var connection = server.CreateConnection())
@@ -195,7 +195,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
new TestServiceContext(LoggerFactory),
listenOptions =>
{
- listenOptions.UseHttps(TestResources.TestCertificatePath, "testPassword");
+ listenOptions.UseHttps(TestResources.GetTestCertificate());
}))
{
using (var connection = server.CreateConnection())
@@ -238,7 +238,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
new TestServiceContext(LoggerFactory),
listenOptions =>
{
- listenOptions.UseHttps(TestResources.TestCertificatePath, "testPassword");
+ listenOptions.UseHttps(TestResources.GetTestCertificate());
}))
{
using (var connection = server.CreateConnection())
@@ -269,7 +269,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
new TestServiceContext(LoggerFactory),
listenOptions =>
{
- listenOptions.UseHttps(TestResources.TestCertificatePath, "testPassword");
+ listenOptions.UseHttps(TestResources.GetTestCertificate());
}))
{
using (var connection = server.CreateConnection())
@@ -295,7 +295,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
new TestServiceContext(LoggerFactory),
listenOptions =>
{
- listenOptions.UseHttps(TestResources.TestCertificatePath, "testPassword");
+ listenOptions.UseHttps(TestResources.GetTestCertificate());
}))
{
using (var connection = server.CreateConnection())
@@ -323,7 +323,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
{
listenOptions.UseHttps(o =>
{
- o.ServerCertificate = new X509Certificate2(TestResources.TestCertificatePath, "testPassword");
+ o.ServerCertificate = new X509Certificate2(TestResources.GetTestCertificate());
o.OnHandshakeStarted = () => handshakeStartedTcs.SetResult(null);
handshakeTimeout = o.HandshakeTimeout;
@@ -359,7 +359,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
new TestServiceContext(LoggerFactory),
listenOptions =>
{
- listenOptions.UseHttps(TestResources.TestCertificatePath, "testPassword");
+ listenOptions.UseHttps(TestResources.GetTestCertificate());
}))
{
using (var connection = server.CreateConnection())
diff --git a/test/Kestrel.InMemory.FunctionalTests/LoggingConnectionAdapterTests.cs b/test/Kestrel.InMemory.FunctionalTests/LoggingConnectionAdapterTests.cs
index 5eca246b35..cba0db84bd 100644
--- a/test/Kestrel.InMemory.FunctionalTests/LoggingConnectionAdapterTests.cs
+++ b/test/Kestrel.InMemory.FunctionalTests/LoggingConnectionAdapterTests.cs
@@ -25,7 +25,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.InMemory.FunctionalTests
listenOptions =>
{
listenOptions.UseConnectionLogging();
- listenOptions.UseHttps(TestResources.TestCertificatePath, "testPassword");
+ listenOptions.UseHttps(TestResources.GetTestCertificate());
listenOptions.UseConnectionLogging();
}))
{
diff --git a/test/Kestrel.Tests/KestrelConfigurationBuilderTests.cs b/test/Kestrel.Tests/KestrelConfigurationBuilderTests.cs
index 96f6f8777f..2299d38f63 100644
--- a/test/Kestrel.Tests/KestrelConfigurationBuilderTests.cs
+++ b/test/Kestrel.Tests/KestrelConfigurationBuilderTests.cs
@@ -136,7 +136,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tests
serverOptions.ConfigureHttpsDefaults(opt =>
{
- opt.ServerCertificate = new X509Certificate2(TestResources.TestCertificatePath, "testPassword");
+ opt.ServerCertificate = TestResources.GetTestCertificate();
opt.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
});
@@ -179,7 +179,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tests
serverOptions.ConfigureEndpointDefaults(opt =>
{
opt.NoDelay = false;
- opt.UseHttps(new X509Certificate2(TestResources.TestCertificatePath, "testPassword"));
+ opt.UseHttps(TestResources.GetTestCertificate());
});
serverOptions.ConfigureHttpsDefaults(opt =>
@@ -335,7 +335,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tests
serverOptions.ConfigureHttpsDefaults(opt =>
{
- opt.ServerCertificate = new X509Certificate2(TestResources.TestCertificatePath, "testPassword");
+ opt.ServerCertificate = TestResources.GetTestCertificate();
opt.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
});
@@ -391,7 +391,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Tests
serverOptions.ConfigureHttpsDefaults(opt =>
{
- opt.ServerCertificate = new X509Certificate2(TestResources.TestCertificatePath, "testPassword");
+ opt.ServerCertificate = TestResources.GetTestCertificate();
opt.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
});
diff --git a/test/Kestrel.Transport.BindTests/AddressRegistrationTests.cs b/test/Kestrel.Transport.BindTests/AddressRegistrationTests.cs
index 98abbc3a5c..972941e72d 100644
--- a/test/Kestrel.Transport.BindTests/AddressRegistrationTests.cs
+++ b/test/Kestrel.Transport.BindTests/AddressRegistrationTests.cs
@@ -289,7 +289,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
if (testUrl.StartsWith("https"))
{
- listenOptions.UseHttps(TestResources.TestCertificatePath, "testPassword");
+ listenOptions.UseHttps(TestResources.GetTestCertificate());
}
});
})
@@ -461,7 +461,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
if (mockHttps)
{
- options.DefaultCertificate = new X509Certificate2(TestResources.TestCertificatePath, "testPassword");
+ options.DefaultCertificate = TestResources.GetTestCertificate();
}
})
.Configure(ConfigureEchoAddress);
@@ -545,7 +545,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
options.Listen(new IPEndPoint(IPAddress.Loopback, 0), listenOptions =>
{
- listenOptions.UseHttps(TestResources.TestCertificatePath, "testPassword");
+ listenOptions.UseHttps(TestResources.GetTestCertificate());
});
})
.UseUrls(useUrlsAddress)
@@ -622,7 +622,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
{
options.Listen(new IPEndPoint(IPAddress.Loopback, 0), listenOptions =>
{
- listenOptions.UseHttps(TestResources.TestCertificatePath, "testPassword");
+ listenOptions.UseHttps(TestResources.GetTestCertificate());
});
})
.PreferHostingUrls(true)
diff --git a/test/Kestrel.Transport.FunctionalTests/ChromeConstants.cs b/test/Kestrel.Transport.FunctionalTests/ChromeConstants.cs
new file mode 100644
index 0000000000..e6f19feb73
--- /dev/null
+++ b/test/Kestrel.Transport.FunctionalTests/ChromeConstants.cs
@@ -0,0 +1,28 @@
+// 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.Runtime.InteropServices;
+
+namespace Interop.FunctionalTests
+{
+ public static class ChromeConstants
+ {
+ public static string ExecutablePath { get; } = ResolveChromeExecutablePath();
+
+ private static string ResolveChromeExecutablePath()
+ {
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "Google", "Chrome", "Application", "chrome.exe");
+ }
+ else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ {
+ return Path.Combine("/usr", "bin", "chromium-browser");
+ }
+
+ throw new PlatformNotSupportedException();
+ }
+ }
+}
diff --git a/test/Kestrel.Transport.FunctionalTests/ChromeTests.cs b/test/Kestrel.Transport.FunctionalTests/ChromeTests.cs
new file mode 100644
index 0000000000..89b16a8077
--- /dev/null
+++ b/test/Kestrel.Transport.FunctionalTests/ChromeTests.cs
@@ -0,0 +1,139 @@
+// 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.
+
+#if NETCOREAPP2_2
+
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Net;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Server.Kestrel.Core;
+using Microsoft.AspNetCore.Server.Kestrel.FunctionalTests;
+using Microsoft.AspNetCore.Testing;
+using Microsoft.AspNetCore.Testing.xunit;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Testing;
+using Xunit;
+
+namespace Interop.FunctionalTests
+{
+ [SkipIfChromeUnavailable]
+ public class ChromeTests : LoggedTest
+ {
+ private static readonly string _postHtml =
+@"
+
+
+
+
+
+
+
+";
+
+ private string NetLogPath { get; set; }
+ private string StartupLogPath { get; set; }
+ private string ShutdownLogPath { get; set; }
+ private string ChromeArgs { get; set; }
+
+ private void InitializeArgs()
+ {
+ NetLogPath = Path.Combine(ResolvedLogOutputDirectory, $"{ResolvedTestMethodName}.nl.json");
+ StartupLogPath = Path.Combine(ResolvedLogOutputDirectory, $"{ResolvedTestMethodName}.su.json");
+ ShutdownLogPath = Path.Combine(ResolvedLogOutputDirectory, $"{ResolvedTestMethodName}.sd.json");
+
+ ChromeArgs = $"--headless " +
+ $"--disable-gpu " +
+ $"--allow-insecure-localhost " +
+ $"--enable-logging " +
+ $"--dump-dom " +
+ $"--virtual-time-budget=10000 " +
+ $"--log-net-log={NetLogPath} " +
+ $"--trace-startup --trace-startup-file={StartupLogPath} " +
+ $"--trace-shutdown --trace-shutdown-file={ShutdownLogPath}";
+ }
+
+ [ConditionalTheory]
+ [OSSkipCondition(OperatingSystems.MacOSX, SkipReason = "Missing SslStream ALPN support: https://github.com/dotnet/corefx/issues/30492")]
+ [MinimumOSVersion(OperatingSystems.Windows, WindowsVersions.Win81, SkipReason = "Missing Windows ALPN support: https://en.wikipedia.org/wiki/Application-Layer_Protocol_Negotiation#Support")]
+ [InlineData("", "Interop HTTP/2 GET")]
+ [InlineData("?TestMethod=POST", "Interop HTTP/2 POST")]
+ public async Task Http2(string requestSuffix, string expectedResponse)
+ {
+ InitializeArgs();
+
+ using (var server = new TestServer(async context =>
+ {
+ if (string.Equals(context.Request.Query["TestMethod"], "POST", StringComparison.OrdinalIgnoreCase))
+ {
+ await context.Response.WriteAsync(_postHtml);
+ }
+ else
+ {
+ await context.Response.WriteAsync($"Interop {context.Request.Protocol} {context.Request.Method}");
+ }
+ },
+ new TestServiceContext(LoggerFactory),
+ options => options.Listen(IPAddress.Loopback, 0, listenOptions =>
+ {
+ listenOptions.Protocols = HttpProtocols.Http2;
+ listenOptions.UseHttps(TestResources.GetTestCertificate());
+ })))
+ {
+ var chromeOutput = await RunHeadlessChrome($"https://localhost:{server.Port}/{requestSuffix}");
+
+ AssertExpectedResponseOrShowDebugInstructions(expectedResponse, chromeOutput);
+ }
+ }
+
+ private async Task RunHeadlessChrome(string testUrl)
+ {
+ var chromeArgs = $"{ChromeArgs} {testUrl}";
+ var chromeStartInfo = new ProcessStartInfo
+ {
+ FileName = ChromeConstants.ExecutablePath,
+ Arguments = chromeArgs,
+ UseShellExecute = false,
+ CreateNoWindow = true,
+ RedirectStandardError = true,
+ RedirectStandardOutput = true
+ };
+
+ Logger.LogInformation($"Staring chrome: {ChromeConstants.ExecutablePath} {chromeArgs}");
+
+ var headlessChromeProcess = Process.Start(chromeStartInfo);
+ var chromeOutput = await headlessChromeProcess.StandardOutput.ReadToEndAsync();
+
+ headlessChromeProcess.WaitForExit();
+
+ return chromeOutput;
+ }
+
+ private void AssertExpectedResponseOrShowDebugInstructions(string expectedResponse, string actualResponse)
+ {
+ try
+ {
+ Assert.Contains(expectedResponse, actualResponse);
+ }
+ catch
+ {
+ Logger.LogError("Chrome interop tests failed. Please consult the following logs:");
+ Logger.LogError($"Network logs: {NetLogPath}");
+ Logger.LogError($"Startup logs: {StartupLogPath}");
+ Logger.LogError($"Shutdown logs: {ShutdownLogPath}");
+ throw;
+ }
+ }
+ }
+}
+
+#elif NET461 // No ALPN support
+#else
+#error TFMs need updating
+#endif
diff --git a/test/Kestrel.Transport.FunctionalTests/ResponseTests.cs b/test/Kestrel.Transport.FunctionalTests/ResponseTests.cs
index 3b50a8071b..d9aa983cac 100644
--- a/test/Kestrel.Transport.FunctionalTests/ResponseTests.cs
+++ b/test/Kestrel.Transport.FunctionalTests/ResponseTests.cs
@@ -560,7 +560,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
const int chunks = 256 * 1024;
var chunkData = new byte[chunkSize];
- var certificate = new X509Certificate2(TestResources.TestCertificatePath, "testPassword");
+ var certificate = TestResources.GetTestCertificate();
var responseRateTimeoutMessageLogged = new TaskCompletionSource