microsoft/vscode-react-native

Public

mirrored fromhttps://github.com/microsoft/vscode-react-nativeAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
6e491635e08c261310d93f18da08074c210b3da2

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

src/cdp-proxy/debuggerEndpointHelper.ts

126lines · modecode

1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the MIT license. See LICENSE file in the project root for details.
3
4import * as URL from "url";
5import * as ipModule from "ip";
6const dns = require("dns").promises;
7import * as http from "http";
8import * as https from "https";
9
10export class DebuggerEndpointHelper {
11 private localv4: Buffer;
12 private localv6: Buffer;
13
14 constructor() {
15 this.localv4 = ipModule.toBuffer("127.0.0.1");
16 this.localv6 = ipModule.toBuffer("::1");
17 }
18
19 /**
20 * Returns the debugger websocket URL a process listening at the given address.
21 * @param browserURL -- Address like `http://localhost:1234`
22 */
23 public async getWSEndpoint(browserURL: string): Promise<string> {
24 const jsonVersion = await this.fetchJson<{ webSocketDebuggerUrl?: string }>(
25 URL.resolve(browserURL, "/json/version")
26 );
27 if (jsonVersion.webSocketDebuggerUrl) {
28 return jsonVersion.webSocketDebuggerUrl;
29 }
30
31 // Chrome its top-level debugg on /json/version, while Node does not.
32 // Request both and return whichever one got us a string.
33 const jsonList = await this.fetchJson<{ webSocketDebuggerUrl: string }[]>(
34 URL.resolve(browserURL, "/json/list")
35 );
36 if (jsonList.length) {
37 return jsonList[0].webSocketDebuggerUrl;
38 }
39
40 throw new Error("Could not find any debuggable target");
41 }
42
43 /**
44 * Fetches JSON content from the given URL.
45 */
46 private async fetchJson<T>(url: string): Promise<T> {
47 const data = await this.fetch(url);
48 return JSON.parse(data);
49 }
50
51 /**
52 * Fetches content from the given URL.
53 */
54 private async fetch(url: string): Promise<string> {
55 const isSecure = !url.startsWith("http://");
56 const driver = isSecure ? https : http;
57 const targetAddressIsLoopback = await this.isLoopback(url);
58
59 return new Promise<string>((fulfill, reject) => {
60 const requestOptions: https.RequestOptions = {};
61
62 if (isSecure && targetAddressIsLoopback) {
63 requestOptions.rejectUnauthorized = false;
64 }
65
66 const request = driver.get(url, requestOptions, response => {
67
68 let data = "";
69 response.setEncoding("utf8");
70 response.on("data", (chunk: string) => (data += chunk));
71 response.on("end", () => fulfill(data));
72 response.on("error", reject);
73 });
74
75 request.on("error", reject);
76 request.end();
77 });
78 }
79
80 /**
81 * Gets whether the IP is a loopback address.
82 */
83 private async isLoopback(address: string) {
84 let ipOrHostname: string;
85 try {
86 const url = new URL.URL(address);
87 // replace brackets in ipv6 addresses:
88 ipOrHostname = url.hostname.replace(/^\[|\]$/g, "");
89 } catch {
90 ipOrHostname = address;
91 }
92
93 if (this.isLoopbackIp(ipOrHostname)) {
94 return true;
95 }
96
97 try {
98 const resolved = await dns.lookup(ipOrHostname);
99 return this.isLoopbackIp(resolved.address);
100 } catch {
101 return false;
102 }
103 }
104
105 /**
106 * Checks if the given address, well-formed loopback IPs. We don't need exotic
107 * variations like `127.1` because `dns.lookup()` will resolve the proper
108 * version for us. The "right" way would be to parse the IP to an integer
109 * like Go does (https://golang.org/pkg/net/#IP.IsLoopback), but this
110 * is lightweight and works.
111 */
112 private isLoopbackIp(ipOrLocalhost: string) {
113 if (ipOrLocalhost.toLowerCase() === "localhost") {
114 return true;
115 }
116
117 let buf: Buffer;
118 try {
119 buf = ipModule.toBuffer(ipOrLocalhost);
120 } catch {
121 return false;
122 }
123
124 return buf.equals(this.localv4) || buf.equals(this.localv6);
125 }
126}
127