Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[release/7.0] [wasm][debugger] Support create, debugging and running wasmbrowser template from VS #76113

Merged
merged 10 commits into from
Sep 28, 2022
5 changes: 5 additions & 0 deletions src/mono/wasm/build/WasmApp.targets
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,11 @@
<WasmDebugLevel Condition="('$(WasmDebugLevel)' == '' or '$(WasmDebugLevel)' == '0') and ('$(DebuggerSupport)' == 'true' or '$(Configuration)' == 'Debug')">-1</WasmDebugLevel>
</PropertyGroup>

<ItemGroup>
<!-- Allow running/debugging from VS -->
<ProjectCapability Include="DotNetCoreWeb"/>
</ItemGroup>

<PropertyGroup Label="Identify app bundle directory to run from">
<!-- Allow running from custom WasmAppDir -->
<_AppBundleDirForRunCommand Condition="'$(WasmAppDir)' != ''">$(WasmAppDir)</_AppBundleDirForRunCommand>
Expand Down
10 changes: 7 additions & 3 deletions src/mono/wasm/host/BrowserHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,13 @@ private async Task RunAsync(ILoggerFactory loggerFactory, CancellationToken toke
debugging: _args.CommonConfig.Debugging);
runArgsJson.Save(Path.Combine(_args.CommonConfig.AppPath, "runArgs.json"));

string[] urls = envVars.TryGetValue("ASPNETCORE_URLS", out string? aspnetUrls)
? aspnetUrls.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries)
: new string[] { $"http://127.0.0.1:{_args.CommonConfig.HostProperties.WebServerPort}", "https://127.0.0.1:0" };

(ServerURLs serverURLs, IWebHost host) = await StartWebServerAsync(_args.CommonConfig.AppPath,
_args.ForwardConsoleOutput ?? false,
_args.CommonConfig.HostProperties.WebServerPort,
urls,
token);

string[] fullUrls = BuildUrls(serverURLs, _args.AppArgs);
Expand All @@ -84,7 +88,7 @@ private async Task RunAsync(ILoggerFactory loggerFactory, CancellationToken toke
await host.WaitForShutdownAsync(token);
}

private async Task<(ServerURLs, IWebHost)> StartWebServerAsync(string appPath, bool forwardConsole, int port, CancellationToken token)
private async Task<(ServerURLs, IWebHost)> StartWebServerAsync(string appPath, bool forwardConsole, string[] urls, CancellationToken token)
{
WasmTestMessagesProcessor? logProcessor = null;
if (forwardConsole)
Expand All @@ -100,7 +104,7 @@ private async Task RunAsync(ILoggerFactory loggerFactory, CancellationToken toke
ContentRootPath: Path.GetFullPath(appPath),
WebServerUseCors: true,
WebServerUseCrossOriginPolicy: true,
Port: port
Urls: urls
);

(ServerURLs serverURLs, IWebHost host) = await WebServer.StartAsync(options, _logger, token);
Expand Down
2 changes: 1 addition & 1 deletion src/mono/wasm/host/WebServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,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;

IWebHostBuilder builder = new WebHostBuilder()
.UseKestrel()
Expand Down
2 changes: 1 addition & 1 deletion src/mono/wasm/host/WebServerOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ internal sealed record WebServerOptions
string? ContentRootPath,
bool WebServerUseCors,
bool WebServerUseCrossOriginPolicy,
int Port,
string [] Urls,
string DefaultFileName = "index.html"
);
77 changes: 72 additions & 5 deletions src/mono/wasm/host/WebServerStartup.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
// 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.Diagnostics;
using System.IO;
using System.Net;
using System.Net.WebSockets;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Web;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Options;
using Microsoft.WebAssembly.Diagnostics;

#nullable enable

Expand All @@ -16,9 +26,32 @@ namespace Microsoft.WebAssembly.AppHost;
internal sealed class WebServerStartup
{
private readonly IWebHostEnvironment _hostingEnvironment;

private static readonly object LaunchLock = new object();
private static string LaunchedDebugProxyUrl = "";
public WebServerStartup(IWebHostEnvironment hostingEnvironment) => _hostingEnvironment = hostingEnvironment;

public static int StartDebugProxy(string devToolsHost)
{
//we need to start another process, otherwise it will be running the BrowserDebugProxy in the same process that will be debugged, so pausing in a breakpoint
//on managed code will freeze because it will not be able to continue executing the BrowserDebugProxy to get the locals value
var executablePath = Path.Combine(System.AppContext.BaseDirectory, "BrowserDebugHost.dll");
var ownerPid = Environment.ProcessId;
var generateRandomPort = new Random().Next(5000, 5300);
var processStartInfo = new ProcessStartInfo
{
FileName = "dotnet" + (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".exe" : ""),
Arguments = $"exec \"{executablePath}\" --OwnerPid {ownerPid} --DevToolsUrl {devToolsHost} --DevToolsProxyPort {generateRandomPort}",
UseShellExecute = false,
RedirectStandardOutput = true,
};
var debugProxyProcess = Process.Start(processStartInfo);
if (debugProxyProcess is null)
{
throw new InvalidOperationException("Unable to start debug proxy process.");
}
return generateRandomPort;
}

public void Configure(IApplicationBuilder app, IOptions<WebServerOptions> optionsContainer)
{
var provider = new FileExtensionContentTypeProvider();
Expand Down Expand Up @@ -73,9 +106,43 @@ public void Configure(IApplicationBuilder app, IOptions<WebServerOptions> option
});
}

// app.UseEndpoints(endpoints =>
// {
// endpoints.MapFallbackToFile(options.DefaultFileName);
// });
app.Map("/debug", app =>
{
app.Run(async (context) =>
{
//debug from VS
var queryParams = HttpUtility.ParseQueryString(context.Request.QueryString.Value!);
var browserParam = queryParams.Get("browser");
Uri? browserUrl = null;
var devToolsHost = "http://localhost:9222";
if (browserParam != null)
{
browserUrl = new Uri(browserParam);
devToolsHost = $"http://{browserUrl.Host}:{browserUrl.Port}";
}
lock (LaunchLock)
{
if (LaunchedDebugProxyUrl == "")
{
LaunchedDebugProxyUrl = $"http://localhost:{StartDebugProxy(devToolsHost)}";
}
}
var requestPath = context.Request.Path.ToString();
if (requestPath == string.Empty)
{
requestPath = "/";
}
context.Response.Redirect($"{LaunchedDebugProxyUrl}{browserUrl!.PathAndQuery}");
await Task.FromResult(0);
});
});
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/", context =>
{
context.Response.Redirect("index.html", permanent: false);
return Task.CompletedTask;
});
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,24 @@
"type": "project"
},
"symbols": {
"kestrelHttpPortGenerated": {
"type": "generated",
"generator": "port",
"parameters": {
"low": 5000,
"high": 5300
},
"replaces": "5000"
},
"kestrelHttpsPortGenerated": {
"type": "generated",
"generator": "port",
"parameters": {
"low": 7000,
"high": 7300
},
"replaces": "5001"
},
"framework": {
"type": "parameter",
"description": "The target framework for the project.",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"profiles": {
"browser.0": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/debug?browser={browserInspectUri}"
}
}
}