Skip to content

Commit 27b2872

Browse files
Support VS Code "pwa-chrome" debug type
1 parent e7a92c4 commit 27b2872

File tree

1 file changed

+57
-7
lines changed

1 file changed

+57
-7
lines changed

src/Components/Blazor/Server/src/MonoDebugProxy/BlazorMonoDebugProxyAppBuilderExtensions.cs

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -132,10 +132,42 @@ private static void UseVisualStudioDebuggerConnectionRequestHandlers(this IAppli
132132
}
133133
else if (requestPath.Equals("/json/version", StringComparison.OrdinalIgnoreCase))
134134
{
135-
var browserVersionJson = await GetBrowserVersionInfoAsync();
135+
// VS Code's "js-debug" nightly extension, when configured to use the "pwa-chrome"
136+
// debug type, uses the /json/version endpoint to find the websocket endpoint for
137+
// debugging the browser that listens on a user-specified port.
138+
//
139+
// To make this flow work with the Mono debug proxy, we pass the request through
140+
// to the underlying browser (to get its actual version info) but then overwrite
141+
// the "webSocketDebuggerUrl" with the URL to the proxy.
142+
//
143+
// This whole connection flow isn't very good because it doesn't have any way
144+
// to specify the debug port for the underlying browser. So, we end up assuming
145+
// the default port 9222 in all cases. This is good enough for a manual "attach"
146+
// but isn't good enough if the IDE is responsible for launching the browser,
147+
// as it will be on a random port. So,
148+
//
149+
// - VS isn't going to use this. Instead it will use a configured "debugEndpoint"
150+
// property from which it can construct the proxy URL directly (including adding
151+
// a "browser" querystring value to specify the underlying endpoint), bypassing
152+
// /json/version altogether
153+
// - We will need to update the VS Code debug adapter to make it do the same as VS
154+
// if there is a "debugEndpoint" property configured
155+
//
156+
// Once both VS and VS Code support the "debugEndpoint" flow, we should be able to
157+
// remove this /json/version code altogether. We should check that in-browser
158+
// debugging still works at that point.
159+
160+
var browserVersionJsonStream = await GetBrowserVersionInfoAsync();
161+
var browserVersion = await JsonSerializer.DeserializeAsync<Dictionary<string, object>>(browserVersionJsonStream);
162+
163+
if (browserVersion.TryGetValue("webSocketDebuggerUrl", out var browserEndpoint))
164+
{
165+
var proxyEndpoint = GetProxyEndpoint(request, ((JsonElement)browserEndpoint).GetString());
166+
browserVersion["webSocketDebuggerUrl"] = proxyEndpoint;
167+
}
136168

137169
context.Response.ContentType = "application/json";
138-
await context.Response.WriteAsync(browserVersionJson);
170+
await JsonSerializer.SerializeAsync(context.Response.Body, browserVersion);
139171
}
140172
}
141173
else
@@ -238,13 +270,30 @@ There is more than one browser tab at <code>{targetTabUrl}</code>.
238270
// page and redirect there
239271
var tabToDebug = matchingTabs.Single();
240272
var underlyingV8Endpoint = tabToDebug.WebSocketDebuggerUrl;
241-
var proxyEndpoint = $"{request.Host}{request.PathBase}/_framework/debug/ws-proxy?browser={WebUtility.UrlEncode(underlyingV8Endpoint)}";
273+
var proxyEndpoint = GetProxyEndpoint(request, underlyingV8Endpoint);
242274
var devToolsUrlAbsolute = new Uri(debuggerHost + tabToDebug.DevtoolsFrontendUrl);
243-
var wsParamName = request.IsHttps ? "wss" : "ws";
244-
var devToolsUrlWithProxy = $"{devToolsUrlAbsolute.Scheme}://{devToolsUrlAbsolute.Authority}{devToolsUrlAbsolute.AbsolutePath}?{wsParamName}={proxyEndpoint}";
275+
var devToolsUrlWithProxy = $"{devToolsUrlAbsolute.Scheme}://{devToolsUrlAbsolute.Authority}{devToolsUrlAbsolute.AbsolutePath}?{proxyEndpoint.Scheme}={proxyEndpoint.Authority}{proxyEndpoint.PathAndQuery}";
245276
context.Response.Redirect(devToolsUrlWithProxy);
246277
}
247278

279+
private static Uri GetProxyEndpoint(HttpRequest incomingRequest, string browserEndpoint)
280+
{
281+
var builder = new UriBuilder(
282+
schemeName: incomingRequest.IsHttps ? "wss" : "ws",
283+
hostName: incomingRequest.Host.Host)
284+
{
285+
Path = $"{incomingRequest.PathBase}/_framework/debug/ws-proxy",
286+
Query = $"browser={WebUtility.UrlEncode(browserEndpoint)}"
287+
};
288+
289+
if (incomingRequest.Host.Port.HasValue)
290+
{
291+
builder.Port = incomingRequest.Host.Port.Value;
292+
}
293+
294+
return builder.Uri;
295+
}
296+
248297
private static string GetLaunchChromeInstructions(string appRootUrl)
249298
{
250299
var profilePath = Path.Combine(Path.GetTempPath(), "blazor-chrome-debug");
@@ -292,11 +341,12 @@ private static string GetLaunchEdgeInstructions(string appRootUrl)
292341
}
293342
}
294343

295-
private static async Task<string> GetBrowserVersionInfoAsync()
344+
private static async Task<Stream> GetBrowserVersionInfoAsync()
296345
{
297346
using var httpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(5) };
298347
var debuggerHost = GetDebuggerHost();
299-
return await httpClient.GetStringAsync($"{debuggerHost}/json/version");
348+
var response = await httpClient.GetAsync($"{debuggerHost}/json/version");
349+
return await response.Content.ReadAsStreamAsync();
300350
}
301351

302352
private static async Task<IEnumerable<BrowserTab>> GetOpenedBrowserTabs()

0 commit comments

Comments
 (0)