diff --git a/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/Autobahn/AutobahnTester.cs b/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/Autobahn/AutobahnTester.cs index 4f5c0afbb5..3217f4029b 100644 --- a/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/Autobahn/AutobahnTester.cs +++ b/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/Autobahn/AutobahnTester.cs @@ -34,7 +34,7 @@ namespace Microsoft.AspNetCore.WebSockets.ConformanceTest.Autobahn Spec = baseSpec; } - public async Task Run() + public async Task Run(CancellationToken cancellationToken) { var specFile = Path.GetTempFileName(); try @@ -43,7 +43,7 @@ namespace Microsoft.AspNetCore.WebSockets.ConformanceTest.Autobahn // Run the test (write something to the console so people know this will take a while...) _logger.LogInformation("Now launching Autobahn Test Suite. This will take a while."); - var exitCode = await Wstest.Default.ExecAsync("-m fuzzingclient -s " + specFile); + var exitCode = await Wstest.Default.ExecAsync("-m fuzzingclient -s " + specFile, cancellationToken); if (exitCode != 0) { throw new Exception("wstest failed"); @@ -57,6 +57,8 @@ namespace Microsoft.AspNetCore.WebSockets.ConformanceTest.Autobahn } } + cancellationToken.ThrowIfCancellationRequested(); + // Parse the output. var outputFile = Path.Combine(Directory.GetCurrentDirectory(), Spec.OutputDirectory, "index.json"); using (var reader = new StreamReader(File.OpenRead(outputFile))) @@ -84,7 +86,7 @@ namespace Microsoft.AspNetCore.WebSockets.ConformanceTest.Autobahn Assert.True(failures.Length == 0, "Autobahn results did not meet expectations:" + Environment.NewLine + failures.ToString()); } - public async Task DeployTestAndAddToSpec(ServerType server, bool ssl, string environment, Action expectationConfig = null) + public async Task DeployTestAndAddToSpec(ServerType server, bool ssl, string environment, CancellationToken cancellationToken, Action expectationConfig = null) { var port = Interlocked.Increment(ref _nextPort); var baseUrl = ssl ? $"https://localhost:{port}" : $"http://localhost:{port}"; @@ -104,6 +106,7 @@ namespace Microsoft.AspNetCore.WebSockets.ConformanceTest.Autobahn var deployer = ApplicationDeployerFactory.Create(parameters, logger); var result = deployer.Deploy(); _deployers.Add(deployer); + cancellationToken.ThrowIfCancellationRequested(); #if NET451 System.Net.ServicePointManager.ServerCertificateValidationCallback = (_, __, ___, ____) => true; @@ -123,10 +126,13 @@ namespace Microsoft.AspNetCore.WebSockets.ConformanceTest.Autobahn // Make sure the server works var resp = await RetryHelper.RetryRequest(() => { + cancellationToken.ThrowIfCancellationRequested(); return client.GetAsync(result.ApplicationBaseUri); - }, logger, result.HostShutdownToken, retryCount: 5); + }, logger, CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, result.HostShutdownToken).Token, retryCount: 5); resp.EnsureSuccessStatusCode(); + cancellationToken.ThrowIfCancellationRequested(); + // Add to the current spec var wsUrl = result.ApplicationBaseUri.Replace("https://", "wss://").Replace("http://", "ws://"); Spec.WithServer(name, wsUrl); @@ -134,6 +140,8 @@ namespace Microsoft.AspNetCore.WebSockets.ConformanceTest.Autobahn var expectations = new AutobahnExpectations(server, ssl, environment); expectationConfig?.Invoke(expectations); _expectations.Add(expectations); + + cancellationToken.ThrowIfCancellationRequested(); } public void Dispose() diff --git a/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/Autobahn/Executable.cs b/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/Autobahn/Executable.cs index f404a79a0e..41b798303e 100644 --- a/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/Autobahn/Executable.cs +++ b/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/Autobahn/Executable.cs @@ -2,6 +2,7 @@ using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; +using System.Threading; using System.Threading.Tasks; namespace Microsoft.AspNetCore.WebSockets.ConformanceTest.Autobahn @@ -30,7 +31,7 @@ namespace Microsoft.AspNetCore.WebSockets.ConformanceTest.Autobahn return null; } - public Task ExecAsync(string args) + public async Task ExecAsync(string args, CancellationToken cancellationToken) { var process = new Process() { @@ -44,11 +45,23 @@ namespace Microsoft.AspNetCore.WebSockets.ConformanceTest.Autobahn }; var tcs = new TaskCompletionSource(); - process.Exited += (_, __) => tcs.TrySetResult(process.ExitCode); + using (cancellationToken.Register(() => Cancel(process, tcs))) + { + process.Exited += (_, __) => tcs.TrySetResult(process.ExitCode); - process.Start(); + process.Start(); - return tcs.Task; + return await tcs.Task; + } + } + + private static void Cancel(Process process, TaskCompletionSource tcs) + { + if (process != null && !process.HasExited) + { + process.Kill(); + } + tcs.TrySetCanceled(); } } } diff --git a/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/AutobahnTests.cs b/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/AutobahnTests.cs index 88ae8f0055..4f5b53a29a 100644 --- a/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/AutobahnTests.cs +++ b/test/Microsoft.AspNetCore.WebSockets.ConformanceTest/AutobahnTests.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.IO; using System.Linq; using System.Runtime.InteropServices; +using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Server.IntegrationTesting; using Microsoft.AspNetCore.Testing.xunit; @@ -39,20 +40,23 @@ namespace Microsoft.AspNetCore.WebSockets.ConformanceTest var loggerFactory = new LoggerFactory(); // No logging by default! It's very loud... - if(string.Equals(Environment.GetEnvironmentVariable("AUTOBAHN_SUITES_LOG"), "1", StringComparison.Ordinal)) + if (string.Equals(Environment.GetEnvironmentVariable("AUTOBAHN_SUITES_LOG"), "1", StringComparison.Ordinal)) { loggerFactory.AddConsole(); } + var cts = new CancellationTokenSource(); + cts.CancelAfter(TimeSpan.FromMinutes(5)); // These tests generally complete in just over 1 minute. + AutobahnResult result; using (var tester = new AutobahnTester(loggerFactory, spec)) { - await tester.DeployTestAndAddToSpec(ServerType.Kestrel, ssl: false, environment: "ManagedSockets"); + await tester.DeployTestAndAddToSpec(ServerType.Kestrel, ssl: false, environment: "ManagedSockets", cancellationToken: cts.Token); // Windows-only IIS tests, and Kestrel SSL tests (due to: https://github.com/aspnet/WebSockets/issues/102) if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - await tester.DeployTestAndAddToSpec(ServerType.Kestrel, ssl: true, environment: "ManagedSockets"); + await tester.DeployTestAndAddToSpec(ServerType.Kestrel, ssl: true, environment: "ManagedSockets", cancellationToken: cts.Token); if (IsWindows8OrHigher()) { @@ -60,12 +64,12 @@ namespace Microsoft.AspNetCore.WebSockets.ConformanceTest { // IIS Express tests are a bit flaky, some tests fail occasionally or get non-strict passes // https://github.com/aspnet/WebSockets/issues/100 - await tester.DeployTestAndAddToSpec(ServerType.IISExpress, ssl: false, environment: "ManagedSockets", expectationConfig: expect => expect + await tester.DeployTestAndAddToSpec(ServerType.IISExpress, ssl: false, environment: "ManagedSockets", cancellationToken: cts.Token, expectationConfig: expect => expect .OkOrFail(Enumerable.Range(1, 20).Select(i => $"5.{i}").ToArray()) // 5.* occasionally fail on IIS express .OkOrNonStrict("3.2", "3.3", "3.4", "4.1.3", "4.1.4", "4.1.5", "4.2.3", "4.2.4", "4.2.5", "5.15")); // These occasionally get non-strict results } - await tester.DeployTestAndAddToSpec(ServerType.WebListener, ssl: false, environment: "ManagedSockets", expectationConfig: expect => expect + await tester.DeployTestAndAddToSpec(ServerType.WebListener, ssl: false, environment: "ManagedSockets", cancellationToken: cts.Token, expectationConfig: expect => expect .OkOrNonStrict("4.2.4")); } } @@ -73,9 +77,12 @@ namespace Microsoft.AspNetCore.WebSockets.ConformanceTest // REQUIRES a build of WebListener that supports native WebSockets, which we don't have right now //await tester.DeployTestAndAddToSpec(ServerType.WebListener, ssl: false, environment: "NativeSockets"); - result = await tester.Run(); + result = await tester.Run(cts.Token); tester.Verify(result); } + + // If it hasn't been cancelled yet, cancel the token just to be sure + cts.Cancel(); } private bool IsWindows8OrHigher()