UNPKG

@loopback/socketio

Version:

LoopBack's WebSocket server based on socket.io

201 lines 7.66 kB
"use strict"; // Copyright IBM Corp. and LoopBack contributors 2019,2020. All Rights Reserved. // Node module: @loopback/socketio // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT Object.defineProperty(exports, "__esModule", { value: true }); exports.SocketIoServer = exports.socketIoControllers = exports.getNamespaceKeyForName = void 0; const tslib_1 = require("tslib"); const core_1 = require("@loopback/core"); const http_server_1 = require("@loopback/http-server"); const debug_1 = tslib_1.__importDefault(require("debug")); const lodash_1 = require("lodash"); const socket_io_1 = require("socket.io"); const decorators_1 = require("./decorators"); const keys_1 = require("./keys"); const socketio_controller_factory_1 = require("./socketio-controller-factory"); const debug = (0, debug_1.default)('loopback:socketio:server'); const getNamespaceKeyForName = (name) => `socketio.namespace.${name}`; exports.getNamespaceKeyForName = getNamespaceKeyForName; /** * A binding filter to match socket.io controllers * @param binding - Binding object */ const socketIoControllers = binding => { // It has to be tagged with `controller` if (!binding.tagNames.includes(core_1.CoreTags.CONTROLLER)) return false; // It can be explicitly tagged with `socket.io` if (binding.tagNames.includes(keys_1.SocketIoTags.SOCKET_IO)) return true; // Now inspect socket.io decorations if (binding.valueConstructor) { const cls = binding.valueConstructor; const classMeta = core_1.MetadataInspector.getClassMetadata(decorators_1.SOCKET_IO_METADATA, cls); if (classMeta != null) { debug('SocketIo metadata found at class %s', cls.name); return true; } const subscribeMeta = core_1.MetadataInspector.getAllMethodMetadata(decorators_1.SOCKET_IO_SUBSCRIBE_METADATA, cls.prototype); if (subscribeMeta != null) { debug('SocketIo subscribe metadata found at methods of %s', cls.name); return true; } const connectMeta = core_1.MetadataInspector.getAllMethodMetadata(decorators_1.SOCKET_IO_CONNECT_METADATA, cls.prototype); if (connectMeta != null) { debug('SocketIo connect metadata found at methods of %s', cls.name); return true; } } return false; }; exports.socketIoControllers = socketIoControllers; /** * A socketio server */ let SocketIoServer = class SocketIoServer extends core_1.Context { constructor(app, options = {}) { super(app); this.app = app; this.options = options; if (!options.socketIoOptions) { this.io = new socket_io_1.Server(); } else { this.io = new socket_io_1.Server(options.socketIoOptions); } app.bind(keys_1.SocketIoBindings.IO).to(this.io); this.controllers = this.createView(exports.socketIoControllers); } get listening() { return this.httpServer ? this.httpServer.listening : false; } /** * Register a sock.io middleware function * @param fn */ use(fn) { return this.io.use(fn); } get url() { var _a; return (_a = this.httpServer) === null || _a === void 0 ? void 0 : _a.url; } /** * Register a socketio controller * @param controllerClass * @param meta */ route(controllerClass, meta) { if (meta instanceof RegExp || typeof meta === 'string') { meta = { namespace: meta }; } if (meta == null) { meta = (0, decorators_1.getSocketIoMetadata)(controllerClass); } const nsp = (meta === null || meta === void 0 ? void 0 : meta.namespace) ? this.io.of(meta.namespace) : this.io; if (meta === null || meta === void 0 ? void 0 : meta.name) { this.app.bind((0, exports.getNamespaceKeyForName)(meta.name)).to(nsp); } nsp.on('connection', async (socket) => { await this.createSocketHandler(controllerClass)(socket); }); return nsp; } /** * Create socket handler from the controller class * @param controllerClass */ createSocketHandler(controllerClass) { return async (socket) => { debug('SocketIo connected: id=%s namespace=%s', socket.id, socket.nsp.name); try { await new socketio_controller_factory_1.SocketIoControllerFactory(this, controllerClass, socket).create(); } catch (err) { debug('SocketIo error: error creating controller instance con connection', err); } }; } /** * Register a socket.io controller * @param controllerClass */ controller(controllerClass) { debug('Adding controller %s', controllerClass.name); const binding = (0, core_1.createBindingFromClass)(controllerClass, { namespace: keys_1.SocketIoBindings.CONTROLLERS_NAMESPACE, defaultScope: core_1.BindingScope.TRANSIENT, }).tag(keys_1.SocketIoTags.SOCKET_IO, core_1.CoreTags.CONTROLLER); this.add(binding); debug('Controller binding: %j', binding); return binding; } /** * Discover all socket.io controllers and register routes */ discoverAndRegister() { const bindings = this.controllers.bindings; for (const binding of bindings) { if (binding.valueConstructor) { debug('Controller binding found: %s %s', binding.key, binding.valueConstructor.name); this.route(binding.valueConstructor); } } } /** * Start the socketio server */ async start() { const requestListener = this.getSync(keys_1.SocketIoBindings.REQUEST_LISTENER); const serverOptions = resolveHttpServerConfig(this.options.httpServerOptions); this.httpServer = new http_server_1.HttpServer(requestListener, serverOptions); await this.httpServer.start(); this.io.attach(this.httpServer.server, this.options.socketIoOptions); } /** * Stop the socketio server */ async stop() { const closePromise = new Promise((resolve, _reject) => { this.io .close(() => { resolve(); }) .catch(err => { }); }); await closePromise; if (this.httpServer) await this.httpServer.stop(); } }; exports.SocketIoServer = SocketIoServer; exports.SocketIoServer = SocketIoServer = tslib_1.__decorate([ tslib_1.__param(0, (0, core_1.inject)(core_1.CoreBindings.APPLICATION_INSTANCE)), tslib_1.__param(1, (0, core_1.config)({ fromBinding: core_1.CoreBindings.APPLICATION_INSTANCE })), tslib_1.__metadata("design:paramtypes", [core_1.Application, Object]) ], SocketIoServer); const DEFAULT_CONFIG = { port: 3000, cors: { origin: '*', methods: 'GET,HEAD,PUT,PATCH,POST,DELETE', preflightContinue: false, optionsSuccessStatus: 204, maxAge: 86400, credentials: true, }, }; function resolveHttpServerConfig(applicationConfig) { const result = Object.assign((0, lodash_1.cloneDeep)(DEFAULT_CONFIG), applicationConfig); // Can't check falsiness, 0 is a valid port. if (result.port == null) { result.port = 3000; } if (result.host == null) { // Set it to '' so that the http server will listen on all interfaces result.host = undefined; } return result; } //# sourceMappingURL=socketio.server.js.map