omelop
Version:
[](https://travis-ci.org/node-omelop/omelop)
610 lines • 35.7 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.FrontendSession = exports.Session = exports.SessionService = void 0;
const events_1 = require("events");
const omelop_logger_1 = require("omelop-logger");
const utils = require("../../util/utils");
const path = require("path");
let logger = (0, omelop_logger_1.getLogger)('omelop', path.basename(__filename));
let FRONTEND_SESSION_FIELDS = ['id', 'frontendId', 'uid', '__sessionService__'];
let EXPORTED_SESSION_FIELDS = ['id', 'frontendId', 'uid', 'settings'];
let ST_INITED = 0;
let ST_CLOSED = 1;
/**
* Session service maintains the internal session for each client connection.
*
* Session service is created by session component and is only
* <b>available</b> in frontend servers. You can access the service by
* `app.get('sessionService')` or `app.sessionService` in frontend servers.
*
* @param {Object} opts constructor parameters
* @class
* @constructor
*/
class SessionService {
constructor(opts) {
this.akick = utils.promisify(this.kick.bind(this));
this.akickBySessionId = utils.promisify(this.kickBySessionId.bind(this));
this.abind = utils.promisify(this.bind.bind(this));
this.aunbind = utils.promisify(this.unbind.bind(this));
this.aimport = utils.promisify(this.import.bind(this));
this.aimportAll = utils.promisify(this.importAll.bind(this));
opts = opts || {};
this.singleSession = opts.singleSession;
this.sessions = {}; // sid -> session
this.uidMap = {}; // uid -> sessions
}
/**
* Create and return internal session.
*
* @param {Integer} sid uniqe id for the internal session
* @param {String} frontendId frontend server in which the internal session is created
* @param {Object} socket the underlying socket would be held by the internal session
*
* @return {Session}
*
* @memberOf SessionService
* @api private
*/
create(sid, frontendId, socket) {
let session = new Session(sid, frontendId, socket, this);
this.sessions[session.id] = session;
return session;
}
/**
* Bind the session with a user id.
*
* @memberOf SessionService
* @api private
*/
bind(sid, uid, cb) {
let session = this.sessions[sid];
if (!session) {
process.nextTick(function () {
cb(new Error('session does not exist, sid: ' + sid));
});
return;
}
if (session.uid) {
if (session.uid === uid) {
// already bound with the same uid
cb(null);
return;
}
// already bound with other uid
process.nextTick(function () {
cb(new Error('session has already bound with ' + session.uid));
});
return;
}
let sessions = this.uidMap[uid];
if (!!this.singleSession && !!sessions) {
process.nextTick(function () {
cb(new Error('singleSession is enabled, and session has already bound with uid: ' + uid));
});
return;
}
if (!sessions) {
sessions = this.uidMap[uid] = [];
}
for (let i = 0, l = sessions.length; i < l; i++) {
// session has binded with the uid
if (sessions[i].id === session.id) {
process.nextTick(cb);
return;
}
}
sessions.push(session);
session.bind(uid);
if (cb) {
process.nextTick(cb);
}
}
/**
* Unbind a session with the user id.
*
* @memberOf SessionService
* @api private
*/
unbind(sid, uid, cb) {
let session = this.sessions[sid];
if (!session) {
process.nextTick(function () {
cb(new Error('session does not exist, sid: ' + sid));
});
return;
}
if (!session.uid || session.uid !== uid) {
process.nextTick(function () {
cb(new Error('session has not bind with ' + session.uid));
});
return;
}
let sessions = this.uidMap[uid], sess;
if (sessions) {
for (let i = 0, l = sessions.length; i < l; i++) {
sess = sessions[i];
if (sess.id === sid) {
sessions.splice(i, 1);
break;
}
}
if (sessions.length === 0) {
delete this.uidMap[uid];
}
}
session.unbind(uid);
if (cb) {
process.nextTick(cb);
}
}
/**
* Get session by id.
*
* @param {Number} id The session id
* @return {Session}
*
* @memberOf SessionService
* @api private
*/
get(sid) {
return this.sessions[sid];
}
/**
* Get sessions by userId.
*
* @param {Number} uid User id associated with the session
* @return {Array} list of session binded with the uid
*
* @memberOf SessionService
* @api private
*/
getByUid(uid) {
return this.uidMap[uid];
}
/**
* Remove session by key.
*
* @param {Number} sid The session id
*
* @memberOf SessionService
* @api private
*/
remove(sid) {
let session = this.sessions[sid];
if (session) {
let uid = session.uid;
delete this.sessions[session.id];
let sessions = this.uidMap[uid];
if (!sessions) {
return;
}
for (let i = 0, l = sessions.length; i < l; i++) {
if (sessions[i].id === sid) {
sessions.splice(i, 1);
if (sessions.length === 0) {
delete this.uidMap[uid];
}
break;
}
}
}
}
/**
* Import the key/value into session.
*
* @api private
*/
import(sid, key, value, cb) {
let session = this.sessions[sid];
if (!session) {
utils.invokeCallback(cb, new Error('session does not exist, sid: ' + sid));
return;
}
session.set(key, value);
utils.invokeCallback(cb);
}
/**
* Import new value for the existed session.
*
* @memberOf SessionService
* @api private
*/
importAll(sid, settings, cb) {
let session = this.sessions[sid];
if (!session) {
utils.invokeCallback(cb, new Error('session does not exist, sid: ' + sid));
return;
}
for (let f in settings) {
session.set(f, settings[f]);
}
utils.invokeCallback(cb);
}
/**
* Kick all the session offline under the user id.
*
* @param {Number} uid user id asscociated with the session
* @param {Function} cb callback function
*
* @memberOf SessionService
*/
kick(uid, reason, cb) {
// compatible for old kick(uid, cb);
if (typeof reason === 'function') {
cb = reason;
reason = 'kick';
}
let sessions = this.getByUid(uid);
if (sessions) {
// notify client
let sids = [];
let self = this;
sessions.forEach(function (session) {
sids.push(session.id);
});
sids.forEach(function (sid) {
self.sessions[sid].closed(reason);
});
process.nextTick(function () {
utils.invokeCallback(cb);
});
}
else {
process.nextTick(function () {
utils.invokeCallback(cb);
});
}
}
/**
* Kick a user offline by session id.
*
* @param {Number} sid session id
* @param {Function} cb callback function
*
* @memberOf SessionService
*/
kickBySessionId(sid, reason, cb) {
if (typeof reason === 'function') {
cb = reason;
reason = 'kick';
}
let session = this.get(sid);
if (session) {
// notify client
session.closed(reason);
process.nextTick(function () {
utils.invokeCallback(cb);
});
}
else {
process.nextTick(function () {
utils.invokeCallback(cb);
});
}
}
/**
* Get client remote address by session id.
*
* @param {Number} sid session id
* @return {Object} remote address of client
*
* @memberOf SessionService
*/
getClientAddressBySessionId(sid) {
let session = this.get(sid);
if (session) {
let socket = session.__socket__;
return socket.remoteAddress;
}
else {
return null;
}
}
/**
* Send message to the client by session id.
*
* @param {String} sid session id
* @param {Object} msg message to send
*
* @memberOf SessionService
* @api private
*/
sendMessage(sid, msg) {
let session = this.sessions[sid];
if (!session) {
logger.debug('Fail to send message for non-existing session, sid: ' + sid + ' msg: ' + msg);
return false;
}
return send(this, session, msg);
}
/**
* Send message to the client by user id.
*
* @param {String} uid userId
* @param {Object} msg message to send
*
* @memberOf SessionService
* @api private
*/
sendMessageByUid(uid, msg) {
let sessions = this.uidMap[uid];
if (!sessions) {
logger.debug('fail to send message by uid for non-existing session. uid: %j', uid);
return false;
}
for (let i = 0, l = sessions.length; i < l; i++) {
send(this, sessions[i], msg);
}
}
/**
* Iterate all the session in the session service.
*
* @param {Function} cb callback function to fetch session
* @api private
*/
forEachSession(cb) {
for (let sid in this.sessions) {
cb(this.sessions[sid]);
}
}
/**
* Iterate all the binded session in the session service.
*
* @param {Function} cb callback function to fetch session
* @api private
*/
forEachBindedSession(cb) {
let i, l, sessions;
for (let uid in this.uidMap) {
sessions = this.uidMap[uid];
for (i = 0, l = sessions.length; i < l; i++) {
cb(sessions[i]);
}
}
}
/**
* Get sessions' quantity in specified server.
*
*/
getSessionsCount() {
return Object.keys(this.sessions).length;
}
}
exports.SessionService = SessionService;
/**
* Send message to the client that associated with the session.
*
* @api private
*/
let send = function (service, session, msg) {
session.send(msg);
return true;
};
/**
* Session maintains the relationship between client connection and user information.
* There is a session associated with each client connection. And it should bind to a
* user id after the client passes the identification.
*
* Session is created in frontend server and should not be accessed in handler.
* There is a proxy class called BackendSession in backend servers and FrontendSession
* in frontend servers.
*/
class Session extends events_1.EventEmitter {
constructor(sid, frontendId, socket, service) {
super();
this.id = sid; // r
this.frontendId = frontendId; // r
this.uid = null; // r
this.settings = {};
// private
this.__socket__ = socket;
this.__sessionService__ = service;
this.__state__ = ST_INITED;
}
/*
* Export current session as frontend session.
*/
toFrontendSession() {
return new FrontendSession(this);
}
/**
* Bind the session with the the uid.
*
* @param {Number} uid User id
* @api public
*/
bind(uid) {
this.uid = uid;
this.emit('bind', uid);
}
/**
* Unbind the session with the the uid.
*
* @param {Number} uid User id
* @api private
*/
unbind(uid) {
this.uid = null;
this.emit('unbind', uid);
}
set(keyOrValues, value) {
if (utils.isObject(keyOrValues)) {
let values = keyOrValues;
for (let i in values) {
this.settings[i] = values[i];
}
}
else {
this.settings[keyOrValues] = value;
}
}
/**
* Remove value from the session.
*
* @param {String} key session key
* @api public
*/
remove(key) {
delete this.settings[key];
}
/**
* Get value from the session.
*
* @param {String} key session key
* @return {Object} value associated with session key
* @api public
*/
get(key) {
return this.settings[key];
}
/**
* Send message to the session.
*
* @param {Object} msg final message sent to client
*/
send(msg) {
this.__socket__.send(msg);
}
/**
* Send message to the session in batch.
*
* @param {Array} msgs list of message
*/
sendBatch(msgs) {
this.__socket__.sendBatch(msgs);
}
/**
* Closed callback for the session which would disconnect client in next tick.
*
* @api public
*/
closed(reason) {
if (this.__state__ === ST_CLOSED) {
return;
}
logger.debug('session on [%s] is closed with session id: %s,uid:%s,reason:%j', this.frontendId, this.id, this.uid, reason);
this.__state__ = ST_CLOSED;
this.__sessionService__.remove(this.id);
this.emit('closed', this.toFrontendSession(), reason);
this.__socket__.emit('closing', reason);
let self = this;
// give a chance to send disconnect message to client
process.nextTick(function () {
self.__socket__.disconnect();
});
}
/**
* 是否在线
*/
get isOnline() {
return this.__state__ !== ST_CLOSED;
}
/**
* 获取客户端的地址
*/
get remoteAddress() {
return this.__socket__.remoteAddress;
}
}
exports.Session = Session;
/**
* Frontend session for frontend server.
*/
class FrontendSession extends events_1.EventEmitter {
constructor(session) {
super();
clone(session, this, FRONTEND_SESSION_FIELDS);
// deep copy for settings
this.settings = dclone(session.settings);
this.__session__ = session;
}
bind(uid, cb) {
let self = this;
this.__sessionService__.bind(this.id, uid, function (err) {
if (!err) {
self.uid = uid;
}
utils.invokeCallback(cb, err);
});
}
unbind(uid, cb) {
let self = this;
this.__sessionService__.unbind(this.id, uid, function (err) {
if (!err) {
self.uid = null;
}
utils.invokeCallback(cb, err);
});
}
set(key, value) {
this.settings[key] = value;
}
get(key) {
return this.settings[key];
}
push(key, cb) {
this.__sessionService__.import(this.id, key, this.get(key), cb);
}
pushAll(cb) {
this.__sessionService__.importAll(this.id, this.settings, cb);
}
on(event, listener) {
this.__session__.on(event, listener);
return super.on(event, listener);
}
abind(uid) {
return new Promise((resolve, reject) => this.bind(uid, (err, ret) => err ? reject(err) : resolve(ret)));
}
aunbind(uid) {
return new Promise((resolve, reject) => this.unbind(uid, (err, ret) => err ? reject(err) : resolve(ret)));
}
apush(key) {
return new Promise((resolve, reject) => this.push(key, (err, ret) => err ? reject(err) : resolve(ret)));
}
apushAll() {
return new Promise((resolve, reject) => this.pushAll((err, ret) => err ? reject(err) : resolve(ret)));
}
// abind = utils.promisify(this.bind.bind(this));
// aunbind = utils.promisify(this.unbind.bind(this));
// apush = utils.promisify(this.push.bind(this));
// apushAll = utils.promisify(this.pushAll.bind(this));
/**
* Export the key/values for serialization.
*
* @api private
*/
export() {
let res = {};
clone(this, res, EXPORTED_SESSION_FIELDS);
return res;
}
/**
* 是否在线
*/
get isOnline() {
return this.__session__.isOnline;
}
/**
* 获取客户端的地址
*/
get remoteAddress() {
return this.__session__.remoteAddress;
}
}
exports.FrontendSession = FrontendSession;
let clone = function (src, dest, includes) {
let f;
for (let i = 0, l = includes.length; i < l; i++) {
f = includes[i];
dest[f] = src[f];
}
};
let dclone = function (src) {
let res = {};
for (let f in src) {
res[f] = src[f];
}
return res;
};
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2Vzc2lvblNlcnZpY2UuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9saWIvY29tbW9uL3NlcnZpY2Uvc2Vzc2lvblNlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsbUNBQXNDO0FBRXRDLGlEQUEwQztBQUMxQywwQ0FBMEM7QUFHMUMsNkJBQTZCO0FBRTdCLElBQUksTUFBTSxHQUFHLElBQUEseUJBQVMsRUFBQyxRQUFRLEVBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDO0FBRTVELElBQUksdUJBQXVCLEdBQUcsQ0FBQyxJQUFJLEVBQUUsWUFBWSxFQUFFLEtBQUssRUFBRSxvQkFBb0IsQ0FBQyxDQUFDO0FBQ2hGLElBQUksdUJBQXVCLEdBQUcsQ0FBQyxJQUFJLEVBQUUsWUFBWSxFQUFFLEtBQUssRUFBRSxVQUFVLENBQUMsQ0FBQztBQUV0RSxJQUFJLFNBQVMsR0FBRyxDQUFDLENBQUM7QUFDbEIsSUFBSSxTQUFTLEdBQUcsQ0FBQyxDQUFDO0FBTWxCOzs7Ozs7Ozs7O0dBVUc7QUFDSCxNQUFhLGNBQWM7SUFLdkIsWUFBWSxJQUE0QjtRQWlZeEMsVUFBSyxHQUFpRCxLQUFLLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7UUFDNUYscUJBQWdCLEdBQWlELEtBQUssQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLGVBQWUsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQztRQUNsSCxVQUFLLEdBQTBDLEtBQUssQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQztRQUNyRixZQUFPLEdBQTBDLEtBQUssQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQztRQUN6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LENBQUM7SUFFRCxFQUFFLENBQUMsS0FBc0IsRUFBRSxRQUFrQztRQUN6RCxJQUFJLENBQUMsV0FBVyxDQUFDLEVBQUUsQ0FBQyxLQUFLLEVBQUUsUUFBUSxDQUFDLENBQUM7UUFDckMsT0FBTyxLQUFLLENBQUMsRUFBRSxDQUFDLEtBQUssRUFBRSxRQUFRLENBQUMsQ0FBQztJQUNyQyxDQUFDO0lBRUQsS0FBSyxDQUFDLEdBQVc7UUFDYixPQUFPLElBQUksT0FBTyxDQUFDLENBQUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxFQUFFLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLEdBQVUsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNuSCxDQUFDO0lBRUQsT0FBTyxDQUFDLEdBQVc7UUFDZixPQUFPLElBQUksT0FBTyxDQUFDLENBQUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxFQUFFLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxHQUFHLEVBQUUsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLEdBQVUsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNySCxDQUFDO0lBRUQsS0FBSyxDQUFDLEdBQVc7UUFDYixPQUFPLElBQUksT0FBTyxDQUFDLENBQUMsT0FBTyxFQUFFLE1BQU0sRUFBRSxFQUFFLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLEdBQVUsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUNuSCxDQUFDO0lBRUQsUUFBUTtRQUNKLE9BQU8sSUFBSSxPQUFPLENBQUMsQ0FBQyxPQUFPLEVBQUUsTUFBTSxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSxFQUFFLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxHQUFVLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDakgsQ0FBQztJQUdELGlEQUFpRDtJQUNqRCxxREFBcUQ7SUFDckQsaURBQWlEO0lBQ2pELHVEQUF1RDtJQUV2RDs7OztPQUlHO0lBQ0gsTUFBTTtRQUNGLElBQUksR0FBRyxHQUFHLEVBQUUsQ0FBQztRQUNiLEtBQUssQ0FBQyxJQUFJLEVBQUUsR0FBRyxFQUFFLHVCQUF1QixDQUFDLENBQUM7UUFDMUMsT0FBTyxHQUFHLENBQUM7SUFDZixDQUFDO0lBRUQ7O09BRUc7SUFDSCxJQUFJLFFBQVE7UUFDUixPQUFPLElBQUksQ0FBQyxXQUFXLENBQUMsUUFBUSxDQUFDO0lBQ3JDLENBQUM7SUFFRDs7T0FFRztJQUNILElBQUksYUFBYTtRQUNiLE9BQU8sSUFBSSxDQUFDLFdBQVcsQ0FBQyxhQUFhLENBQUM7SUFDMUMsQ0FBQztDQUNKO0FBeEdELDBDQXdHQztBQUVELElBQUksS0FBSyxHQUFHLFVBQVUsR0FBUSxFQUFFLElBQVMsRUFBRSxRQUFhO0lBQ3BELElBQUksQ0FBQyxDQUFDO0lBQ04sS0FBSyxJQUFJLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxHQUFHLFFBQVEsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEVBQUUsRUFBRTtRQUM3QyxDQUFDLEdBQUcsUUFBUSxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ2hCLElBQUksQ0FBQyxDQUFDLENBQUMsR0FBRyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUM7S0FDcEI7QUFDTCxDQUFDLENBQUM7QUFFRixJQUFJLE1BQU0sR0FBRyxVQUFVLEdBQVE7SUFDM0IsSUFBSSxHQUFHLEdBQVEsRUFBRSxDQUFDO0lBQ2xCLEtBQUssSUFBSSxDQUFDLElBQUksR0FBRyxFQUFFO1FBQ2YsR0FBRyxDQUFDLENBQUMsQ0FBQyxHQUFHLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQztLQUNuQjtJQUNELE9BQU8sR0FBRyxDQUFDO0FBQ2YsQ0FBQyxDQUFDIn0=