web-dev-server
Version:
Node.js simple http server for common development or training purposes.
425 lines • 16.9 kB
JavaScript
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