microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
revert-2693-dev/lucygramley/networkIsolationPolicy

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/networkInspector/clientDevice.ts

272lines · 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
4/* eslint-disable */
5/* eslint-enable prettier/prettier*/
6
7import { ReactiveSocket } from "rsocket-types";
8import { ClientOS } from "./clientUtils";
9import { InspectorView, InspectorViewType } from "./views/inspectorView";
10import { OutputChannelLogger } from "../log/OutputChannelLogger";
11import { InspectorViewFactory } from "./views/inspectorViewFactory";
12
13/**
14 * @preserve
15 * Start region: the code is borrowed from https://github.com/facebook/flipper/blob/v0.79.1/desktop/app/src/Client.tsx
16 *
17 * Copyright (c) Facebook, Inc. and its affiliates.
18 *
19 * This source code is licensed under the MIT license found in the
20 * LICENSE file in the root directory of this source tree.
21 *
22 * @format
23 */
24
25export interface ClientIdConstituents {
26 app: string;
27 os: ClientOS;
28 device: string;
29 device_id: string;
30}
31
32export interface UninitializedClient {
33 os: ClientOS;
34 deviceName: string;
35 appName: string;
36}
37
38export interface ClientCsrQuery {
39 csr?: string;
40 csr_path?: string;
41}
42
43export interface ClientQuery extends ClientIdConstituents {
44 sdk_version?: number;
45}
46
47export interface SecureClientQuery extends ClientQuery, ClientCsrQuery {
48 medium?: number;
49}
50
51export interface RequestParams {
52 api: string;
53 method: string;
54 params?: Record<string, any>;
55}
56
57type RequestMetadata = { method: string; id: number; params: RequestParams | undefined };
58
59type ErrorType = { message: string; stacktrace: string; name: string };
60
61export class ClientDevice {
62 private readonly networkInspectorPluginName: string;
63
64 private _id: string;
65 private _query: ClientQuery;
66 private _connection: ReactiveSocket<any, any> | null | undefined;
67 private logger: OutputChannelLogger;
68 private messageIdCounter: number;
69 private sdkVersion: number;
70 private activePlugins: Set<string>;
71 private inspectorView: InspectorView;
72
73 private requestCallbacks: Map<
74 number,
75 {
76 resolve: (data: any) => void;
77 reject: (err: Error) => void;
78 metadata: RequestMetadata;
79 }
80 >;
81
82 constructor(
83 id: string,
84 query: ClientQuery,
85 connection: ReactiveSocket<any, any> | null | undefined,
86 inspectorViewType: InspectorViewType,
87 logger: OutputChannelLogger,
88 ) {
89 this.networkInspectorPluginName = "Network";
90
91 this._id = id;
92 this._query = query;
93 this._connection = connection;
94 this.logger = logger;
95 this.messageIdCounter = 0;
96 this.sdkVersion = query.sdk_version || 0;
97 this.activePlugins = new Set();
98 this.requestCallbacks = new Map();
99 this.inspectorView = InspectorViewFactory.getInspectorView(inspectorViewType);
100 }
101
102 get id(): string {
103 return this._id;
104 }
105
106 get query(): ClientQuery {
107 return this._query;
108 }
109
110 get connection(): ReactiveSocket<any, any> | null | undefined {
111 return this._connection;
112 }
113
114 public async init(): Promise<void> {
115 const plugins = await this.loadPlugins();
116 if (plugins.includes(this.networkInspectorPluginName)) {
117 this.initNetworkInspectorPlugin();
118 }
119 await this.inspectorView.init();
120 }
121
122 public onMessage(msg: string): void {
123 if (typeof msg !== "string") {
124 return;
125 }
126
127 let rawData;
128 try {
129 rawData = JSON.parse(msg);
130 } catch (err) {
131 this.logger.error(`Invalid JSON: ${msg}`);
132 return;
133 }
134
135 const data: {
136 id?: number;
137 method?: string;
138 params?: RequestParams;
139 success?: Record<string, any>;
140 error?: ErrorType;
141 } = rawData;
142
143 if (!data.id) {
144 const { error } = data;
145 if (error) {
146 this.logger.error(
147 `Error received from device ${
148 data.method ? `when calling ${data.method}` : ""
149 }: ${error.message} + \nDevice Stack Trace: ${error.stacktrace}`,
150 );
151 } else if (data.method === "execute" && data.params) {
152 this.inspectorView.handleMessage(data.params);
153 }
154 return; // method === "execute";
155 }
156
157 if (this.sdkVersion < 1) {
158 const callbacks = this.requestCallbacks.get(data.id);
159 if (!callbacks) {
160 return;
161 }
162 this.requestCallbacks.delete(data.id);
163 this.onResponse(data, callbacks.resolve, callbacks.reject);
164 }
165 }
166
167 private initNetworkInspectorPlugin(): void {
168 this.activePlugins.add(this.networkInspectorPluginName);
169 this.rawSend("init", { plugin: this.networkInspectorPluginName });
170 }
171
172 private async loadPlugins(): Promise<string[]> {
173 const plugins = await this.rawCall<{ plugins: string[] }>("getPlugins", false).then(
174 data => data.plugins,
175 );
176 return plugins;
177 }
178
179 private rawSend(method: string, params?: Record<string, any>): void {
180 const data = {
181 method,
182 params,
183 };
184 if (this._connection) {
185 this._connection.fireAndForget({ data: JSON.stringify(data) });
186 }
187 }
188
189 private rawCall<T>(method: string, fromPlugin: boolean, params?: RequestParams): Promise<T> {
190 return new Promise((resolve, reject) => {
191 const id = this.messageIdCounter++;
192 const metadata: RequestMetadata = {
193 method,
194 id,
195 params,
196 };
197
198 if (this.sdkVersion < 1) {
199 this.requestCallbacks.set(id, { reject, resolve, metadata });
200 }
201
202 const data = {
203 id,
204 method,
205 params,
206 };
207
208 const plugin = params ? params.api : undefined;
209
210 if (this.sdkVersion < 1) {
211 if (this._connection) {
212 this._connection.fireAndForget({ data: JSON.stringify(data) });
213 }
214 return;
215 }
216
217 if (!fromPlugin || this.isAcceptingMessagesFromPlugin(plugin)) {
218 this._connection!.requestResponse({
219 data: JSON.stringify(data),
220 }).subscribe({
221 onComplete: payload => {
222 if (!fromPlugin || this.isAcceptingMessagesFromPlugin(plugin)) {
223 const response: {
224 success?: Record<string, any>;
225 error?: ErrorType;
226 } = JSON.parse(payload.data);
227
228 this.onResponse(response, resolve, reject);
229 }
230 },
231 onError: e => {
232 reject(e);
233 },
234 });
235 } else {
236 reject(
237 new Error(
238 `Cannot send ${method}, client is not accepting messages for plugin ${plugin}`,
239 ),
240 );
241 }
242 });
243 }
244
245 private onResponse(
246 data: {
247 success?: Record<string, any>;
248 error?: ErrorType;
249 },
250 resolve: ((a: any) => any) | undefined,
251 reject: (error: ErrorType) => any,
252 ): void {
253 if (data.success) {
254 resolve && resolve(data.success);
255 } else if (data.error) {
256 reject(data.error);
257 const { error } = data;
258 if (error) {
259 this.logger.debug(error.message);
260 }
261 }
262 }
263
264 private isAcceptingMessagesFromPlugin(plugin: string | null | undefined): boolean {
265 return !!(this._connection && (!plugin || this.activePlugins.has(plugin)));
266 }
267}
268
269/**
270 * @preserve
271 * End region: https://github.com/facebook/flipper/blob/v0.79.1/desktop/app/src/Client.tsx
272 */
273