microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
1.5.1

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/networkInspector/clientDevice.ts

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