microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
fced706df75baed17e7c94a4b833767cbc0edf9f

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/extensionServer.ts

194lines · 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 * as net from "net";
5import * as Q from "q";
6import * as vscode from "vscode";
7
8import * as em from "../common/extensionMessaging";
9import {Log} from "../common/log/log";
10import {LogLevel} from "../common/log/logHelper";
11import {Packager} from "../common/packager";
12import {PackagerStatus, PackagerStatusIndicator} from "./packagerStatusIndicator";
13import {LogCatMonitor} from "./android/logCatMonitor";
14import {FileSystem} from "../common/node/fileSystem";
15import {Telemetry} from "../common/telemetry";
16
17export class ExtensionServer implements vscode.Disposable {
18 private serverInstance: net.Server = null;
19 private messageHandlerDictionary: { [id: number]: ((...argArray: any[]) => Q.Promise<any>) } = {};
20 private reactNativePackager: Packager;
21 private reactNativePackageStatusIndicator: PackagerStatusIndicator;
22 private pipePath: string;
23 private logCatMonitor: LogCatMonitor = null;
24
25 public constructor(projectRootPath: string, reactNativePackager: Packager, packagerStatusIndicator: PackagerStatusIndicator) {
26
27 this.pipePath = new em.MessagingChannel(projectRootPath).getPath();
28 this.reactNativePackager = reactNativePackager;
29 this.reactNativePackageStatusIndicator = packagerStatusIndicator;
30
31 /* register handlers for all messages */
32 this.messageHandlerDictionary[em.ExtensionMessage.START_PACKAGER] = this.startPackager;
33 this.messageHandlerDictionary[em.ExtensionMessage.STOP_PACKAGER] = this.stopPackager;
34 this.messageHandlerDictionary[em.ExtensionMessage.PREWARM_BUNDLE_CACHE] = this.prewarmBundleCache;
35 this.messageHandlerDictionary[em.ExtensionMessage.START_MONITORING_LOGCAT] = this.startMonitoringLogCat;
36 this.messageHandlerDictionary[em.ExtensionMessage.STOP_MONITORING_LOGCAT] = this.stopMonitoringLogCat;
37 this.messageHandlerDictionary[em.ExtensionMessage.SEND_TELEMETRY] = this.sendTelemetry;
38 }
39
40 /**
41 * Starts the server.
42 */
43 public setup(): Q.Promise<void> {
44
45 let deferred = Q.defer<void>();
46
47 let launchCallback = (error: any) => {
48 Log.logInternalMessage(LogLevel.Info, `Extension messaging server started at ${this.pipePath}.`);
49 if (error) {
50 deferred.reject(error);
51 } else {
52 deferred.resolve(null);
53 }
54 };
55
56 this.serverInstance = net.createServer(this.handleSocket.bind(this));
57 this.serverInstance.on("error", this.recoverServer.bind(this));
58 this.serverInstance.listen(this.pipePath, launchCallback);
59
60 return deferred.promise;
61 }
62
63 /**
64 * Stops the server.
65 */
66 public dispose(): void {
67 if (this.serverInstance) {
68 this.serverInstance.close();
69 this.serverInstance = null;
70 }
71
72 this.stopMonitoringLogCat();
73 }
74
75 /**
76 * Message handler for START_PACKAGER.
77 */
78 private startPackager(): Q.Promise<any> {
79 return this.reactNativePackager.start()
80 .then(() => this.reactNativePackageStatusIndicator.updatePackagerStatus(PackagerStatus.PACKAGER_STARTED));
81 }
82
83 /**
84 * Message handler for STOP_PACKAGER.
85 */
86 private stopPackager(): Q.Promise<any> {
87 return this.reactNativePackager.stop()
88 .then(() => this.reactNativePackageStatusIndicator.updatePackagerStatus(PackagerStatus.PACKAGER_STOPPED));
89 }
90
91 /**
92 * Message handler for PREWARM_BUNDLE_CACHE.
93 */
94 private prewarmBundleCache(platform: string): Q.Promise<any> {
95 return this.reactNativePackager.prewarmBundleCache(platform);
96 }
97
98 /**
99 * Message handler for START_MONITORING_LOGCAT.
100 */
101 private startMonitoringLogCat(deviceId: string, logCatArguments: string): Q.Promise<any> {
102 this.stopMonitoringLogCat(); // Stop previous logcat monitor if it's running
103
104 // this.logCatMonitor can be mutated, so we store it locally too
105 const logCatMonitor = this.logCatMonitor = new LogCatMonitor(deviceId, logCatArguments);
106 logCatMonitor.start() // The LogCat will continue running forever, so we don't wait for it
107 .catch(error =>
108 Log.logWarning("Error while monitoring LogCat", error))
109 .done();
110
111 return Q.resolve<void>(void 0);
112 }
113
114 private stopMonitoringLogCat(): Q.Promise<void> {
115 if (this.logCatMonitor) {
116 this.logCatMonitor.dispose();
117 this.logCatMonitor = null;
118 }
119
120 return Q.resolve<void>(void 0);
121 }
122
123 /**
124 * Sends telemetry
125 */
126 private sendTelemetry(extensionId: string, extensionVersion: string, appInsightsKey: string, eventName: string, properties: {[key: string]: string}, measures: {[key: string]: number}): Q.Promise<any> {
127 Telemetry.sendExtensionTelemetry(extensionId, extensionVersion, appInsightsKey, eventName, properties, measures);
128 return Q.resolve({});
129 }
130
131 /**
132 * Extension message handler.
133 */
134 private handleExtensionMessage(messageWithArgs: em.MessageWithArguments): Q.Promise<any> {
135 let handler = this.messageHandlerDictionary[messageWithArgs.message];
136 if (handler) {
137 Log.logInternalMessage(LogLevel.Info, "Handling message: " + em.ExtensionMessage[messageWithArgs.message]);
138 return handler.apply(this, messageWithArgs.args);
139 } else {
140 return Q.reject("Invalid message: " + messageWithArgs.message);
141 }
142 }
143
144 /**
145 * Handles connections to the server.
146 */
147 private handleSocket(socket: net.Socket): void {
148 let handleError = (e: any) => {
149 Log.logError(e);
150 socket.end(em.ErrorMarker);
151 };
152
153 let dataCallback = (data: any) => {
154 try {
155 let messageWithArgs: em.MessageWithArguments = JSON.parse(data);
156 this.handleExtensionMessage(messageWithArgs)
157 .then(result => {
158 socket.end(JSON.stringify(result));
159 })
160 .catch((e) => { handleError(e); })
161 .done();
162 } catch (e) {
163 handleError(e);
164 }
165 };
166
167 socket.on("data", dataCallback);
168 };
169
170 /**
171 * Recovers the server in case the named socket we use already exists, but no other instance of VSCode is active.
172 */
173 private recoverServer(error: any): void {
174 let errorHandler = (e: any) => {
175 /* The named socket is not used. */
176 if (e.code === "ECONNREFUSED") {
177 new FileSystem().removePathRecursivelyAsync(this.pipePath)
178 .then(() => {
179 this.serverInstance.listen(this.pipePath);
180 })
181 .done();
182 }
183 };
184
185 /* The named socket already exists. */
186 if (error.code === "EADDRINUSE") {
187 let clientSocket = new net.Socket();
188 clientSocket.on("error", errorHandler);
189 clientSocket.connect(this.pipePath, function() {
190 clientSocket.end();
191 });
192 }
193 }
194}