import * as URL from "url" ; import * as ipModule from "ip" ; const dns = require ( "dns" ) . promises ; import * as http from "http" ; import * as https from "https" ; import { CancellationToken } from "vscode" ; import { delay } from "../../utils/extensionHelper" ; import * as nls from "vscode-nls" ; nls . config ( { messageFormat : nls . MessageFormat . bundle , bundleFormat : nls . BundleFormat . standalone } ) ( ) ; const localize = nls . loadMessageBundle ( ) ; export class DebuggerEndpointHelper { private localv4 : Buffer ; private localv6 : Buffer ; constructor ( ) { this . localv4 = ipModule . toBuffer ( "127.0.0.1" ) ; this . localv6 = ipModule . toBuffer ( "::1" ) ; } param browserURL param cancellationToken public async retryGetWSEndpoint ( browserURL , attemptNumber , cancellationToken ) { try { return await this . getWSEndpoint ( browserURL ) ; } catch ( err ) { if ( attemptNumber < 1 || cancellationToken . isCancellationRequested ) { const internalError = new Error ( localize ( "CouldNotConnectToTheDebugTarget" , "Could not connect to the debug target at {0}: {1}" , browserURL , err . message ) ) ; if ( cancellationToken . isCancellationRequested ) { throw new Error ( localize ( "OperationCancelled" , "Operation canceled" ) ) ; } throw internalError ; } await delay ( 1000 ) ; return await this . retryGetWSEndpoint ( browserURL , -- attemptNumber , cancellationToken ) ; } } param browserURL public async getWSEndpoint ( browserURL , isSimulate = false ) { if ( ! isSimulate ) { const jsonVersion = await this . fetchJson < { webSocketDebuggerUrl ? : string ; } > ( URL . resolve ( browserURL , "/json/version" ) ) ; if ( jsonVersion . webSocketDebuggerUrl ) { return jsonVersion . webSocketDebuggerUrl ; } } const jsonList = await this . fetchJson < { webSocketDebuggerUrl : string ; } [ ] > ( URL . resolve ( browserURL , "/json/list" ) ) ; if ( jsonList . length ) { return jsonList [ 0 ] . webSocketDebuggerUrl ; } throw new Error ( localize ( "CouldNotFindAnyDebuggableTarget" , "Could not find any debuggable target" ) ) ; } /**
     * Fetches JSON content from the given URL.
     */ private async fetchJson < T > ( url ) { const data = await this . fetch ( url ) ; return JSON . parse ( data ) ; } /**
     * Fetches content from the given URL.
     */ private async fetch ( url ) { const isSecure = ! url . startsWith ( "http://" ) ; const driver = isSecure ? https : http ; const targetAddressIsLoopback = await this . isLoopback ( url ) ; return new Promise < string > ( ( fulfill , reject ) => { const requestOptions = { } ; if ( isSecure && targetAddressIsLoopback ) { requestOptions . rejectUnauthorized = false ; } const request = driver . get ( url , requestOptions , response => { let data = "" ; response . setEncoding ( "utf8" ) ; response . on ( "data" , ( chunk ) => ( data += chunk ) ) ; response . on ( "end" , ( ) => fulfill ( data ) ) ; response . on ( "error" , reject ) ; } ) ; request . on ( "error" , reject ) ; request . end ( ) ; } ) ; } /**
     * Gets whether the IP is a loopback address.
     */ private async isLoopback ( address ) { let ipOrHostname ; try { const url = new URL . URL ( address ) ; ipOrHostname = url . hostname . replace ( /^\[|\]$/g , "" ) ; } catch { ipOrHostname = address ; } if ( this . isLoopbackIp ( ipOrHostname ) ) { return true ; } try { const resolved = await dns . lookup ( ipOrHostname ) ; return this . isLoopbackIp ( resolved . address ) ; } catch { return false ; } } /**
     * Checks if the given address, well-formed loopback IPs. We don't need exotic
     * variations like `127.1` because `dns.lookup()` will resolve the proper
     * version for us. The "right" way would be to parse the IP to an integer
     * like Go does (https://golang.org/pkg/net/#IP.IsLoopback), but this
     * is lightweight and works.
     */ private isLoopbackIp ( ipOrLocalhost ) { if ( ipOrLocalhost . toLowerCase ( ) === "localhost" ) { return true ; } let buf ; try { buf = ipModule . toBuffer ( ipOrLocalhost ) ; } catch { return false ; } return buf . equals ( this . localv4 ) || buf . equals ( this . localv6 ) ; } }