diff --git a/src/Microsoft.DotNet.XHarness.CLI/CommandArguments/Arguments/WebServerUseDefaultFiles.cs b/src/Microsoft.DotNet.XHarness.CLI/CommandArguments/Arguments/WebServerUseDefaultFiles.cs new file mode 100644 index 000000000..1816088a9 --- /dev/null +++ b/src/Microsoft.DotNet.XHarness.CLI/CommandArguments/Arguments/WebServerUseDefaultFiles.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.DotNet.XHarness.CLI.CommandArguments; + +internal class WebServerUseDefaultFilesArguments : SwitchArgument +{ + public WebServerUseDefaultFilesArguments() + : base("web-server-use-default-files", "Enable default files like index.html", false) + { + } +} diff --git a/src/Microsoft.DotNet.XHarness.CLI/CommandArguments/WASM/IWebServerArguments.cs b/src/Microsoft.DotNet.XHarness.CLI/CommandArguments/WASM/IWebServerArguments.cs index b1efbbbe1..461983cb1 100644 --- a/src/Microsoft.DotNet.XHarness.CLI/CommandArguments/WASM/IWebServerArguments.cs +++ b/src/Microsoft.DotNet.XHarness.CLI/CommandArguments/WASM/IWebServerArguments.cs @@ -12,4 +12,5 @@ internal interface IWebServerArguments WebServerUseHttpsArguments WebServerUseHttps { get; } WebServerUseCorsArguments WebServerUseCors { get; } WebServerUseCrossOriginPolicyArguments WebServerUseCrossOriginPolicy { get; } + WebServerUseDefaultFilesArguments WebServerUseDefaultFiles { get; } } diff --git a/src/Microsoft.DotNet.XHarness.CLI/CommandArguments/WASM/WasmTestBrowserCommandArguments.cs b/src/Microsoft.DotNet.XHarness.CLI/CommandArguments/WASM/WasmTestBrowserCommandArguments.cs index 72aad1ef5..6a438113b 100644 --- a/src/Microsoft.DotNet.XHarness.CLI/CommandArguments/WASM/WasmTestBrowserCommandArguments.cs +++ b/src/Microsoft.DotNet.XHarness.CLI/CommandArguments/WASM/WasmTestBrowserCommandArguments.cs @@ -36,6 +36,7 @@ internal class WasmTestBrowserCommandArguments : XHarnessCommandArguments, IWebS public WebServerUseHttpsArguments WebServerUseHttps { get; } = new(); public WebServerUseCorsArguments WebServerUseCors { get; } = new(); public WebServerUseCrossOriginPolicyArguments WebServerUseCrossOriginPolicy { get; } = new(); + public WebServerUseDefaultFilesArguments WebServerUseDefaultFiles { get; } = new(); protected override IEnumerable GetArguments() => new Argument[] { @@ -63,6 +64,7 @@ internal class WasmTestBrowserCommandArguments : XHarnessCommandArguments, IWebS WebServerUseHttps, WebServerUseCors, WebServerUseCrossOriginPolicy, + WebServerUseDefaultFiles, }; public override void Validate() diff --git a/src/Microsoft.DotNet.XHarness.CLI/CommandArguments/WASM/WasmTestCommandArguments.cs b/src/Microsoft.DotNet.XHarness.CLI/CommandArguments/WASM/WasmTestCommandArguments.cs index b14e96b78..7108f697d 100644 --- a/src/Microsoft.DotNet.XHarness.CLI/CommandArguments/WASM/WasmTestCommandArguments.cs +++ b/src/Microsoft.DotNet.XHarness.CLI/CommandArguments/WASM/WasmTestCommandArguments.cs @@ -28,6 +28,7 @@ internal class WasmTestCommandArguments : XHarnessCommandArguments, IWebServerAr public WebServerUseHttpsArguments WebServerUseHttps { get; } = new(); public WebServerUseCorsArguments WebServerUseCors { get; } = new(); public WebServerUseCrossOriginPolicyArguments WebServerUseCrossOriginPolicy { get; } = new(); + public WebServerUseDefaultFilesArguments WebServerUseDefaultFiles { get; } = new(); protected override IEnumerable GetArguments() => new Argument[] { @@ -49,5 +50,6 @@ internal class WasmTestCommandArguments : XHarnessCommandArguments, IWebServerAr WebServerUseHttps, WebServerUseCors, WebServerUseCrossOriginPolicy, + WebServerUseDefaultFiles, }; } diff --git a/src/Microsoft.DotNet.XHarness.CLI/CommandArguments/WASM/WebServerCommandArguments.cs b/src/Microsoft.DotNet.XHarness.CLI/CommandArguments/WASM/WebServerCommandArguments.cs new file mode 100644 index 000000000..d405d8fdc --- /dev/null +++ b/src/Microsoft.DotNet.XHarness.CLI/CommandArguments/WASM/WebServerCommandArguments.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; + +namespace Microsoft.DotNet.XHarness.CLI.CommandArguments.Wasm; + +internal class WebServerCommandArguments : XHarnessCommandArguments, IWebServerArguments +{ + public AppPathArgument AppPackagePath { get; } = new(); + + public WebServerMiddlewareArgument WebServerMiddlewarePathsAndTypes { get; } = new(); + public WebServerHttpEnvironmentVariables WebServerHttpEnvironmentVariables { get; } = new(); + public WebServerHttpsEnvironmentVariables WebServerHttpsEnvironmentVariables { get; } = new(); + public WebServerUseHttpsArguments WebServerUseHttps { get; } = new(); + public WebServerUseCorsArguments WebServerUseCors { get; } = new(); + public WebServerUseCrossOriginPolicyArguments WebServerUseCrossOriginPolicy { get; } = new(); + public WebServerUseDefaultFilesArguments WebServerUseDefaultFiles { get; } = new(); + + public TimeoutArgument Timeout { get; } = new(TimeSpan.FromMinutes(15)); + + protected override IEnumerable GetArguments() => new Argument[] + { + AppPackagePath, + Timeout, + WebServerMiddlewarePathsAndTypes, + WebServerHttpEnvironmentVariables, + WebServerHttpsEnvironmentVariables, + WebServerUseHttps, + WebServerUseCors, + WebServerUseCrossOriginPolicy, + WebServerUseDefaultFiles, + }; +} diff --git a/src/Microsoft.DotNet.XHarness.CLI/Commands/WASM/Browser/WasmBrowserTestRunner.cs b/src/Microsoft.DotNet.XHarness.CLI/Commands/WASM/Browser/WasmBrowserTestRunner.cs index 514a28b30..b5449eb14 100644 --- a/src/Microsoft.DotNet.XHarness.CLI/Commands/WASM/Browser/WasmBrowserTestRunner.cs +++ b/src/Microsoft.DotNet.XHarness.CLI/Commands/WASM/Browser/WasmBrowserTestRunner.cs @@ -13,6 +13,7 @@ using System.Threading.Tasks; using System.Web; +using Microsoft.DotNet.XHarness.CLI.Commands; using Microsoft.DotNet.XHarness.CLI.CommandArguments.Wasm; using Microsoft.DotNet.XHarness.Common.CLI; using Microsoft.Extensions.Logging; @@ -58,11 +59,13 @@ public async Task RunTestsWithWebDriver(DriverService driverService, I { var consolePumpTcs = new TaskCompletionSource(); var logProcessorTask = Task.Run(() => _messagesProcessor.RunAsync(cts.Token)); + + var webServerOptions = WebServer.TestWebServerOptions.FromArguments(_arguments); + webServerOptions.ContentRoot = _arguments.AppPackagePath; + webServerOptions.OnConsoleConnected = socket => RunConsoleMessagesPump(socket, cts.Token); ServerURLs serverURLs = await WebServer.Start( - _arguments, - _arguments.AppPackagePath, + webServerOptions, _logger, - socket => RunConsoleMessagesPump(socket, cts.Token), cts.Token); string testUrl = BuildUrl(serverURLs); diff --git a/src/Microsoft.DotNet.XHarness.CLI/Commands/WASM/JS/WasmTestCommand.cs b/src/Microsoft.DotNet.XHarness.CLI/Commands/WASM/JS/WasmTestCommand.cs index 8e935746e..90eaa88c6 100644 --- a/src/Microsoft.DotNet.XHarness.CLI/Commands/WASM/JS/WasmTestCommand.cs +++ b/src/Microsoft.DotNet.XHarness.CLI/Commands/WASM/JS/WasmTestCommand.cs @@ -73,9 +73,7 @@ protected override async Task InvokeInternal(ILogger logger) { serverURLs = await WebServer.Start( Arguments, - null, logger, - null, cts.Token); cts.CancelAfter(Arguments.Timeout); } diff --git a/src/Microsoft.DotNet.XHarness.CLI/Commands/WASM/WasmCommandSet.cs b/src/Microsoft.DotNet.XHarness.CLI/Commands/WASM/WasmCommandSet.cs index d06a7d090..c32bc849d 100644 --- a/src/Microsoft.DotNet.XHarness.CLI/Commands/WASM/WasmCommandSet.cs +++ b/src/Microsoft.DotNet.XHarness.CLI/Commands/WASM/WasmCommandSet.cs @@ -15,5 +15,6 @@ public WasmCommandSet() : base("wasm") { Add(new WasmTestCommand()); Add(new WasmTestBrowserCommand()); + Add(new WebServerCommand()); } } diff --git a/src/Microsoft.DotNet.XHarness.CLI/Commands/WASM/WebServerCommand.cs b/src/Microsoft.DotNet.XHarness.CLI/Commands/WASM/WebServerCommand.cs new file mode 100644 index 000000000..bd198525e --- /dev/null +++ b/src/Microsoft.DotNet.XHarness.CLI/Commands/WASM/WebServerCommand.cs @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.DotNet.XHarness.CLI.CommandArguments.Wasm; +using Microsoft.DotNet.XHarness.CLI.Commands; +using Microsoft.DotNet.XHarness.Common; +using Microsoft.DotNet.XHarness.Common.CLI; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace Microsoft.DotNet.XHarness.CLI.Commands.Wasm; + +internal class WebServerCommand : XHarnessCommand +{ + private const string CommandHelp = "Starts a webserver"; + + protected override string CommandUsage { get; } = "wasm webserver [OPTIONS]"; + protected override string CommandDescription { get; } = CommandHelp; + + protected override WebServerCommandArguments Arguments { get; } = new(); + + public WebServerCommand() + : base(TargetPlatform.WASM, "webserver", allowsExtraArgs: true, new ServiceCollection(), CommandHelp) + { + } + + protected override async Task InvokeInternal(ILogger logger) + { + var cts = new CancellationTokenSource(); + var webServerOptions = WebServer.TestWebServerOptions.FromArguments(Arguments); + webServerOptions.ContentRoot = Arguments.AppPackagePath; + ServerURLs serverURLs = await WebServer.Start( + webServerOptions, + logger, + cts.Token); + + logger.LogInformation($"Now listening on: http://{serverURLs.Http}"); + if (!string.IsNullOrEmpty(serverURLs.Https)) + logger.LogInformation($"Now listening on: https://{serverURLs.Https}"); + + await Task.Delay(Arguments.Timeout, cts.Token); + if (cts.Token.IsCancellationRequested) + { + logger.LogError("Token cancelled for unknown reasons, exiting."); + return ExitCode.GENERAL_FAILURE; + } + else + { + logger.LogInformation($"Stopping the webserver after the timeout of {Arguments.Timeout}"); + return ExitCode.SUCCESS; + } + } +} diff --git a/src/Microsoft.DotNet.XHarness.CLI/Commands/WebServer.cs b/src/Microsoft.DotNet.XHarness.CLI/Commands/WebServer.cs index 1bef6b408..3a42365f9 100644 --- a/src/Microsoft.DotNet.XHarness.CLI/Commands/WebServer.cs +++ b/src/Microsoft.DotNet.XHarness.CLI/Commands/WebServer.cs @@ -26,9 +26,16 @@ namespace Microsoft.DotNet.XHarness.CLI.Commands; public class WebServer { - internal static async Task Start(IWebServerArguments arguments, string? contentRoot, ILogger logger, Func? onConsoleConnected, CancellationToken token) + internal static Task Start(IWebServerArguments arguments, ILogger logger, CancellationToken token, Func? onConsoleConnected = null) { - var urls = arguments.WebServerUseHttps + var options = TestWebServerOptions.FromArguments(arguments); + options.OnConsoleConnected = onConsoleConnected; + return Start(options, logger, token); + } + + internal static async Task Start(TestWebServerOptions webServerOptions, ILogger logger, CancellationToken token) + { + var urls = webServerOptions.UseHttps ? new string[] { "http://127.0.0.1:0", "https://127.0.0.1:0" } : new string[] { "http://127.0.0.1:0" }; @@ -41,7 +48,7 @@ internal static async Task Start(IWebServerArguments arguments, stri }) .ConfigureServices((ctx, services) => { - if (arguments.WebServerUseCors) + if (webServerOptions.UseCors) { services.AddCors(o => o.AddPolicy("AnyCors", builder => { @@ -56,20 +63,14 @@ internal static async Task Start(IWebServerArguments arguments, stri services.Configure(ctx.Configuration); services.Configure(options => { - options.WebServerUseCors = arguments.WebServerUseCors; - options.WebServerUseCrossOriginPolicy = arguments.WebServerUseCrossOriginPolicy; - options.OnConsoleConnected = onConsoleConnected; - foreach (var middlewareType in arguments.WebServerMiddlewarePathsAndTypes.GetLoadedTypes()) - { - options.EchoServerMiddlewares.Add(middlewareType); - } + webServerOptions.CopyTo(options); }); }) .UseUrls(urls); - if (contentRoot != null) + if (webServerOptions.ContentRoot != null) { - builder.UseContentRoot(contentRoot); + builder.UseContentRoot(webServerOptions.ContentRoot); } var host = builder.Build(); @@ -84,7 +85,7 @@ internal static async Task Start(IWebServerArguments arguments, stri .Select(uri => $"{uri.Host}:{uri.Port}") .FirstOrDefault(); - var ipAddressSecure = arguments.WebServerUseHttps + var ipAddressSecure = webServerOptions.UseHttps ? host.ServerFeatures .Get()? .Addresses @@ -94,7 +95,7 @@ internal static async Task Start(IWebServerArguments arguments, stri .FirstOrDefault() : null; - if (ipAddress == null || (arguments.WebServerUseHttps && ipAddressSecure == null)) + if (ipAddress == null || (webServerOptions.UseHttps && ipAddressSecure == null)) { throw new InvalidOperationException("Failed to determine web server's IP address or port"); } @@ -127,7 +128,7 @@ public void Configure(IApplicationBuilder app, IOptionsMonitor { @@ -137,6 +138,14 @@ public void Configure(IApplicationBuilder app, IOptionsMonitor? OnConsoleConnected { get; set; } public IList EchoServerMiddlewares { get; set; } = new List(); - public bool WebServerUseCors { get; set; } - public bool WebServerUseCrossOriginPolicy { get; set; } + public bool UseCors { get; set; } + public bool UseHttps { get; set; } + public bool UseCrossOriginPolicy { get; set; } + public bool UseDefaultFiles { get; set; } + public string? ContentRoot { get; set; } + + public void CopyTo(TestWebServerOptions otherOptions) + { + otherOptions.OnConsoleConnected = OnConsoleConnected; + otherOptions.EchoServerMiddlewares = EchoServerMiddlewares; + otherOptions.UseCors = UseCors; + otherOptions.UseHttps = UseHttps; + otherOptions.UseCrossOriginPolicy = UseCrossOriginPolicy; + otherOptions.UseDefaultFiles = UseDefaultFiles; + otherOptions.ContentRoot = ContentRoot; + } + + public static TestWebServerOptions FromArguments(IWebServerArguments arguments) + { + TestWebServerOptions options = new(); + options.UseCors = arguments.WebServerUseCors; + options.UseHttps = arguments.WebServerUseHttps; + options.UseCrossOriginPolicy = arguments.WebServerUseCrossOriginPolicy; + options.UseDefaultFiles = arguments.WebServerUseDefaultFiles; + foreach (var middlewareType in arguments.WebServerMiddlewarePathsAndTypes.GetLoadedTypes()) + { + options.EchoServerMiddlewares.Add(middlewareType); + } + return options; + } } }