UNPKG

@loopeco/socketio

Version:

A enhanced LoopBack's WebSocket server based on socket.io

215 lines 8.14 kB
"use strict"; 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 express_1 = require("@loopback/express"); const decorators_1 = require("./decorators"); const keys_1 = require("./keys"); const socketio_controller_factory_1 = require("./socketio-controller-factory"); const middleware_1 = require("./middleware"); const types_1 = require("./types"); const debug = debug_1.default('loopeco: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.SOCKETIO)) 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 express_1.BaseMiddlewareRegistry { 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); this.attachedHttpServer = () => undefined; } get httpServer() { var _a; return (_a = this.attachedHttpServer()) !== null && _a !== void 0 ? _a : this.ownedHttpServer; } get listening() { return this.httpServer ? this.httpServer.listening : false; } get url() { var _a; return (_a = this.httpServer) === null || _a === void 0 ? void 0 : _a.url; } /** * Register a sock.io middleware function * @param fn */ use(fn) { return this.io.use(fn); } /** * 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 = 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(exports.getNamespaceKeyForName(meta.name)).to(nsp); } nsp.use(middleware_1.toSocketIoMiddleware(this)); nsp.on('connection', socket => this.createSocketHandler(controllerClass)(socket)); return nsp; } /** * Register a socket.io controller * @param controllerClass */ controller(controllerClass) { debug('Adding controller %s', controllerClass.name); const binding = core_1.createBindingFromClass(controllerClass, { namespace: keys_1.SocketIoBindings.CONTROLLERS_NAMESPACE, defaultScope: core_1.BindingScope.TRANSIENT, }).tag(keys_1.SocketIoTags.SOCKETIO, 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); } } } attach(httpServer) { this.attachedHttpServer = typeof httpServer === 'function' ? httpServer : () => httpServer; } /** * Start the socketio server */ async start() { let httpServer = this.attachedHttpServer(); if (!httpServer) { const requestListener = this.getSync(keys_1.SocketIoBindings.REQUEST_LISTENER); const serverOptions = resolveHttpServerConfig(this.options.httpServerOptions); httpServer = this.ownedHttpServer = new http_server_1.HttpServer(requestListener, serverOptions); await httpServer.start(); } this.io.attach(httpServer.server, this.options.socketIoOptions); } /** * Stop the socketio server */ async stop() { const closePromise = new Promise((resolve, _reject) => { this.io.close(() => { resolve(); }); }); await closePromise; if (this.ownedHttpServer) await this.ownedHttpServer.stop(); } /** * Retrieve the middleware context from the socket * @param socket - Socket object */ getConnectionContext(socket) { return types_1.getConnectionContext(socket); } /** * 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); } }; } }; SocketIoServer = tslib_1.__decorate([ tslib_1.__param(0, core_1.inject(core_1.CoreBindings.APPLICATION_INSTANCE)), tslib_1.__param(1, core_1.config({ fromBinding: core_1.CoreBindings.APPLICATION_INSTANCE })), tslib_1.__metadata("design:paramtypes", [core_1.Application, Object]) ], SocketIoServer); exports.SocketIoServer = 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(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