| 270 | } |
| 271 | |
| 272 | public async exposeUrl(urlString: string, logger: Logger): Promise<string> { |
| 273 | const uri = vs.Uri.parse(urlString, true); |
| 274 | logger.info(`Exposing URL: ${uri.toString()}`); |
| 275 | const isWebSocket = uri.scheme === "ws" || uri.scheme === "wss"; |
| 276 | const isSecure = uri.scheme === "wss" || uri.scheme === "https"; |
| 277 | |
| 278 | // TODO: Remove this scheme mapping when https://github.com/microsoft/vscode/issues/84819 |
| 279 | // is resolved. |
| 280 | let fakeScheme = uri.scheme; |
| 281 | if (isWebSocket) |
| 282 | fakeScheme = isSecure ? "https" : "http"; |
| 283 | |
| 284 | const url = new URL(urlString); |
| 285 | |
| 286 | // Ensure the URL always has a port, as some cloud providers fail to expose URLs correctly |
| 287 | // that don't have explicit port numbers. |
| 288 | // |
| 289 | // Additionally, on some cloud providers we get an IPv6 loopback which fails to connect |
| 290 | // correctly. Assume that if we get this, it's safe to use the "localhost" hostname. |
| 291 | const fakeHostname = url.hostname === "[::]" ? "localhost" : url.hostname; |
| 292 | const fakePort = url.port || (isSecure ? "443" : "80"); // Don't change to ??, port can be empty string! |
| 293 | const fakeAuthority = `${fakeHostname}:${fakePort}`; |
| 294 | |
| 295 | const uriToMap = uri.with({ scheme: fakeScheme, authority: fakeAuthority }); |
| 296 | logger.info(`Mapping URI: ${uriToMap.toString()}`); |
| 297 | |
| 298 | const mappedUri = await vsEnv.asExternalUri(uriToMap); |
| 299 | logger.info(`Mapped URI: ${mappedUri.toString()}`); |
| 300 | |
| 301 | // Now we need to map the scheme back to WS if that's what was originally asked for, however |
| 302 | // we need to take into account whether asExternalUri pushed is up to secure, so use |
| 303 | // the http/https to decide which to go back to. |
| 304 | let newScheme = mappedUri.scheme; |
| 305 | if (isWebSocket) |
| 306 | // Note: We use mappedUri.scheme here and not isSecure because we |
| 307 | // care if the *exposed* URI is secure. |
| 308 | newScheme = mappedUri.scheme === "https" ? "wss" : "ws"; |
| 309 | |
| 310 | const mappedUrl = new URL(uriToString(mappedUri)); |
| 311 | logger.info(`Mapped URL: ${mappedUrl}`); |
| 312 | |
| 313 | // Copy the important (mapped) parts back onto the original URL, preserving |
| 314 | // the path/querystring that was not messed with by VS Code's Uri class. |
| 315 | url.protocol = newScheme; |
| 316 | url.host = mappedUrl.host; |
| 317 | url.port = mappedUrl.port; |
| 318 | logger.info(`Final URL: ${url}`); |
| 319 | |
| 320 | return url.toString(); |
| 321 | } |
| 322 | } |
| 323 | |
| 324 | function uriToString(uri: vs.Uri) { |