microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
1.10.2

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/cdp-proxy/reactNativeCDPProxy.ts

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