microsoft/vscode-react-native

Public

mirrored from https://github.com/microsoft/vscode-react-nativeAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
4c757eeb398e0299d0e9cd9bc95b68dd2f87a06e

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/cdp-proxy/debuggerEndpointHelper.ts

126lines · modeblame

d9c9ddcbRedMickey6 years ago1// 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 {
11private localv4: Buffer;
12private localv6: Buffer;
13
14constructor() {
15this.localv4 = ipModule.toBuffer("127.0.0.1");
16this.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*/
23public async getWSEndpoint(browserURL: string): Promise<string> {
24const jsonVersion = await this.fetchJson<{ webSocketDebuggerUrl?: string }>(
25URL.resolve(browserURL, "/json/version")
26);
27if (jsonVersion.webSocketDebuggerUrl) {
28return 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.
33const jsonList = await this.fetchJson<{ webSocketDebuggerUrl: string }[]>(
34URL.resolve(browserURL, "/json/list")
35);
36if (jsonList.length) {
37return jsonList[0].webSocketDebuggerUrl;
38}
39
40throw new Error("Could not find any debuggable target");
41}
42
43/**
44* Fetches JSON content from the given URL.
45*/
46private async fetchJson<T>(url: string): Promise<T> {
47const data = await this.fetch(url);
48return JSON.parse(data);
49}
50
51/**
52* Fetches content from the given URL.
53*/
54private async fetch(url: string): Promise<string> {
55const isSecure = !url.startsWith("http://");
56const driver = isSecure ? https : http;
57const targetAddressIsLoopback = await this.isLoopback(url);
58
59return new Promise<string>((fulfill, reject) => {
60const requestOptions: https.RequestOptions = {};
61
62if (isSecure && targetAddressIsLoopback) {
63requestOptions.rejectUnauthorized = false;
64}
65
66const request = driver.get(url, requestOptions, response => {
67
68let data = "";
69response.setEncoding("utf8");
70response.on("data", (chunk: string) => (data += chunk));
71response.on("end", () => fulfill(data));
72response.on("error", reject);
73});
74
75request.on("error", reject);
76request.end();
77});
78}
79
80/**
81* Gets whether the IP is a loopback address.
82*/
83private async isLoopback(address: string) {
84let ipOrHostname: string;
85try {
86const url = new URL.URL(address);
87// replace brackets in ipv6 addresses:
88ipOrHostname = url.hostname.replace(/^\[|\]$/g, "");
89} catch {
90ipOrHostname = address;
91}
92
93if (this.isLoopbackIp(ipOrHostname)) {
94return true;
95}
96
97try {
98const resolved = await dns.lookup(ipOrHostname);
99return this.isLoopbackIp(resolved.address);
100} catch {
101return 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*/
112private isLoopbackIp(ipOrLocalhost: string) {
113if (ipOrLocalhost.toLowerCase() === "localhost") {
114return true;
115}
116
117let buf: Buffer;
118try {
119buf = ipModule.toBuffer(ipOrLocalhost);
120} catch {
121return false;
122}
123
124return buf.equals(this.localv4) || buf.equals(this.localv6);
125}
126}