diff --git a/react-packager/index.js b/react-packager/index.js index 960342f6..ef143264 100644 --- a/react-packager/index.js +++ b/react-packager/index.js @@ -73,17 +73,6 @@ exports.getOrderedDependencyPaths = function(options, bundleOptions) { }); }; -exports.createClientFor = function(options) { - if (options.verbose) { - enableDebug(); - } - startSocketInterface(); - return ( - require('./src/SocketInterface') - .getOrCreateSocketFor(omit(options, ['verbose'])) - ); -}; - function useGracefulFs() { var fs = require('fs'); var gracefulFs = require('graceful-fs'); @@ -111,7 +100,6 @@ function createServer(options) { enableDebug(); } - startSocketInterface(); var Server = require('./src/Server'); return new Server(omit(options, ['verbose'])); } @@ -135,19 +123,3 @@ function omit(obj, blacklistedKeys) { return clone; }, {}); } - -// we need to listen on a socket as soon as a server is created, but only once. -// This file also serves as entry point when spawning a socket server; in that -// case we need to start the server immediately. -var didStartSocketInterface = false; -function startSocketInterface() { - if (didStartSocketInterface) { - return; - } - didStartSocketInterface = true; - require('./src/SocketInterface').listenOnServerMessages(); -} - -if (require.main === module) { // used as entry point - startSocketInterface(); -} diff --git a/react-packager/src/SocketInterface/SocketClient.js b/react-packager/src/SocketInterface/SocketClient.js deleted file mode 100644 index c0016e17..00000000 --- a/react-packager/src/SocketInterface/SocketClient.js +++ /dev/null @@ -1,170 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ -'use strict'; - -const Bundle = require('../Bundler/Bundle'); -const PrepackBundle = require('../Bundler/PrepackBundle'); -const Promise = require('promise'); -const bser = require('bser'); -const debug = require('debug')('ReactNativePackager:SocketClient'); -const fs = require('fs'); -const net = require('net'); -const path = require('path'); -const tmpdir = require('os').tmpdir(); - -const LOG_PATH = path.join(tmpdir, 'react-packager.log'); - -class SocketClient { - static create(sockPath) { - return new SocketClient(sockPath).onReady(); - } - - constructor(sockPath) { - debug('connecting to', sockPath); - - this._sock = net.connect(sockPath); - this._ready = new Promise((resolve, reject) => { - this._sock.on('connect', () => { - this._sock.removeAllListeners('error'); - process.on('uncaughtException', (error) => { - console.error('uncaught error', error.stack); - setImmediate(() => process.exit(1)); - }); - resolve(this); - }); - this._sock.on('error', (e) => { - e.message = `Error connecting to server on ${sockPath} ` + - `with error: ${e.message}`; - e.message += getServerLogs(); - - reject(e); - }); - }); - - this._resolvers = Object.create(null); - const bunser = new bser.BunserBuf(); - this._sock.on('data', (buf) => bunser.append(buf)); - bunser.on('value', (message) => this._handleMessage(message)); - - this._sock.on('close', () => { - if (!this._closing) { - const terminate = (result) => { - const sockPathExists = fs.existsSync(sockPath); - throw new Error( - 'Server closed unexpectedly.\n' + - 'Server ping connection attempt result: ' + result + '\n' + - 'Socket path: `' + sockPath + '` ' + - (sockPathExists ? ' exists.' : 'doesn\'t exist') + '\n' + - getServerLogs() - ); - }; - - // before throwing ping the server to see if it's still alive - const socket = net.connect(sockPath); - socket.on('connect', () => { - socket.end(); - terminate('OK'); - }); - socket.on('error', error => terminate(error)); - } - }); - } - - onReady() { - return this._ready; - } - - getDependencies(main) { - return this._send({ - type: 'getDependencies', - data: main, - }); - } - - getOrderedDependencyPaths(main) { - return this._send({ - type: 'getOrderedDependencyPaths', - data: main, - }); - } - - buildBundle(options) { - return this._send({ - type: 'buildBundle', - data: options, - }).then(json => Bundle.fromJSON(json)); - } - - buildPrepackBundle(options) { - return this._send({ - type: 'buildPrepackBundle', - data: options, - }).then(json => PrepackBundle.fromJSON(json)); - } - - _send(message) { - message.id = uid(); - this._sock.write(bser.dumpToBuffer(message)); - return new Promise((resolve, reject) => { - this._resolvers[message.id] = {resolve, reject}; - }); - } - - _handleMessage(message) { - if (!(message && message.id && message.type)) { - throw new Error( - 'Malformed message from server ' + JSON.stringify(message) - ); - } - - debug('got message with type', message.type); - - const resolver = this._resolvers[message.id]; - if (!resolver) { - throw new Error( - 'Unrecognized message id (' + message.id + ') ' + - 'message already resolved or never existed.' - ); - } - - delete this._resolvers[message.id]; - - if (message.type === 'error') { - const errorLog = - message.data && message.data.indexOf('TimeoutError') === -1 - ? 'See logs ' + LOG_PATH - : getServerLogs(); - - resolver.reject(new Error(message.data + '\n' + errorLog)); - } else { - resolver.resolve(message.data); - } - } - - close() { - debug('closing connection'); - this._closing = true; - this._sock.end(); - } -} - -module.exports = SocketClient; - -function uid(len) { - len = len || 7; - return Math.random().toString(35).substr(2, len); -} - -function getServerLogs() { - if (fs.existsSync(LOG_PATH)) { - return '\nServer logs:\n' + fs.readFileSync(LOG_PATH, 'utf8'); - } - - return ''; -} diff --git a/react-packager/src/SocketInterface/SocketServer.js b/react-packager/src/SocketInterface/SocketServer.js deleted file mode 100644 index 3b107c47..00000000 --- a/react-packager/src/SocketInterface/SocketServer.js +++ /dev/null @@ -1,224 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ -'use strict'; - -const Promise = require('promise'); -const Server = require('../Server'); -const bser = require('bser'); -const debug = require('debug')('ReactNativePackager:SocketServer'); -const fs = require('fs'); -const net = require('net'); - -const MAX_IDLE_TIME = 30 * 1000; -const MAX_STARTUP_TIME = 5 * 60 * 1000; - -class SocketServer { - constructor(sockPath, options) { - this._server = net.createServer(); - this._server.listen(sockPath); - this._ready = new Promise((resolve, reject) => { - this._server.once('error', (e) => reject(e)); - this._server.once('listening', () => { - // Remove error listener so we make sure errors propagate. - this._server.removeAllListeners('error'); - this._server.on( - 'close', - () => debug('server closed') - ); - - debug( - 'Process %d listening on socket path %s ' + - 'for server with options %j', - process.pid, - sockPath, - options - ); - resolve(this); - process.on('exit', code => { - debug('exit code:', code); - fs.unlinkSync(sockPath); - }); - }); - }); - - process.on('uncaughtException', (error) => { - debug('uncaught error', error.stack); - setImmediate(() => process.exit(1)); - }); - - this._server.on('connection', (sock) => this._handleConnection(sock)); - - // Disable the file watcher. - options.nonPersistent = true; - this._packagerServer = new Server(options); - this._dieEventually(MAX_STARTUP_TIME); - } - - onReady() { - return this._ready; - } - - _handleConnection(sock) { - debug('connection to server', process.pid); - - const bunser = new bser.BunserBuf(); - sock.on('data', (buf) => bunser.append(buf)); - bunser.on('value', (m) => this._handleMessage(sock, m)); - bunser.on('error', (e) => { - e.message = 'Unhandled error from the bser buffer. ' + - 'Either error on encoding or message handling: \n' + - e.message; - throw e; - }); - } - - _handleMessage(sock, m) { - if (!m || !m.id || !m.data) { - console.error('SocketServer recieved a malformed message: %j', m); - return; - } - - debug('got request', m); - - // Debounce the kill timer. - this._dieEventually(); - - const handleError = (error) => { - debug('request error', error); - this._reply(sock, m.id, 'error', error.stack); - - // Fatal error from JSTransformer transform workers. - if (error.type === 'ProcessTerminatedError') { - setImmediate(() => process.exit(1)); - } - }; - - switch (m.type) { - case 'getDependencies': - this._packagerServer.getDependencies(m.data).then( - ({ dependencies }) => this._reply(sock, m.id, 'result', dependencies), - handleError, - ); - break; - - case 'buildBundle': - this._packagerServer.buildBundle(m.data).then( - (result) => this._reply(sock, m.id, 'result', result), - handleError, - ); - break; - - case 'buildPrepackBundle': - this._packagerServer.buildPrepackBundle(m.data).then( - (result) => this._reply(sock, m.id, 'result', result), - handleError, - ); - break; - - case 'getOrderedDependencyPaths': - this._packagerServer.getOrderedDependencyPaths(m.data).then( - (dependencies) => this._reply(sock, m.id, 'result', dependencies), - handleError, - ); - break; - - default: - this._reply(sock, m.id, 'error', 'Unknown message type: ' + m.type); - } - } - - _reply(sock, id, type, data) { - debug('request finished', type); - - try { - data = toJSON(data); - } catch (e) { - console.error('SocketServer exception:', e); - } - - sock.write(bser.dumpToBuffer({ - id, - type, - data, - })); - - // Debounce the kill timer to make sure all the bytes are sent through - // the socket and the client has time to fully finish and disconnect. - this._dieEventually(); - } - - _dieEventually(delay = MAX_IDLE_TIME) { - clearTimeout(this._deathTimer); - this._deathTimer = setTimeout(() => { - this._server.getConnections((error, numConnections) => { - // error is passed when connection count is below 0 - if (error || numConnections <= 0) { - debug('server dying', process.pid); - process.exit(); - } - this._dieEventually(); - }); - }, delay); - } - - static listenOnServerIPCMessages() { - process.on('message', (message) => { - if (!(message && message.type && message.type === 'createSocketServer')) { - return; - } - - debug('server got ipc message', message); - - const {options, sockPath} = message.data; - // regexp doesn't naturally serialize to json. - options.blacklistRE = new RegExp(options.blacklistRE.source); - - new SocketServer(sockPath, options).onReady().then( - () => { - debug('succesfully created server'); - process.send({ type: 'createdServer' }); - }, - error => { - if (error.code === 'EADDRINUSE' || error.code === 'EEXIST') { - // Server already listening, this may happen if multiple - // clients where started in quick succussion (buck). - process.send({ type: 'createdServer' }); - - // Kill this server because some other server with the same - // config and socket already started. - debug('server already started'); - setImmediate(() => process.exit()); - } else { - debug('error creating server', error.code); - throw error; - } - } - ).done(); - }); - } -} - -// TODO move this to bser code. -function toJSON(object) { - if (!(object && typeof object === 'object')) { - return object; - } - - if (object.toJSON) { - return object.toJSON(); - } - - for (var p in object) { - object[p] = toJSON(object[p]); - } - - return object; -} - -module.exports = SocketServer; diff --git a/react-packager/src/SocketInterface/__tests__/SocketClient-test.js b/react-packager/src/SocketInterface/__tests__/SocketClient-test.js deleted file mode 100644 index df8d8f09..00000000 --- a/react-packager/src/SocketInterface/__tests__/SocketClient-test.js +++ /dev/null @@ -1,113 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ -'use strict'; - -jest.setMock('worker-farm', function() { return () => {}; }) - .setMock('uglify-js') - .mock('net') - .dontMock('../SocketClient'); - -var Bundle = require('../../Bundler/Bundle'); -var SocketClient = require('../SocketClient'); -var bser = require('bser'); -var net = require('net'); - -describe('SocketClient', () => { - let sock; - let bunser; - - beforeEach(() => { - const {EventEmitter} = require.requireActual('events'); - sock = new EventEmitter(); - sock.write = jest.genMockFn(); - - net.connect.mockImpl(() => sock); - - bunser = new EventEmitter(); - bser.BunserBuf.mockImpl(() => bunser); - bser.dumpToBuffer.mockImpl((a) => a); - - Bundle.fromJSON.mockImpl((a) => a); - }); - - pit('create a connection', () => { - const client = new SocketClient('/sock'); - sock.emit('connect'); - return client.onReady().then(c => { - expect(c).toBe(client); - expect(net.connect).toBeCalledWith('/sock'); - }); - }); - - pit('buildBundle', () => { - const client = new SocketClient('/sock'); - sock.emit('connect'); - const options = { entryFile: '/main' }; - - const promise = client.buildBundle(options); - - expect(sock.write).toBeCalled(); - const message = sock.write.mock.calls[0][0]; - expect(message.type).toBe('buildBundle'); - expect(message.data).toEqual(options); - expect(typeof message.id).toBe('string'); - - bunser.emit('value', { - id: message.id, - type: 'result', - data: { bundle: 'foo' }, - }); - - return promise.then(bundle => expect(bundle).toEqual({ bundle: 'foo' })); - }); - - pit('getDependencies', () => { - const client = new SocketClient('/sock'); - sock.emit('connect'); - const main = '/main'; - - const promise = client.getDependencies(main); - - expect(sock.write).toBeCalled(); - const message = sock.write.mock.calls[0][0]; - expect(message.type).toBe('getDependencies'); - expect(message.data).toEqual(main); - expect(typeof message.id).toBe('string'); - - bunser.emit('value', { - id: message.id, - type: 'result', - data: ['a', 'b', 'c'], - }); - - return promise.then(result => expect(result).toEqual(['a', 'b', 'c'])); - }); - - pit('handle errors', () => { - const client = new SocketClient('/sock'); - sock.emit('connect'); - const main = '/main'; - - const promise = client.getDependencies(main); - - expect(sock.write).toBeCalled(); - const message = sock.write.mock.calls[0][0]; - expect(message.type).toBe('getDependencies'); - expect(message.data).toEqual(main); - expect(typeof message.id).toBe('string'); - - bunser.emit('value', { - id: message.id, - type: 'error', - data: 'some error' - }); - - return promise.catch(m => expect(m.message).toContain('some error')); - }); -}); diff --git a/react-packager/src/SocketInterface/__tests__/SocketInterface-test.js b/react-packager/src/SocketInterface/__tests__/SocketInterface-test.js deleted file mode 100644 index 4c1aa812..00000000 --- a/react-packager/src/SocketInterface/__tests__/SocketInterface-test.js +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ -'use strict'; - -jest.setMock('uglify-js') - .mock('child_process') - .dontMock('../'); - -var SocketInterface = require('../'); -var SocketClient = require('../SocketClient'); -var childProcess = require('child_process'); -var fs = require('fs'); - -describe('SocketInterface', () => { - describe('getOrCreateSocketFor', () => { - pit('creates socket path by hashing options', () => { - fs.existsSync = jest.genMockFn().mockImpl(() => true); - fs.unlinkSync = jest.genMockFn(); - let callback; - - childProcess.spawn.mockImpl(() => ({ - on: (event, cb) => callback = cb, - send: (message) => { - setImmediate(() => callback({ type: 'createdServer' })); - }, - unref: () => undefined, - disconnect: () => undefined, - })); - - // Check that given two equivelant server options, we end up with the same - // socket path. - const options1 = { projectRoots: ['/root'], transformModulePath: '/root/foo' }; - const options2 = { transformModulePath: '/root/foo', projectRoots: ['/root'] }; - const options3 = { projectRoots: ['/root', '/root2'] }; - - return SocketInterface.getOrCreateSocketFor(options1).then(() => { - expect(SocketClient.create).toBeCalled(); - return SocketInterface.getOrCreateSocketFor(options2).then(() => { - expect(SocketClient.create.mock.calls.length).toBe(2); - expect(SocketClient.create.mock.calls[0]).toEqual(SocketClient.create.mock.calls[1]); - return SocketInterface.getOrCreateSocketFor(options3).then(() => { - expect(SocketClient.create.mock.calls.length).toBe(3); - expect(SocketClient.create.mock.calls[1]).not.toEqual(SocketClient.create.mock.calls[2]); - }); - }); - }); - }); - - pit('should fork a server', () => { - fs.existsSync = jest.genMockFn().mockImpl(() => false); - fs.unlinkSync = jest.genMockFn(); - let sockPath; - let callback; - - childProcess.spawn.mockImpl(() => ({ - on: (event, cb) => callback = cb, - send: (message) => { - expect(message.type).toBe('createSocketServer'); - expect(message.data.options).toEqual({ projectRoots: ['/root'] }); - expect(message.data.sockPath).toContain('react-packager'); - sockPath = message.data.sockPath; - - setImmediate(() => callback({ type: 'createdServer' })); - }, - unref: () => undefined, - disconnect: () => undefined, - })); - - return SocketInterface.getOrCreateSocketFor({ projectRoots: ['/root'] }) - .then(() => { - expect(SocketClient.create).toBeCalledWith(sockPath); - }); - }); - }); -}); diff --git a/react-packager/src/SocketInterface/__tests__/SocketServer-test.js b/react-packager/src/SocketInterface/__tests__/SocketServer-test.js deleted file mode 100644 index fb25fb91..00000000 --- a/react-packager/src/SocketInterface/__tests__/SocketServer-test.js +++ /dev/null @@ -1,102 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ -'use strict'; - -jest.autoMockOff(); -jest.setMock('uglify-js') - .mock('net') - .mock('fs') - .mock('bser') - .mock('../../Server'); - -var PackagerServer = require('../../Server'); -var SocketServer = require('../SocketServer'); -var bser = require('bser'); -var net = require('net'); - -describe('SocketServer', () => { - let netServer; - let bunser; - let processOn; - - beforeEach(() => { - const {EventEmitter} = require.requireActual('events'); - netServer = new EventEmitter(); - netServer.listen = jest.genMockFn(); - net.createServer.mockImpl(() => netServer); - - bunser = new EventEmitter(); - bser.BunserBuf.mockImpl(() => bunser); - bser.dumpToBuffer.mockImpl((a) => a); - - // Don't attach `process.on('exit')` handlers directly from SocketServer - processOn = process.on; - process.on = jest.genMockFn(); - }); - - afterEach(() => { - process.on = processOn; - }); - - pit('create a server', () => { - const server = new SocketServer('/sock', { projectRoots: ['/root'] }); - netServer.emit('listening'); - return server.onReady().then(s => { - expect(s).toBe(server); - expect(netServer.listen).toBeCalledWith('/sock'); - }); - }); - - pit('handles getDependencies message', () => { - const server = new SocketServer('/sock', { projectRoots: ['/root'] }); - netServer.emit('listening'); - return server.onReady().then(() => { - const sock = { on: jest.genMockFn(), write: jest.genMockFn() }; - netServer.emit('connection', sock); - PackagerServer.prototype.getDependencies.mockImpl( - () => Promise.resolve({ dependencies: ['a', 'b', 'c'] }) - ); - bunser.emit('value', { type: 'getDependencies', id: 1, data: '/main' }); - expect(PackagerServer.prototype.getDependencies).toBeCalledWith('/main'); - - // Run pending promises. - return Promise.resolve().then(() => { - expect(sock.write).toBeCalledWith( - { id: 1, type: 'result', data: ['a', 'b', 'c']} - ); - }); - }); - }); - - pit('handles buildBundle message', () => { - const server = new SocketServer('/sock', { projectRoots: ['/root'] }); - netServer.emit('listening'); - return server.onReady().then(() => { - const sock = { on: jest.genMockFn(), write: jest.genMockFn() }; - netServer.emit('connection', sock); - PackagerServer.prototype.buildBundle.mockImpl( - () => Promise.resolve({ bundle: 'foo' }) - ); - bunser.emit( - 'value', - { type: 'buildBundle', id: 1, data: { options: 'bar' } } - ); - expect(PackagerServer.prototype.buildBundle).toBeCalledWith( - { options: 'bar' } - ); - - // Run pending promises. - return Promise.resolve().then(() => { - expect(sock.write).toBeCalledWith( - { id: 1, type: 'result', data: { bundle: 'foo' }} - ); - }); - }); - }); -}); diff --git a/react-packager/src/SocketInterface/index.js b/react-packager/src/SocketInterface/index.js deleted file mode 100644 index f9f3a77b..00000000 --- a/react-packager/src/SocketInterface/index.js +++ /dev/null @@ -1,146 +0,0 @@ -/** - * Copyright (c) 2015-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - */ -'use strict'; - -const Promise = require('promise'); -const SocketClient = require('./SocketClient'); -const SocketServer = require('./SocketServer'); -const crypto = require('crypto'); -const debug = require('debug')('ReactNativePackager:SocketInterface'); -const fs = require('fs'); -const net = require('net'); -const path = require('path'); -const tmpdir = require('os').tmpdir(); -const {spawn} = require('child_process'); - -const CREATE_SERVER_TIMEOUT = 5 * 60 * 1000; - -const SocketInterface = { - getOrCreateSocketFor(options) { - return new Promise((resolve, reject) => { - const hash = crypto.createHash('md5'); - Object.keys(options).sort().forEach(key => { - const value = options[key]; - if (value) { - hash.update( - options[key] != null && typeof value === 'string' - ? value - : JSON.stringify(value) - ); - } - }); - - let sockPath = path.join( - tmpdir, - 'react-packager-' + hash.digest('hex') - ); - if (process.platform === 'win32'){ - // on Windows, use a named pipe, convert sockPath into a valid pipe name - // based on https://gist.github.com/domenic/2790533 - sockPath = sockPath.replace(/^\//, '') - sockPath = sockPath.replace(/\//g, '-') - sockPath = '\\\\.\\pipe\\' + sockPath - } - - if (existsSync(sockPath)) { - var sock = net.connect(sockPath); - sock.on('connect', () => { - SocketClient.create(sockPath).then( - client => { - sock.end(); - resolve(client); - }, - error => { - sock.end(); - reject(error); - } - ); - }); - sock.on('error', (e) => { - try { - debug('deleting socket for not responding', sockPath); - fs.unlinkSync(sockPath); - } catch (err) { - // Another client might have deleted it first. - } - createServer(resolve, reject, options, sockPath); - }); - } else { - createServer(resolve, reject, options, sockPath); - } - }); - }, - - listenOnServerMessages() { - return SocketServer.listenOnServerIPCMessages(); - } -}; - -function createServer(resolve, reject, options, sockPath) { - const logPath = path.join(tmpdir, 'react-packager.log'); - - const timeout = setTimeout( - () => reject( - new Error( - 'Took too long to start server. Server logs: \n' + - fs.readFileSync(logPath, 'utf8') - ) - ), - CREATE_SERVER_TIMEOUT, - ); - - const log = fs.openSync(logPath, 'a'); - - // Enable server debugging by default since it's going to a log file. - const env = Object.assign({}, process.env); - env.DEBUG = 'ReactNativePackager:SocketServer'; - - // We have to go through the main entry point to make sure - // we go through the babel require hook. - const child = spawn( - process.execPath, - [path.join(__dirname, '..', '..', 'index.js')], - { - detached: true, - env: env, - stdio: ['ipc', log, log] - } - ); - - child.unref(); - - child.on('message', m => { - if (m && m.type && m.type === 'createdServer') { - clearTimeout(timeout); - child.disconnect(); - - resolve(SocketClient.create(sockPath)); - } - }); - - if (options.blacklistRE) { - options.blacklistRE = { source: options.blacklistRE.source }; - } - - child.send({ - type: 'createSocketServer', - data: { sockPath, options } - }); -} - -function existsSync(filename) { - try { - fs.accessSync(filename); - return true; - } catch(ex) { - return false; - } -} - -module.exports = SocketInterface;