UNPKG

extensor

Version:

Extra funcionalities to socket.io

592 lines (493 loc) 16 kB
import Debug from 'debug'; import { build } from 'schemapack'; import EventEmitter from 'eventemitter3'; import { promisify } from 'util'; // A type of promise-like that resolves synchronously and supports only one observer const _iteratorSymbol = /*#__PURE__*/ typeof Symbol !== "undefined" ? (Symbol.iterator || (Symbol.iterator = Symbol("Symbol.iterator"))) : "@@iterator"; const _asyncIteratorSymbol = /*#__PURE__*/ typeof Symbol !== "undefined" ? (Symbol.asyncIterator || (Symbol.asyncIterator = Symbol("Symbol.asyncIterator"))) : "@@asyncIterator"; // Asynchronously call a function and send errors to recovery continuation function _catch(body, recover) { try { var result = body(); } catch(e) { return recover(e); } if (result && result.then) { return result.then(void 0, recover); } return result; } var LocalStorage = /*#__PURE__*/function () { function LocalStorage() { this.store = new Map(); this.itens = {}; } var _proto = LocalStorage.prototype; _proto.get = function get(key) { try { var _this2 = this; return Promise.resolve(_this2.store.get(key) || null); } catch (e) { return Promise.reject(e); } }; _proto.set = function set(key, value) { try { var _this4 = this; return Promise.resolve(_this4.store.set(key, value)); } catch (e) { return Promise.reject(e); } }; _proto.del = function del(key) { try { var _this6 = this; _this6.store["delete"](key); return Promise.resolve(1); } catch (e) { return Promise.reject(e); } }; _proto.deleteAll = function deleteAll(keys) { try { var _this8 = this; keys.map(function (key) { return _this8.store["delete"](key); }); return Promise.resolve(keys.length); } catch (e) { return Promise.reject(e); } }; return LocalStorage; }(); var debug = /*#__PURE__*/Debug("extensor"); var ParserDebug = /*#__PURE__*/debug.extend("parser"); var ServerDebug = /*#__PURE__*/debug.extend("server"); var ClientDebug = /*#__PURE__*/debug.extend("client"); var slugify = function slugify(str) { return str.replace(/ /g, "").toLowerCase(); }; function defer() { var defer = {}; defer.resolve = null; defer.reject = null; defer.promise = new Promise(function (resolve, reject) { defer.resolve = resolve; defer.reject = reject; }); return defer; } var kExtensorAuthHandling = /*#__PURE__*/Symbol("extensorIoAuthHandling"); var kSocketAuthStatus = /*#__PURE__*/Symbol("extensorSocketAuthStatus"); var kSocketAuthTimeout = /*#__PURE__*/Symbol("extensorSocketAuthTimeout"); var debug$1 = /*#__PURE__*/ServerDebug.extend("unique"); function uniqueConnections(io, _temp) { var _ref = _temp === void 0 ? {} : _temp, identifier = _ref.identifier, _ref$onError = _ref.onError, onError = _ref$onError === void 0 ? function (local, e, _socket) { debug$1("%s: %s", local, e.message); } : _ref$onError, _ref$storage = _ref.storage, storage = _ref$storage === void 0 ? new LocalStorage() : _ref$storage; debug$1("start"); var keys = new Set(); io.use(function (socket, next) { try { return Promise.resolve(_catch(function () { function _temp3() { var _exit = false; var id = getId(socket, identifier); var key = "extensorUniqueState:" + id; debug$1("handling socket id: %s; identifier: %s", socket.id, id); return Promise.resolve(storage.get(key)).then(function (_storage$get) { if (_storage$get) { _exit = true; return next(new Error("multiple attemp")); } return Promise.resolve(storage.set(key, 1)).then(function () { keys.add(key); socket.on("disconnect", function () { keys["delete"](key); storage.del(key)["catch"](function (e) { onError("disconnect", e, socket); }); }); next(); }); }); } var _temp2 = function () { if (io[kExtensorAuthHandling] === true && identifier) { return Promise.resolve(socket.auth).then(function () {}); } }(); return _temp2 && _temp2.then ? _temp2.then(_temp3) : _temp3(_temp2); }, function (e) { debug$1("error in check of socket id \"" + socket.id + "\": " + e.message); onError("middleware", e, socket); return next(new Error("multiple check fail")); })); } catch (e) { return Promise.reject(e); } }); process.on("exit", function () { storage.deleteAll(Array.from(keys)); }); } function getId(socket, identifier) { if (identifier && identifier in socket) { return socket[identifier]; } var headers = socket.handshake.headers; var ip = headers["x-real-ip"] || headers["x-forwarded-for"] || socket.handshake.address; return "" + ip + slugify(headers["user-agent"]); } function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; subClass.__proto__ = superClass; } // @ts-ignore function buildSchemas(map) { var packetParser = {}; var schemas = {}; var idmap = {}; Object.keys(map).forEach(function (event) { var item = map[event]; if (typeof item.id === "undefined") throw new Error("Undefined ID for event " + event); if (!item.schema) throw new Error("Undefined schema for event " + event); packetParser[event] = build({ _id: "varuint", data: item.schema, id: "varint", nsp: "string" }); schemas[event] = build(item.schema); idmap[item.id] = event; }); return { schemas: schemas, packetParser: packetParser, idmap: idmap }; } var TYPES = { CONNECT: 0, DISCONNECT: 1, EVENT: 2, ACK: 3, ERROR: 4, BINARY_EVENT: 5, BINARY_ACK: 6 }; var debug$2 = /*#__PURE__*/ParserDebug.extend("schemapack").extend("encoder"); var createEncoder = function createEncoder(schemas, packetParser) { return /*#__PURE__*/function () { function Encoder() {} var _proto = Encoder.prototype; _proto.encode = function encode(packet, callback) { switch (packet.type) { case TYPES.EVENT: case TYPES.BINARY_EVENT: if (!(packet.data[0] in schemas)) { return callback([this.json(packet)]); } return callback([this.pack(packet)]); default: return callback([this.json(packet)]); } }; _proto.json = function json(packet) { try { debug$2("json packet %j", packet); return JSON.stringify(packet); } catch (e) { debug$2("json error: %s", e.message); return "{\"type\": " + TYPES.ERROR + ", \"data\": \"parser error\"}"; } }; _proto.pack = function pack(packet) { try { var eventName = packet.data[0]; var eventSchema = packetParser[eventName]; debug$2("binary packet %j", packet); return eventSchema.encode({ _id: schemas[eventName].id, data: packet.data[1], nsp: packet.nsp, id: !("id" in packet) ? -1 : packet.id }); } catch (e) { debug$2("binary error: %s", e.message); return "{\"type\": " + TYPES.ERROR + ", \"data\": \"parser error\"}"; } }; return Encoder; }(); }; var debug$3 = /*#__PURE__*/ParserDebug.extend("schemapack").extend("decoder"); var createDecoder = function createDecoder(idmap, packetParser) { return /*#__PURE__*/function (_EventEmitter) { _inheritsLoose(Decoder, _EventEmitter); function Decoder() { return _EventEmitter.apply(this, arguments) || this; } var _proto = Decoder.prototype; _proto.add = function add(packet) { if (typeof packet === "string") { this.parseJSON(packet); } else { this.parseBinary(packet); } }; _proto.parseJSON = function parseJSON(packet) { try { var decoded = JSON.parse(packet); debug$3("json packet %j", decoded); this.emit("decoded", decoded); } catch (e) { debug$3("json error, packet: %s, error: %s", packet, e.message); this.emit("decoded", { type: TYPES.ERROR, data: "parser error: " + e.message }); } }; _proto.parseBinary = function parseBinary(packet) { try { var view = new Uint8Array(packet); var _id = view[0]; var eventName = idmap[_id]; var eventSchema = packetParser[eventName]; var sent = eventSchema.decode(packet); var finalPacket = { type: TYPES.EVENT, data: [eventName, sent.data], nsp: sent.nsp }; if (sent.id !== -1) { finalPacket.id = sent.id; } debug$3("binary packet: %j", finalPacket); this.emit("decoded", finalPacket); } catch (e) { debug$3("binary error: %s", e.message); this.emit("decoded", { type: TYPES.ERROR, data: "parser error: " + e.message }); } }; _proto.destroy = function destroy() {}; return Decoder; }(EventEmitter); }; function buildSchemapackParser(dataStruct) { var _schemaBuilder = buildSchemas(_extends({}, dataStruct)), idmap = _schemaBuilder.idmap, packetParser = _schemaBuilder.packetParser, schemas = _schemaBuilder.schemas; return { parser: { Encoder: createEncoder(dataStruct, packetParser), Decoder: createDecoder(idmap, packetParser) }, idmap: idmap, schemas: schemas }; } var index = { __proto__: null, schemapack: buildSchemapackParser }; var EVENTS = { AUTHORIZE: "extensorAuthorize", AUTH_RESULT: "extensorAuthResult", AUTH_TIMEOUT: "authTimeout" }; var debug$4 = /*#__PURE__*/ClientDebug.extend("auth"); function ClientAuthWrapper(socket, data, callback) { if (callback) { return authorize(socket, data, callback, callback); } return new Promise(function (resolve, reject) { authorize(socket, data, resolve, reject); }); } function authorize(socket, data, onSuccess, onError) { socket.emit(EVENTS.AUTHORIZE, data, function (result) { debug$4("[socket %s]: server response %s", socket.id, result); if (result.error) { debug$4("[socket %s]: auth failed, error: %s", socket.id, result.error); onError(new Error(result.error)); return; } merge(socket, result.merge); onSuccess(); }); } function merge(socket, props) { debug$4("[socket %s]: merge socket props: %o", socket.id, props); for (var prop in props) { socket[prop] = props[prop]; } } var defaultAuthorized = [EVENTS.AUTHORIZE, EVENTS.AUTH_RESULT, EVENTS.AUTH_TIMEOUT]; function watchPackets(socket, authorize) { var authorizedEvents = authorize ? [].concat(authorize, defaultAuthorized) : defaultAuthorized; socket.use(function (packet, next) { if (socket[kSocketAuthStatus] || authorizedEvents.indexOf(packet[0]) !== -1) return next(); }); } var debug$5 = /*#__PURE__*/ServerDebug.extend("auth"); function ServerAuthWrapper(io, handler, options) { if (options === void 0) { options = {}; } io[kExtensorAuthHandling] = true; io.use(function (socket, next) { try { watchPackets(socket, options.authorizedEvents); socket[kSocketAuthStatus] = false; debug$5("[socket %s]: watching packets", socket.id); next(); if ("timeout" in options && options.timeout !== false) { socket[kSocketAuthTimeout] = setTimeout(function (socket) { socket.emit(EVENTS.AUTH_TIMEOUT); debug$5("[socket %s]: auth timeout", socket.id); socket.disconnect(true); }, options.timeout, socket); } var _defer = defer(), resolve = _defer.resolve, reject = _defer.reject, promise = _defer.promise; socket.auth = promise; socket.on(EVENTS.AUTHORIZE, function (data, ack) { try { var done = function done(result) { var merge = {}; if (result instanceof Error) { debug$5("[socket %s]: auth failed, error: %s", socket.id, result.message); reject(result); ack({ error: result.message }); socket.disconnect(); return; } debug$5("[socket %s]: auth successful", socket.id); socket[kSocketAuthStatus] = true; if (result instanceof Object) { debug$5("[socket %s]: send props to client socket: %o", socket.id, result); merge = _extends({}, result); } resolve(); ack({ merge: merge }); }; if ("timeout" in options && options.timeout !== false) { debug$5("[socket %s]: clear timeout", socket.id, options.timeout); clearTimeout(socket[kSocketAuthTimeout]); } debug$5("[socket %s]: waiting handler result", socket.id); return Promise.resolve(_catch(function () { return Promise.resolve(handler({ socket: socket, data: data, done: done })).then(function (result) { return typeof result !== "undefined" && done(result); }); }, function (e) { done(e); })); } catch (e) { return Promise.reject(e); } }); return Promise.resolve(); } catch (e) { return Promise.reject(e); } }); } function server(io, handler, options) { return ServerAuthWrapper(io, handler, options); } function client(socket, data, callback) { return ClientAuthWrapper(socket, data, callback); } var index$1 = { __proto__: null, server: server, client: client }; var RedisStorageAdapter = /*#__PURE__*/function () { function RedisStorageAdapter(client) { this.client = client; this.getAsync = promisify(this.client.get).bind(this.client); this.setAsync = promisify(this.client.set).bind(this.client); this.delAsync = promisify(this.client.del).bind(this.client); } var _proto = RedisStorageAdapter.prototype; _proto.get = function get(key) { return this.getAsync(key); }; _proto.set = function set(key, value) { return this.setAsync(key, value); }; _proto.del = function del(key) { return this.delAsync(key); }; _proto.deleteAll = function deleteAll(keys) { return this.delAsync(keys); // avoid redis type error }; return RedisStorageAdapter; }(); var IORedisStorageAdapter = /*#__PURE__*/function () { function IORedisStorageAdapter(client) { this.client = client; } var _proto = IORedisStorageAdapter.prototype; _proto.get = function get(key) { return this.client.get(key); }; _proto.set = function set(key, value) { return this.client.set(key, value); }; _proto.del = function del(key) { return this.client.del(key); }; _proto.deleteAll = function deleteAll(keys) { var _this$client; return (_this$client = this.client).del.apply(_this$client, keys); }; return IORedisStorageAdapter; }(); var index$2 = { __proto__: null, Redis: RedisStorageAdapter, IORedis: IORedisStorageAdapter, Local: LocalStorage }; export { EVENTS, index$1 as auth, index as parsers, index$2 as storageAdapters, uniqueConnections as unique }; //# sourceMappingURL=extensor.esm.js.map