extensor
Version:
Extra funcionalities to socket.io
602 lines (500 loc) • 16.3 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
var Debug = _interopDefault(require('debug'));
var schemapack = require('schemapack');
var EventEmitter = _interopDefault(require('eventemitter3'));
var util = require('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] = schemapack.build({
_id: "varuint",
data: item.schema,
id: "varint",
nsp: "string"
});
schemas[event] = schemapack.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 = util.promisify(this.client.get).bind(this.client);
this.setAsync = util.promisify(this.client.set).bind(this.client);
this.delAsync = util.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
};
exports.EVENTS = EVENTS;
exports.auth = index$1;
exports.parsers = index;
exports.storageAdapters = index$2;
exports.unique = uniqueConnections;
//# sourceMappingURL=extensor.cjs.development.js.map