UNPKG

web-dev-server

Version:

Node.js simple http server for common development or training purposes.

425 lines 16.9 kB
Object.defineProperty(exports, "__esModule", { value: true }); exports.Session = void 0; var tslib_1 = require("tslib"); var crypto_1 = require("crypto"); var Namespace_1 = require("./Sessions/Namespace"); var Session = /** @class */ (function () { function Session(id, locked) { if (locked === void 0) { locked = true; } this.id = id; this.locked = locked; this.lastAccessTime = +new Date; this.namespacesHoops = new Map(); this.namespacesExpirations = new Map(); this.namespaces = new Map(); } /** * @summary Set max waiting time in seconds to unlock session for another request. * @param maxLockWaitTime */ Session.SetMaxLockWaitTime = function (maxLockWaitTime) { this.maxLockWaitTime = maxLockWaitTime * 1000; return this; }; /** * @summary Get max waiting time in seconds to unlock session for another request. */ Session.GetMaxLockWaitTime = function () { return this.maxLockWaitTime; }; /** * @summary Set used cookie name to identify user session. * @param cookieName */ Session.SetCookieName = function (cookieName) { this.cookieName = cookieName; return this; }; /** * @summary Get used cookie name to identify user session. */ Session.GetCookieName = function () { return this.cookieName; }; /** * @summary Set max. lifetime for all sessions and it's namespaces. * `0` means unlimited, 30 days by default. * @param maxLifeTimeSeconds */ Session.SetMaxLifeTime = function (maxLifeTimeSeconds) { this.maxLifeTimeMiliSeconds = maxLifeTimeSeconds === 0 ? 0 : maxLifeTimeSeconds * 1000; return this; }; /** * @summary Get max. lifetime for all sessions and it's namespaces in seconds. */ Session.GetMaxLifeTime = function () { if (this.maxLifeTimeMiliSeconds === 0) return 0; return Math.round(this.maxLifeTimeMiliSeconds / 1000); }; /** * Destroy all running sessions. */ Session.DestroyAll = function () { this.store.forEach(function (session) { return session.Destroy(); }); this.store = new Map(); return this; }; /** * @summary Set custom session load handler. * Implement any functionality to assign session instance under it's id into given store. * @param loadHandler */ Session.SetLoadHandler = function (loadHandler) { this.loadHandler = loadHandler; return this; }; /** * @summary Set custom session write handler. * Implement any functionality to store session instance under it's id from given store anywhere else. * @param writeHandler */ Session.SetWriteHandler = function (writeHandler) { this.writeHandler = writeHandler; return this; }; /** * Start session based on cookies and data stored in current process. * @param request * @param response */ Session.Start = function (request, response) { if (response === void 0) { response = null; } return tslib_1.__awaiter(this, void 0, void 0, function () { var session, id; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: id = this.getRequestIdOrNew(request); if (!(!this.store.has(id) && this.loadHandler != null)) return [3 /*break*/, 2]; return [4 /*yield*/, this.loadHandler(id, this.store, false)]; case 1: _a.sent(); _a.label = 2; case 2: if (this.store.has(id)) { session = this.store.get(id); if (session != null && this.maxLifeTimeMiliSeconds !== 0 && session.lastAccessTime + this.maxLifeTimeMiliSeconds < (+new Date)) { session = new Session(id, false); this.store.set(id, session); } } else { session = new Session(id, false); this.store.set(id, session); } if (!(session == null || (session && session.IsLocked()))) return [3 /*break*/, 4]; return [4 /*yield*/, this.waitToUnlock(id)]; case 3: session = _a.sent(); _a.label = 4; case 4: session.init(); if (response) this.setUpResponse(session, response); this.runGarbageCollectingIfNecessary(); return [2 /*return*/, session]; } }); }); }; /** * @summary Check if any session data exists for given request. * @param request */ Session.Exists = function (request) { return tslib_1.__awaiter(this, void 0, void 0, function () { var id; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: id = request.GetCookie(this.cookieName, "a-zA-Z0-9"); if (this.store.has(id)) return [2 /*return*/, true]; if (!this.loadHandler) return [3 /*break*/, 2]; return [4 /*yield*/, this.loadHandler(id, this.store, true)]; case 1: _a.sent(); _a.label = 2; case 2: return [2 /*return*/, this.store.has(id) && this.store.get(id) != null]; } }); }); }; /** * @summary Get session object by session id or `null`. * Returned session could be already locked by another request. * @param sessionId */ Session.Get = function (sessionId) { return tslib_1.__awaiter(this, void 0, void 0, function () { return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: if (!(!this.store.has(sessionId) && this.loadHandler)) return [3 /*break*/, 2]; return [4 /*yield*/, this.loadHandler(sessionId, this.store, false)]; case 1: _a.sent(); _a.label = 2; case 2: if (this.store.has(sessionId)) return [2 /*return*/, this.store.get(sessionId)]; return [2 /*return*/, null]; } }); }); }; /** * @summary Set session object with session id and optional data or lock * into global store. If there is configured any write handler, then the * handler is invoked for this session id. * @param session */ Session.Set = function (session) { return tslib_1.__awaiter(this, void 0, void 0, function () { var sessionId; return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: sessionId = session.GetId(); this.store.set(sessionId, session); if (!(this.writeHandler != null)) return [3 /*break*/, 2]; return [4 /*yield*/, this.writeHandler(sessionId, this.store)]; case 1: _a.sent(); _a.label = 2; case 2: return [2 /*return*/, this]; } }); }); }; Session.setUpResponse = function (session, response) { var _this = this; session.lastAccessTime = +new Date; var expireDate = null; if (this.maxLifeTimeMiliSeconds !== 0) { expireDate = new Date(); expireDate.setTime(session.lastAccessTime + this.maxLifeTimeMiliSeconds); } response.On("session-unlock", function () { return tslib_1.__awaiter(_this, void 0, void 0, function () { return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: session.lastAccessTime = +new Date; session.locked = false; if (!(this.writeHandler != null)) return [3 /*break*/, 2]; return [4 /*yield*/, this.writeHandler(session.GetId(), this.store)]; case 1: _a.sent(); _a.label = 2; case 2: return [2 /*return*/]; } }); }); }); response.SetCookie({ name: this.cookieName, value: session.GetId(), expires: expireDate, path: '/', httpOnly: true }); }; Session.getRequestIdOrNew = function (request) { var id = request.GetCookie(this.cookieName, "a-zA-Z0-9"); if (id == null) { while (true) { id = crypto_1.randomBytes(20).toString('hex').toLowerCase(); if (!this.store.has(id)) break; } } else { id = id.toLowerCase(); } return id; }; Session.runGarbageCollectingIfNecessary = function () { var _this = this; if (this.garbageCollecting !== null) return; this.garbageCollecting = setInterval(function () { if (_this.maxLifeTimeMiliSeconds === 0) return; var nowTime = +new Date; _this.store.forEach(function (session) { if (session.lastAccessTime + _this.maxLifeTimeMiliSeconds < nowTime) session.Destroy(); }); }, this.GC_INTERVAL); }; Session.waitToUnlock = function (id) { return tslib_1.__awaiter(this, void 0, void 0, function () { var session, maxWaitingTime, startTime, timeoutHandler; var _this = this; return tslib_1.__generator(this, function (_a) { session = this.store.get(id); if (session && !session.locked) return [2 /*return*/, session]; maxWaitingTime = Session.maxLockWaitTime; startTime = +new Date; timeoutHandler = function (resolve) { var session = _this.store.get(id); if (session && !session.locked) { session.locked = true; return resolve(session); } var nowTime = +new Date; if (startTime + maxWaitingTime < nowTime) return resolve(session); setTimeout(function () { timeoutHandler(resolve); }, _this.LOCK_CHECK_INTERVAL); }; return [2 /*return*/, new Promise(function (resolve, reject) { setTimeout(function () { timeoutHandler(resolve); }, _this.LOCK_CHECK_INTERVAL); })]; }); }); }; /** * @summary Get session id string. */ Session.prototype.GetId = function () { return this.id; }; /** * @summary Get if session is locked. */ Session.prototype.IsLocked = function () { return this.locked; }; /** * @summary Lock current session and prevent other request to using it. * Session is locked automaticly on session start. Use this method very carefully. */ Session.prototype.Lock = function () { this.locked = true; return this; }; /** * @summary Unlock current session and allow other request to using it. * Session is unlocked automaticly on response send. Use this method very carefully. */ Session.prototype.Unlock = function () { this.locked = false; return this; }; /** * @summary Wait until this session is unlocked by another request end. */ Session.prototype.WaitToUnlock = function () { return tslib_1.__awaiter(this, void 0, void 0, function () { return tslib_1.__generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, Session.waitToUnlock(this.id)]; case 1: _a.sent(); return [2 /*return*/, this]; } }); }); }; /** * @summary Get new or existing session namespace instance. * @param name Session namespace unique name. */ Session.prototype.GetNamespace = function (name) { if (name === void 0) { name = 'default'; } var result; if (this.namespaces.has(name)) { result = this.namespaces.get(name); } else { result = Namespace_1.createNamespace(name, this); this.namespaces.set(name, result); } return result; }; /** * @summary Destroy all namespaces and this session for current user. */ Session.prototype.Destroy = function () { Session.store.delete(this.id); this.id = null; this.locked = null; this.lastAccessTime = null; this.namespacesExpirations = null; this.namespaces = null; }; Session.prototype.setLastAccessTime = function (lastAccessTime) { this.lastAccessTime; return this; }; Session.prototype.destroyNamespace = function (name) { if (this.namespaces.has(name)) this.namespaces.delete(name); return this; }; Session.prototype.setNamespaceExpirationHoops = function (name, hoopsCount) { this.namespacesHoops.set(name, hoopsCount); return this; }; Session.prototype.setNamespaceExpirationTime = function (name, seconds) { var maxLifeTimeSeconds = Session.GetMaxLifeTime(); if (seconds > maxLifeTimeSeconds && maxLifeTimeSeconds > 0) seconds = maxLifeTimeSeconds; var expDate = new Date(); expDate.setTime(expDate.getTime() + (seconds * 1000)); this.namespacesExpirations.set(name, expDate.getTime()); return this; }; Session.prototype.init = function () { var _this = this; this.locked = true; var nowTime = (new Date()).getTime(); this.namespacesHoops.forEach(function (hoopsCount, name) { _this.namespacesHoops.set(name, hoopsCount - 1); }); var namesToUnset = new Map(); this.namespaces.forEach(function (namespace, name) { if (_this.namespacesHoops.has(name) && _this.namespacesHoops.get(name) < 0) namesToUnset.set(name, true); if (_this.namespacesExpirations.has(name) && _this.namespacesExpirations.get(name) < nowTime) namesToUnset.set(name, true); }); namesToUnset.forEach(function (bool, name) { if (_this.namespacesHoops.has(name)) _this.namespacesHoops.delete(name); if (_this.namespacesExpirations.has(name)) _this.namespacesExpirations.delete(name); _this.namespaces.delete(name); }); }; Session.LIFETIMES = { MINUTE: 60, HOUR: 3600, DAY: 86400, WEEK: 604800, MONTH: 2592000, YEAR: 31557600 }; Session.GC_INTERVAL = 60 * 60 * 1000; // once per hour Session.LOCK_CHECK_INTERVAL = 100; Session.store = new Map(); Session.maxLockWaitTime = 30 * 1000; // 30 seconds Session.cookieName = 'sessionid'; Session.maxLifeTimeMiliSeconds = 30 * 24 * 60 * 60 * 1000; Session.garbageCollecting = null; Session.loadHandler = null; Session.writeHandler = null; return Session; }()); exports.Session = Session; //# sourceMappingURL=Session.js.map