From 009a252946d028f7fa76e13f0ede0a2ea925e2f1 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Tue, 27 Sep 2022 10:12:20 +0000 Subject: [PATCH 1/2] WasmAppHost: improve how we get the urls for the webserver This could fail like: ``` WasmAppHost --runtime-config /tmp/cc/bin/Debug/net7.0/browser-wasm/AppBundle/cc.runtimeconfig.json --forward-console Unhandled exception. System.InvalidOperationException: Failed to determine web server's IP address or port at Microsoft.WebAssembly.AppHost.WebServer.StartAsync(WebServerOptions options, ILogger logger, CancellationToken token) at Microsoft.WebAssembly.AppHost.BrowserHost.StartWebServerAsync(String appPath, Boolean forwardConsole, String[] urls, CancellationToken token) at Microsoft.WebAssembly.AppHost.BrowserHost.RunAsync(ILoggerFactory loggerFactory, CancellationToken token) at Microsoft.WebAssembly.AppHost.BrowserHost.InvokeAsync(CommonConfiguration commonArgs, ILoggerFactory loggerFactory, ILogger logger, CancellationToken token) at Microsoft.WebAssembly.AppHost.WasmAppHost.Main(String[] args) at Microsoft.WebAssembly.AppHost.WasmAppHost.
(String[] args) ``` Instead read the addresses on application lifetime's ApplicationStarted event. --- src/mono/wasm/host/WebServer.cs | 30 +++------------ src/mono/wasm/host/WebServerStartup.cs | 52 +++++++++++++++++++++++++- 2 files changed, 57 insertions(+), 25 deletions(-) diff --git a/src/mono/wasm/host/WebServer.cs b/src/mono/wasm/host/WebServer.cs index 51bda60716740d..92e7e35e297ff3 100644 --- a/src/mono/wasm/host/WebServer.cs +++ b/src/mono/wasm/host/WebServer.cs @@ -1,13 +1,9 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Hosting.Server.Features; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -21,6 +17,7 @@ public class WebServer internal static async Task<(ServerURLs, IWebHost)> StartAsync(WebServerOptions options, ILogger logger, CancellationToken token) { string[]? urls = new string[] { $"http://127.0.0.1:{options.Port}", "https://127.0.0.1:0" }; + TaskCompletionSource realUrlsAvailableTcs = new(); IWebHostBuilder builder = new WebHostBuilder() .UseKestrel() @@ -43,6 +40,7 @@ public class WebServer } services.AddSingleton(logger); services.AddSingleton(Options.Create(options)); + services.AddSingleton(realUrlsAvailableTcs); services.AddRouting(); }) .UseUrls(urls); @@ -53,27 +51,11 @@ public class WebServer IWebHost? host = builder.Build(); await host.StartAsync(token); - ICollection? addresses = host.ServerFeatures - .Get()? - .Addresses; + if (token.CanBeCanceled) + token.Register(async () => await host.StopAsync()); - string? ipAddress = - addresses? - .Where(a => a.StartsWith("http:", StringComparison.InvariantCultureIgnoreCase)) - .Select(a => new Uri(a)) - .Select(uri => uri.ToString()) - .FirstOrDefault(); - - string? ipAddressSecure = - addresses? - .Where(a => a.StartsWith("https:", StringComparison.OrdinalIgnoreCase)) - .Select(a => new Uri(a)) - .Select(uri => uri.ToString()) - .FirstOrDefault(); - - return ipAddress == null || ipAddressSecure == null - ? throw new InvalidOperationException("Failed to determine web server's IP address or port") - : (new ServerURLs(ipAddress, ipAddressSecure), host); + ServerURLs serverUrls = await realUrlsAvailableTcs.Task; + return (serverUrls, host); } } diff --git a/src/mono/wasm/host/WebServerStartup.cs b/src/mono/wasm/host/WebServerStartup.cs index ae5e906c518a65..5997d1534fa1df 100644 --- a/src/mono/wasm/host/WebServerStartup.cs +++ b/src/mono/wasm/host/WebServerStartup.cs @@ -1,12 +1,19 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; using System.Net.WebSockets; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Hosting.Server.Features; using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.StaticFiles; using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; #nullable enable @@ -16,11 +23,17 @@ namespace Microsoft.WebAssembly.AppHost; internal sealed class WebServerStartup { private readonly IWebHostEnvironment _hostingEnvironment; + private ILogger? _logger; public WebServerStartup(IWebHostEnvironment hostingEnvironment) => _hostingEnvironment = hostingEnvironment; - public void Configure(IApplicationBuilder app, IOptions optionsContainer) + public void Configure(IApplicationBuilder app, + IOptions optionsContainer, + TaskCompletionSource realUrlsAvailableTcs, + ILogger logger, + IHostApplicationLifetime applicationLifetime) { + _logger = logger; var provider = new FileExtensionContentTypeProvider(); provider.Mappings[".wasm"] = "application/wasm"; provider.Mappings[".cjs"] = "text/javascript"; @@ -73,6 +86,43 @@ public void Configure(IApplicationBuilder app, IOptions option }); } + applicationLifetime.ApplicationStarted.Register(() => + { + TaskCompletionSource tcs = realUrlsAvailableTcs; + try + { + ICollection? addresses = app.ServerFeatures + .Get() + ?.Addresses; + + string? ipAddress = null; + string? ipAddressSecure = null; + if (addresses is not null) + { + ipAddress = GetHttpServerAddress(addresses, secure: false); + ipAddressSecure = GetHttpServerAddress(addresses, secure: true); + } + + if (ipAddress == null) + tcs.SetException(new InvalidOperationException("Failed to determine web server's IP address or port")); + else + tcs.SetResult(new ServerURLs(ipAddress, ipAddressSecure)); + } + catch (Exception ex) + { + _logger?.LogError($"Failed to get urls for the webserver: {ex}"); + tcs.TrySetException(ex); + throw; + } + + static string? GetHttpServerAddress(ICollection addresses, bool secure) + => addresses? + .Where(a => a.StartsWith(secure ? "https:" : "http:", StringComparison.InvariantCultureIgnoreCase)) + .Select(a => new Uri(a)) + .Select(uri => uri.ToString()) + .FirstOrDefault(); + }); + // app.UseEndpoints(endpoints => // { // endpoints.MapFallbackToFile(options.DefaultFileName); From 83ccd6c35064de59fdb0d7cb5e8c9138f1a1ca6f Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Thu, 29 Sep 2022 10:47:45 +0000 Subject: [PATCH 2/2] fix merge --- src/mono/wasm/host/WebServer.cs | 2 +- src/mono/wasm/host/WebServerStartup.cs | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/mono/wasm/host/WebServer.cs b/src/mono/wasm/host/WebServer.cs index 92e7e35e297ff3..1f448edcb98eab 100644 --- a/src/mono/wasm/host/WebServer.cs +++ b/src/mono/wasm/host/WebServer.cs @@ -16,7 +16,7 @@ public class WebServer { internal static async Task<(ServerURLs, IWebHost)> StartAsync(WebServerOptions options, ILogger logger, CancellationToken token) { - string[]? urls = new string[] { $"http://127.0.0.1:{options.Port}", "https://127.0.0.1:0" }; + string[] urls = options.Urls; TaskCompletionSource realUrlsAvailableTcs = new(); IWebHostBuilder builder = new WebHostBuilder() diff --git a/src/mono/wasm/host/WebServerStartup.cs b/src/mono/wasm/host/WebServerStartup.cs index 3a4ea34e6f92a1..933eeb6b6fc196 100644 --- a/src/mono/wasm/host/WebServerStartup.cs +++ b/src/mono/wasm/host/WebServerStartup.cs @@ -158,7 +158,6 @@ public void Configure(IApplicationBuilder app, applicationLifetime.ApplicationStarted.Register(() => { - TaskCompletionSource tcs = realUrlsAvailableTcs; try { ICollection? addresses = app.ServerFeatures @@ -174,14 +173,14 @@ public void Configure(IApplicationBuilder app, } if (ipAddress == null) - tcs.SetException(new InvalidOperationException("Failed to determine web server's IP address or port")); + realUrlsAvailableTcs.SetException(new InvalidOperationException("Failed to determine web server's IP address or port")); else - tcs.SetResult(new ServerURLs(ipAddress, ipAddressSecure)); + realUrlsAvailableTcs.SetResult(new ServerURLs(ipAddress, ipAddressSecure)); } catch (Exception ex) { _logger?.LogError($"Failed to get urls for the webserver: {ex}"); - tcs.TrySetException(ex); + realUrlsAvailableTcs.TrySetException(ex); throw; }