microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
refactor-some-tm-grammars-for-extension-log

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/cdp-proxy/reactNativeCDPProxy.ts

219lines · 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 { IncomingMessage } from "http";
5import {
6 Connection,
7 Server,
8 WebSocketTransport,
9 IProtocolCommand,
10 IProtocolError,
11 IProtocolSuccess,
12} from "vscode-cdp-proxy";
13import { CancellationToken, EventEmitter } from "vscode";
14import { OutputChannelLogger } from "../extension/log/OutputChannelLogger";
15import { LogLevel } from "../extension/log/LogHelper";
16import { DebuggerEndpointHelper } from "./debuggerEndpointHelper";
17import { BaseCDPMessageHandler } from "./CDPMessageHandlers/baseCDPMessageHandler";
18
19export class ReactNativeCDPProxy {
20 private readonly PROXY_LOG_TAGS = {
21 DEBUGGER_COMMAND: "Command Debugger To Target",
22 APPLICATION_COMMAND: "Command Target To Debugger",
23 DEBUGGER_REPLY: "Reply From Debugger To Target",
24 APPLICATION_REPLY: "Reply From Target To Debugger",
25 };
26
27 private server: Server | null;
28 private hostAddress: string;
29 private port: number;
30 private debuggerTarget: Connection | null;
31 private applicationTarget: Connection | null;
32 private logger: OutputChannelLogger;
33 private logLevel: LogLevel;
34 private debuggerEndpointHelper: DebuggerEndpointHelper;
35 private CDPMessageHandler: BaseCDPMessageHandler;
36 private applicationTargetPort: number;
37 private browserInspectUri: string;
38 private cancellationToken: CancellationToken | undefined;
39 private applicationTargetEventEmitter: EventEmitter<unknown> = new EventEmitter();
40 private errorEventEmitter: EventEmitter<Error> = new EventEmitter();
41
42 public readonly onError = this.errorEventEmitter.event;
43 public readonly onApplicationTargetConnectionClosed = this.applicationTargetEventEmitter.event;
44
45 constructor(hostAddress: string, port: number, logLevel: LogLevel = LogLevel.None) {
46 this.port = port;
47 this.hostAddress = hostAddress;
48 this.logger = OutputChannelLogger.getChannel(
49 "React Native Chrome Proxy",
50 process.env.REACT_NATIVE_TOOLS_LAZY_LOGS !== "false",
51 false,
52 true,
53 );
54 this.logLevel = logLevel;
55 this.browserInspectUri = "";
56 this.debuggerEndpointHelper = new DebuggerEndpointHelper();
57 }
58
59 public async initializeServer(
60 CDPMessageHandler: BaseCDPMessageHandler,
61 logLevel: LogLevel,
62 cancellationToken?: CancellationToken,
63 ): Promise<void> {
64 this.logLevel = logLevel;
65 this.CDPMessageHandler = CDPMessageHandler;
66 this.cancellationToken = cancellationToken;
67
68 this.server = await Server.create({ port: this.port, host: this.hostAddress });
69 this.server.onConnection(this.onConnectionHandler.bind(this));
70 }
71
72 public async stopServer(): Promise<void> {
73 if (this.server) {
74 this.server.dispose();
75 this.server = null;
76 }
77
78 if (this.applicationTarget) {
79 await this.applicationTarget.close();
80 this.applicationTarget = null;
81 }
82
83 this.browserInspectUri = "";
84 this.cancellationToken = undefined;
85 }
86
87 public setBrowserInspectUri(browserInspectUri: string): void {
88 this.browserInspectUri = browserInspectUri;
89 }
90
91 public setApplicationTargetPort(applicationTargetPort: number): void {
92 this.applicationTargetPort = applicationTargetPort;
93 }
94
95 // eslint-disable-next-line @typescript-eslint/no-unused-vars
96 private async onConnectionHandler([debuggerTarget, request]: [
97 Connection,
98 IncomingMessage,
99 ]): Promise<void> {
100 this.debuggerTarget = debuggerTarget;
101
102 this.debuggerTarget.pause(); // don't listen for events until the target is ready
103
104 if (!this.browserInspectUri) {
105 if (this.cancellationToken) {
106 this.browserInspectUri = await this.debuggerEndpointHelper.retryGetWSEndpoint(
107 `http://localhost:${this.applicationTargetPort}`,
108 90,
109 this.cancellationToken,
110 );
111 } else {
112 this.browserInspectUri = await this.debuggerEndpointHelper.getWSEndpoint(
113 `http://localhost:${this.applicationTargetPort}`,
114 );
115 }
116 }
117
118 this.applicationTarget = new Connection(
119 await WebSocketTransport.create(this.browserInspectUri),
120 );
121
122 this.applicationTarget.onError(this.onApplicationTargetError.bind(this));
123 this.debuggerTarget.onError(this.onDebuggerTargetError.bind(this));
124
125 this.applicationTarget.onCommand(this.handleApplicationTargetCommand.bind(this));
126 this.debuggerTarget.onCommand(this.handleDebuggerTargetCommand.bind(this));
127
128 this.applicationTarget.onReply(this.handleApplicationTargetReply.bind(this));
129 this.debuggerTarget.onReply(this.handleDebuggerTargetReply.bind(this));
130
131 this.applicationTarget.onEnd(this.onApplicationTargetClosed.bind(this));
132 this.debuggerTarget.onEnd(this.onDebuggerTargetClosed.bind(this));
133
134 this.CDPMessageHandler?.setApplicationTarget(this.applicationTarget);
135 this.CDPMessageHandler?.setDebuggerTarget(this.debuggerTarget);
136
137 // dequeue any messages we got in the meantime
138 this.debuggerTarget.unpause();
139 }
140
141 private handleDebuggerTargetCommand(event: IProtocolCommand) {
142 this.logger.logWithCustomTag(
143 this.PROXY_LOG_TAGS.DEBUGGER_COMMAND,
144 JSON.stringify(event, null, 2),
145 this.logLevel,
146 );
147 const processedMessage = this.CDPMessageHandler.processDebuggerCDPMessage(event);
148
149 if (processedMessage.sendBack) {
150 this.debuggerTarget?.send(processedMessage.event);
151 } else {
152 this.applicationTarget?.send(processedMessage.event);
153 }
154 }
155
156 private handleApplicationTargetCommand(event: IProtocolCommand) {
157 this.logger.logWithCustomTag(
158 this.PROXY_LOG_TAGS.APPLICATION_COMMAND,
159 JSON.stringify(event, null, 2),
160 this.logLevel,
161 );
162 const processedMessage = this.CDPMessageHandler.processApplicationCDPMessage(event);
163
164 if (processedMessage.sendBack) {
165 this.applicationTarget?.send(processedMessage.event);
166 } else {
167 this.debuggerTarget?.send(processedMessage.event);
168 }
169 }
170
171 private handleDebuggerTargetReply(event: IProtocolError | IProtocolSuccess) {
172 this.logger.logWithCustomTag(
173 this.PROXY_LOG_TAGS.DEBUGGER_REPLY,
174 JSON.stringify(event, null, 2),
175 this.logLevel,
176 );
177 const processedMessage = this.CDPMessageHandler.processDebuggerCDPMessage(event);
178
179 if (processedMessage.sendBack) {
180 this.debuggerTarget?.send(processedMessage.event);
181 } else {
182 this.applicationTarget?.send(processedMessage.event);
183 }
184 }
185
186 private handleApplicationTargetReply(event: IProtocolError | IProtocolSuccess) {
187 this.logger.logWithCustomTag(
188 this.PROXY_LOG_TAGS.APPLICATION_REPLY,
189 JSON.stringify(event, null, 2),
190 this.logLevel,
191 );
192 const processedMessage = this.CDPMessageHandler.processApplicationCDPMessage(event);
193
194 if (processedMessage.sendBack) {
195 this.applicationTarget?.send(processedMessage.event);
196 } else {
197 this.debuggerTarget?.send(processedMessage.event);
198 }
199 }
200
201 private onDebuggerTargetError(err: Error) {
202 this.logger.error("Error on debugger transport", err);
203 }
204
205 private onApplicationTargetError(err: Error) {
206 this.logger.error("Error on application transport", err);
207 }
208
209 private async onApplicationTargetClosed() {
210 this.applicationTarget = null;
211 this.applicationTargetEventEmitter.fire({});
212 }
213
214 private async onDebuggerTargetClosed() {
215 this.browserInspectUri = "";
216 this.CDPMessageHandler.processDebuggerCDPMessage({ method: "close" });
217 this.debuggerTarget = null;
218 }
219}
220