microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
0.1.3

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/extension/extensionServer.ts

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