Skip to content

Commit 3c40805

Browse files
authored
[release/7.0] WasmAppHost: Fix crash when starting wasmbrowser project (#76236)
* 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.<Main>(String[] args) ``` Instead read the addresses on application lifetime's ApplicationStarted event. * fix merge
1 parent 3fed4a3 commit 3c40805

File tree

2 files changed

+54
-25
lines changed

2 files changed

+54
-25
lines changed

src/mono/wasm/host/WebServer.cs

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using System;
5-
using System.Collections.Generic;
6-
using System.Linq;
74
using System.Threading;
85
using System.Threading.Tasks;
96
using Microsoft.AspNetCore.Hosting;
10-
using Microsoft.AspNetCore.Hosting.Server.Features;
117
using Microsoft.Extensions.DependencyInjection;
128
using Microsoft.Extensions.Logging;
139
using Microsoft.Extensions.Options;
@@ -21,6 +17,7 @@ public class WebServer
2117
internal static async Task<(ServerURLs, IWebHost)> StartAsync(WebServerOptions options, ILogger logger, CancellationToken token)
2218
{
2319
string[] urls = options.Urls;
20+
TaskCompletionSource<ServerURLs> realUrlsAvailableTcs = new();
2421

2522
IWebHostBuilder builder = new WebHostBuilder()
2623
.UseKestrel()
@@ -43,6 +40,7 @@ public class WebServer
4340
}
4441
services.AddSingleton(logger);
4542
services.AddSingleton(Options.Create(options));
43+
services.AddSingleton(realUrlsAvailableTcs);
4644
services.AddRouting();
4745
})
4846
.UseUrls(urls);
@@ -53,27 +51,11 @@ public class WebServer
5351
IWebHost? host = builder.Build();
5452
await host.StartAsync(token);
5553

56-
ICollection<string>? addresses = host.ServerFeatures
57-
.Get<IServerAddressesFeature>()?
58-
.Addresses;
54+
if (token.CanBeCanceled)
55+
token.Register(async () => await host.StopAsync());
5956

60-
string? ipAddress =
61-
addresses?
62-
.Where(a => a.StartsWith("http:", StringComparison.InvariantCultureIgnoreCase))
63-
.Select(a => new Uri(a))
64-
.Select(uri => uri.ToString())
65-
.FirstOrDefault();
66-
67-
string? ipAddressSecure =
68-
addresses?
69-
.Where(a => a.StartsWith("https:", StringComparison.OrdinalIgnoreCase))
70-
.Select(a => new Uri(a))
71-
.Select(uri => uri.ToString())
72-
.FirstOrDefault();
73-
74-
return ipAddress == null || ipAddressSecure == null
75-
? throw new InvalidOperationException("Failed to determine web server's IP address or port")
76-
: (new ServerURLs(ipAddress, ipAddressSecure), host);
57+
ServerURLs serverUrls = await realUrlsAvailableTcs.Task;
58+
return (serverUrls, host);
7759
}
7860

7961
}

src/mono/wasm/host/WebServerStartup.cs

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,19 @@
77
using System.IO;
88
using System.Net;
99
using System.Net.WebSockets;
10+
using System.Linq;
1011
using System.Reflection;
1112
using System.Runtime.InteropServices;
1213
using System.Threading.Tasks;
1314
using System.Web;
1415
using Microsoft.AspNetCore.Builder;
1516
using Microsoft.AspNetCore.Hosting;
17+
using Microsoft.AspNetCore.Hosting.Server.Features;
1618
using Microsoft.AspNetCore.Routing;
1719
using Microsoft.AspNetCore.StaticFiles;
1820
using Microsoft.Extensions.FileProviders;
21+
using Microsoft.Extensions.Hosting;
22+
using Microsoft.Extensions.Logging;
1923
using Microsoft.Extensions.Options;
2024
using Microsoft.WebAssembly.Diagnostics;
2125

@@ -28,6 +32,8 @@ internal sealed class WebServerStartup
2832
private readonly IWebHostEnvironment _hostingEnvironment;
2933
private static readonly object LaunchLock = new object();
3034
private static string LaunchedDebugProxyUrl = "";
35+
private ILogger? _logger;
36+
3137
public WebServerStartup(IWebHostEnvironment hostingEnvironment) => _hostingEnvironment = hostingEnvironment;
3238

3339
public static int StartDebugProxy(string devToolsHost)
@@ -52,8 +58,13 @@ public static int StartDebugProxy(string devToolsHost)
5258
return generateRandomPort;
5359
}
5460

55-
public void Configure(IApplicationBuilder app, IOptions<WebServerOptions> optionsContainer)
61+
public void Configure(IApplicationBuilder app,
62+
IOptions<WebServerOptions> optionsContainer,
63+
TaskCompletionSource<ServerURLs> realUrlsAvailableTcs,
64+
ILogger logger,
65+
IHostApplicationLifetime applicationLifetime)
5666
{
67+
_logger = logger;
5768
var provider = new FileExtensionContentTypeProvider();
5869
provider.Mappings[".wasm"] = "application/wasm";
5970
provider.Mappings[".cjs"] = "text/javascript";
@@ -144,5 +155,41 @@ public void Configure(IApplicationBuilder app, IOptions<WebServerOptions> option
144155
return Task.CompletedTask;
145156
});
146157
});
158+
159+
applicationLifetime.ApplicationStarted.Register(() =>
160+
{
161+
try
162+
{
163+
ICollection<string>? addresses = app.ServerFeatures
164+
.Get<IServerAddressesFeature>()
165+
?.Addresses;
166+
167+
string? ipAddress = null;
168+
string? ipAddressSecure = null;
169+
if (addresses is not null)
170+
{
171+
ipAddress = GetHttpServerAddress(addresses, secure: false);
172+
ipAddressSecure = GetHttpServerAddress(addresses, secure: true);
173+
}
174+
175+
if (ipAddress == null)
176+
realUrlsAvailableTcs.SetException(new InvalidOperationException("Failed to determine web server's IP address or port"));
177+
else
178+
realUrlsAvailableTcs.SetResult(new ServerURLs(ipAddress, ipAddressSecure));
179+
}
180+
catch (Exception ex)
181+
{
182+
_logger?.LogError($"Failed to get urls for the webserver: {ex}");
183+
realUrlsAvailableTcs.TrySetException(ex);
184+
throw;
185+
}
186+
187+
static string? GetHttpServerAddress(ICollection<string> addresses, bool secure)
188+
=> addresses?
189+
.Where(a => a.StartsWith(secure ? "https:" : "http:", StringComparison.InvariantCultureIgnoreCase))
190+
.Select(a => new Uri(a))
191+
.Select(uri => uri.ToString())
192+
.FirstOrDefault();
193+
});
147194
}
148195
}

0 commit comments

Comments
 (0)