UNPKG

atom-nuclide

Version:

A unified developer experience for web and mobile development, built as a suite of features on top of Atom to provide hackability and the support of an active community.

329 lines (276 loc) 11.1 kB
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); /* * Copyright (c) 2015-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the license found in the LICENSE file in * the root directory of this source tree. */ function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { var callNext = step.bind(null, 'next'); var callThrow = step.bind(null, 'throw'); function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(callNext, callThrow); } } callNext(); }); }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _blocked2; function _blocked() { return _blocked2 = _interopRequireDefault(require('./blocked')); } var _config2; function _config() { return _config2 = require('./config'); } var _utils2; function _utils() { return _utils2 = require('./utils'); } var _nuclideVersion2; function _nuclideVersion() { return _nuclideVersion2 = require('../../nuclide-version'); } var _assert2; function _assert() { return _assert2 = _interopRequireDefault(require('assert')); } var _nuclideLogging2; function _nuclideLogging() { return _nuclideLogging2 = require('../../nuclide-logging'); } var _ws2; function _ws() { return _ws2 = _interopRequireDefault(require('ws')); } var _nuclideRpc2; function _nuclideRpc() { return _nuclideRpc2 = require('../../nuclide-rpc'); } var _QueuedTransport2; function _QueuedTransport() { return _QueuedTransport2 = require('./QueuedTransport'); } var _WebSocketTransport2; function _WebSocketTransport() { return _WebSocketTransport2 = require('./WebSocketTransport'); } var _commonsNodeEvent2; function _commonsNodeEvent() { return _commonsNodeEvent2 = require('../../commons-node/event'); } var connect = require('connect'); var http = require('http'); var https = require('https'); var logger = (0, (_nuclideLogging2 || _nuclideLogging()).getLogger)(); var NuclideServer = (function () { function NuclideServer(options, services) { _classCallCheck(this, NuclideServer); (0, (_assert2 || _assert()).default)(NuclideServer._theServer == null); NuclideServer._theServer = this; var serverKey = options.serverKey; var serverCertificate = options.serverCertificate; var port = options.port; var certificateAuthorityCertificate = options.certificateAuthorityCertificate; var trackEventLoop = options.trackEventLoop; this._version = (0, (_nuclideVersion2 || _nuclideVersion()).getVersion)().toString(); this._app = connect(); this._attachUtilHandlers(); if (serverKey && serverCertificate && certificateAuthorityCertificate) { var webServerOptions = { key: serverKey, cert: serverCertificate, ca: certificateAuthorityCertificate, requestCert: true, rejectUnauthorized: true }; this._webServer = https.createServer(webServerOptions, this._app); } else { this._webServer = http.createServer(this._app); } this._port = port; this._webSocketServer = this._createWebSocketServer(); this._clients = new Map(); this._setupServices(); // Setup 1.0 and 2.0 services. if (trackEventLoop) { (0, (_blocked2 || _blocked()).default)(function (ms) { logger.info('NuclideServer event loop blocked for ' + ms + 'ms'); }); } this._rpcServiceRegistry = (_nuclideRpc2 || _nuclideRpc()).ServiceRegistry.createLocal(services); } _createClass(NuclideServer, [{ key: '_attachUtilHandlers', value: function _attachUtilHandlers() { var _this = this; // Add specific method handlers. ['get', 'post', 'delete', 'put'].forEach(function (methodName) { // $FlowFixMe - Use map instead of computed property on library type. _this._app[methodName] = function (uri, handler) { _this._app.use(uri, function (request, response, next) { if (request.method.toUpperCase() !== methodName.toUpperCase()) { // skip if method doesn't match. return next(); } else { handler(request, response, next); } }); }; }); } }, { key: '_createWebSocketServer', value: function _createWebSocketServer() { var _this2 = this; var webSocketServer = new (_ws2 || _ws()).default.Server({ server: this._webServer }); webSocketServer.on('connection', function (socket) { return _this2._onConnection(socket); }); webSocketServer.on('error', function (error) { return logger.error('WebSocketServer Error:', error); }); return webSocketServer; } }, { key: '_setupServices', value: function _setupServices() { // Lazy require these functions so that we could spyOn them while testing in // ServiceIntegrationTestHelper. this._xhrServiceRegistry = {}; this._setupHeartbeatHandler(); // Setup error handler. this._app.use(function (error, request, response, next) { if (error != null) { (0, (_utils2 || _utils()).sendJsonResponse)(response, { code: error.code, message: error.message }, 500); } else { next(); } }); } }, { key: '_setupHeartbeatHandler', value: function _setupHeartbeatHandler() { var _this3 = this; this._registerService('/' + (_config2 || _config()).HEARTBEAT_CHANNEL, _asyncToGenerator(function* () { return _this3._version; }), 'post', true); } }, { key: '_closeConnection', value: function _closeConnection(client) { if (this._clients.get(client.getTransport().id) === client) { this._clients.delete(client.getTransport().id); client.dispose(); } } }, { key: 'connect', value: function connect() { var _this4 = this; return new Promise(function (resolve, reject) { _this4._webServer.on('listening', function () { resolve(); }); _this4._webServer.on('error', function (e) { _this4._webServer.removeAllListeners(); reject(e); }); _this4._webServer.listen(_this4._port); }); } /** * Calls a registered service with a name and arguments. */ }, { key: 'callService', value: function callService(serviceName, args) { var serviceFunction = this._xhrServiceRegistry[serviceName]; if (!serviceFunction) { throw Error('No service registered with name: ' + serviceName); } return serviceFunction.apply(this, args); } /** * Registers a service function to a service name. * This allows simple future calls of the service by name and arguments or http-triggered * endpoint calls with arguments serialized over http. */ }, { key: '_registerService', value: function _registerService(serviceName, serviceFunction, method, isTextResponse) { if (this._xhrServiceRegistry[serviceName]) { throw new Error('A service with this name is already registered:', serviceName); } this._xhrServiceRegistry[serviceName] = serviceFunction; this._registerHttpService(serviceName, method, isTextResponse); } }, { key: '_registerHttpService', value: function _registerHttpService(serviceName, method, isTextResponse) { var _this5 = this; var loweredCaseMethod = method.toLowerCase(); // $FlowFixMe - Use map instead of computed property. this._app[loweredCaseMethod](serviceName, _asyncToGenerator(function* (request, response, next) { try { var result = yield _this5.callService(serviceName, (0, (_utils2 || _utils()).deserializeArgs)(request.url)); if (isTextResponse) { (0, (_utils2 || _utils()).sendTextResponse)(response, result || ''); } else { (0, (_utils2 || _utils()).sendJsonResponse)(response, result); } } catch (e) { // Delegate to the registered connect error handler. next(e); } })); } }, { key: '_onConnection', value: function _onConnection(socket) { var _this6 = this; logger.debug('WebSocket connecting'); var client = null; var errorSubscription = (0, (_commonsNodeEvent2 || _commonsNodeEvent()).attachEvent)(socket, 'error', function (e) { return logger.error('WebSocket error before first message', e); }); socket.once('message', function (clientId) { errorSubscription.dispose(); client = _this6._clients.get(clientId); var transport = new (_WebSocketTransport2 || _WebSocketTransport()).WebSocketTransport(clientId, socket); if (client == null) { client = (_nuclideRpc2 || _nuclideRpc()).RpcConnection.createServer(_this6._rpcServiceRegistry, new (_QueuedTransport2 || _QueuedTransport()).QueuedTransport(clientId, transport)); _this6._clients.set(clientId, client); } else { (0, (_assert2 || _assert()).default)(clientId === client.getTransport().id); client.getTransport().reconnect(transport); } }); } }, { key: 'close', value: function close() { (0, (_assert2 || _assert()).default)(NuclideServer._theServer === this); NuclideServer._theServer = null; this._webSocketServer.close(); this._webServer.close(); } }], [{ key: 'shutdown', value: function shutdown() { logger.info('Shutting down the server'); try { if (NuclideServer._theServer != null) { NuclideServer._theServer.close(); } } catch (e) { logger.error('Error while shutting down, but proceeding anyway:', e); } finally { (0, (_nuclideLogging2 || _nuclideLogging()).flushLogsAndExit)(0); } } }, { key: 'closeConnection', value: function closeConnection(client) { logger.info('Closing client: #' + client.getTransport().id); if (NuclideServer._theServer != null) { NuclideServer._theServer._closeConnection(client); } } }]); return NuclideServer; })(); module.exports = NuclideServer;