@@ -132,10 +132,42 @@ private static void UseVisualStudioDebuggerConnectionRequestHandlers(this IAppli
132
132
}
133
133
else if ( requestPath . Equals ( "/json/version" , StringComparison . OrdinalIgnoreCase ) )
134
134
{
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
+ }
136
168
137
169
context . Response . ContentType = "application/json" ;
138
- await context . Response . WriteAsync ( browserVersionJson ) ;
170
+ await JsonSerializer . SerializeAsync ( context . Response . Body , browserVersion ) ;
139
171
}
140
172
}
141
173
else
@@ -238,13 +270,30 @@ There is more than one browser tab at <code>{targetTabUrl}</code>.
238
270
// page and redirect there
239
271
var tabToDebug = matchingTabs . Single ( ) ;
240
272
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 ) ;
242
274
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 } ";
245
276
context . Response . Redirect ( devToolsUrlWithProxy ) ;
246
277
}
247
278
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
+
248
297
private static string GetLaunchChromeInstructions ( string appRootUrl )
249
298
{
250
299
var profilePath = Path . Combine ( Path . GetTempPath ( ) , "blazor-chrome-debug" ) ;
@@ -292,11 +341,12 @@ private static string GetLaunchEdgeInstructions(string appRootUrl)
292
341
}
293
342
}
294
343
295
- private static async Task < string > GetBrowserVersionInfoAsync ( )
344
+ private static async Task < Stream > GetBrowserVersionInfoAsync ( )
296
345
{
297
346
using var httpClient = new HttpClient { Timeout = TimeSpan . FromSeconds ( 5 ) } ;
298
347
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 ( ) ;
300
350
}
301
351
302
352
private static async Task < IEnumerable < BrowserTab > > GetOpenedBrowserTabs ( )
0 commit comments