UNPKG

dl

Version:

DreamLab Libs

571 lines (507 loc) 17.8 kB
var core = require('core'), Event = core.event.Event, ErrorEvent = core.event.ErrorEvent, EventDispatcher = core.event.EventDispatcher, Loader = core.http.Loader, JsonRpcResponse = core.jsonrpc.JsonRpcResponse, OpalRequest = require('../opal/OpalRequest.js').OpalRequest, OpalLoader = require('../opal/OpalLoader.js').OpalLoader; /** * Application session API * * @param {core.http.Request} request HTTP request * @param {core.http.Response} response HTTP response * @param {Object} [cookieParams] Session cookie parameters * - {String} [name="onet_nsess"]: cookie name; * - {Number} [lifetime=0]: lifetime of the cookie, defined in seconds; * - {String} [path="/"]: path on the domain where the cookie will work; * - {String} [domain]: cookie domain; * - {Boolean} [secure=false]: if true cookie will only be sent over secure connections; * - {Boolean} [httponly=true]: if set to true then it will be sent the httponly flag when setting the cookie. * @constructor */ var NsessManager = function (request, response, cookieParams) { EventDispatcher.call(this); this._request = request; this._response = response; this._cookieParams = { name: 'onet_nsess', lifetime: 0, path: '/', domain: null, secure: false, httponly: true }; if (cookieParams) { Object.keys(this._cookieParams).forEach(function (name) { if (cookieParams.hasOwnProperty(name)) { this._cookieParams[name] = cookieParams[name]; } }, this); } this._sessionId = request.getCookie(this._cookieParams.name); this._data = null; this._opening = false; this._opened = false; this._closing = false; this._closed = false; this._modified = false; this._queue = []; }; NsessManager.prototype = Object.create(EventDispatcher.prototype); /** * Gets session data * This method reads data from storage if it is necessary. * * @param {Function} callback Callback function * - {core.event.ErrorEvent|null} err: error; * - {Object|null} data: session data. * @type NsessManager */ NsessManager.prototype.get = function (callback) { if (this._opened || this._closed) { callback(null, this._data); } else { this._queue.push(callback); this.read(); } return this; }; /** * Sets session data * This method doesn't write anything to storage. * You mustn't call this method after NsessManager#write nor NsessManager#destroy. * * @param {Object} data New session data * @type NsessManager * @throws {Error} NsessManager.Exception.CLOSED */ NsessManager.prototype.set = function (data) { if (this._closing || this._closed) { throw new Error(NsessManager.Exception.CLOSED); } this._data = typeof data == 'undefined' ? null : data; this._modified = true; this._opened = true; this._execute(null); return this; }; /** * Reads data from storage * You don't need to call this method - just use NsessManager#get. * You mustn't call this method after NsessManager#write nor NsessManager#destroy. * * @type NsessManager * @throws {Error} NsessManager.Exception.OPENED * @throws {Error} NsessManager.Exception.CLOSED */ NsessManager.prototype.read = function () { var loader, data; if (this._opened) { throw new Error(NsessManager.Exception.OPENED); } if (this._closed) { throw new Error(NsessManager.Exception.CLOSED); } if (!this._opening) { if (this._sessionId) { this._opening = true; loader = new OpalLoader(new OpalRequest({ url: NsessManager.OPAL, method: 'get', params: { key: this._sessionId } })).setTimeout(NsessManager.TIMEOUT); loader.addEventListener(OpalLoader.Event.JSON_RESPONSE, function (e) { var body = e.data.getBody(), result, event; this._opening = false; if (body.isError()) { event = this._handleError(body.getError(), NsessManager.Event.READ_ERROR, NsessManager.Event.READ_OK); if (event) { this._execute(event); } } else { this._opened = true; result = body.getResult(); if (result.key && result.key != this._sessionId) { this._sessionId = result.key; this._setCookie(); } if (!this._modified) { this._data = result.value; } this.dispatchEvent(new Event(NsessManager.Event.OK, result.value)); this.dispatchEvent(new Event(NsessManager.Event.READ_OK, result.value)); this._execute(null); } }, this); loader.addEventListener(Loader.Event.ERROR, function (e) { var event; this._opening = false; event = this._handleError(e, NsessManager.Event.READ_ERROR, NsessManager.Event.READ_OK); if (event) { this._execute(event); } }, this); this._addEventListeners(loader); loader.load(); } else { this._opened = true; data = JSON.parse(JSON.stringify(this._data)); this.dispatchEvent(new Event(NsessManager.Event.OK, data)); this.dispatchEvent(new Event(NsessManager.Event.READ_OK, data)); this._execute(null); } } return this; }; /** * Writes data to storage * You must always call this method and do it only once - when all session data modifications will be done. * You mustn't call this method after NsessManager#destroy. * * @type NsessManager * @throws {Error} NsessManager.Exception.CLOSED */ NsessManager.prototype.write = function () { var destroying, request, loader, data; if (this._closed) { throw new Error(NsessManager.Exception.CLOSED); } if (!this._closing) { destroying = this._sessionId && this._cookieParams.lifetime < 0; if (destroying) { request = { method: 'destroy', params: { key: this._sessionId } }; } else if (this._modified) { request = { method: 'replace', params: { value: JSON.parse(JSON.stringify(this._data)) } }; if (this._sessionId) { request.params.key = this._sessionId; } if (this._cookieParams.lifetime > 0) { request.params.ttl = this._cookieParams.lifetime; } } if (request) { this._closing = true; request.url = NsessManager.OPAL; loader = new OpalLoader(new OpalRequest(request)).setTimeout(NsessManager.TIMEOUT); loader.addEventListener(OpalLoader.Event.JSON_RESPONSE, function (e) { var body = e.data.getBody(), result; this._closing = false; if (body.isError()) { if (!this._handleError(body.getError(), NsessManager.Event.WRITE_ERROR, NsessManager.Event.WRITE_OK)) { this._closed = true; } } else { this._closed = true; if (destroying) { this._sessionId = null; this._data = null; this._setCookie(); this.dispatchEvent(new Event(NsessManager.Event.OK, this._data)); this.dispatchEvent(new Event(NsessManager.Event.WRITE_OK, this._data)); } else { result = body.getResult(); if (result.key && result.key != this._sessionId) { this._sessionId = result.key; this._setCookie(); } this.dispatchEvent(new Event(NsessManager.Event.OK, request.params.value)); this.dispatchEvent(new Event(NsessManager.Event.WRITE_OK, request.params.value)); } this._execute(null); } }, this); loader.addEventListener(Loader.Event.ERROR, function (e) { this._closing = false; this._handleError(e, NsessManager.Event.WRITE_ERROR, NsessManager.Event.WRITE_OK); }, this); this._addEventListeners(loader); loader.load(); } else { this._closed = true; data = JSON.parse(JSON.stringify(this._data)); this.dispatchEvent(new Event(NsessManager.Event.OK, data)); this.dispatchEvent(new Event(NsessManager.Event.WRITE_OK, data)); } } return this; }; /** * Destroys data on storage * This method may be called only once and you mustn't call NsessManager#write before. * * @type NsessManager * @throws {Error} NsessManager.Exception.CLOSED */ NsessManager.prototype.destroy = function () { var loader; if (this._closed) { throw new Error(NsessManager.Exception.CLOSED); } if (!this._closing) { if (this._sessionId) { this._closing = true; loader = new OpalLoader(new OpalRequest({ url: NsessManager.OPAL, method: 'destroy', params: { key: this._sessionId } })).setTimeout(NsessManager.TIMEOUT); loader.addEventListener(OpalLoader.Event.JSON_RESPONSE, function (e) { var body = e.data.getBody(); this._closing = false; if (body.isError()) { if (!this._handleError(body.getError(), NsessManager.Event.DESTROY_ERROR, NsessManager.Event.DESTROY_OK)) { this._closed = true; } } else { this._closed = true; this._sessionId = null; this._data = null; this._setCookie(); this.dispatchEvent(new Event(NsessManager.Event.OK, this._data)); this.dispatchEvent(new Event(NsessManager.Event.DESTROY_OK, this._data)); this._execute(null); } }, this); loader.addEventListener(Loader.Event.ERROR, function (e) { this._closing = false; this._handleError(e, NsessManager.Event.DESTROY_ERROR, NsessManager.Event.DESTROY_OK); }, this); this._addEventListeners(loader); loader.load(); } else { this._closed = true; this._data = null; this.dispatchEvent(new Event(NsessManager.Event.OK, this._data)); this.dispatchEvent(new Event(NsessManager.Event.DESTROY_OK, this._data)); } } return this; }; /** * Executes queue * * @param {core.event.ErrorEvent|null} err Error * @type NsessManager * @private */ NsessManager.prototype._execute = function (err) { var callback; while ((callback = this._queue.shift())) { callback(err, err ? null : this._data); } return this; }; /** * Sets session cookie * * @type NsessManager * @private */ NsessManager.prototype._setCookie = function () { var cookie, domain; if (this._sessionId || (!this._sessionId || this._cookieParams.lifetime < 0) && this._request.getCookie(this._cookieParams.name) !== null) { cookie = this._cookieParams.name + '='; if (this._sessionId) { cookie += encodeURIComponent(this._sessionId); if (this._cookieParams.lifetime) { cookie += '; expires=' + new Date(new Date().getTime() + this._cookieParams.lifetime * 1000).toUTCString(); } } else { cookie += '; expires=' + new Date(new Date().getTime() - 1000000).toUTCString(); } if (this._cookieParams.path) { cookie += '; path=' + this._cookieParams.path; } if (this._cookieParams.domain) { cookie += '; domain=' + this._cookieParams.domain; } else if (this._cookieParams.secure || this._cookieParams.httponly) { domain = this._request.getHost() || this._request.getConnectionHost(); if (domain) { cookie += '; domain=' + domain; } } if (this._cookieParams.secure) { cookie += '; secure'; } if (this._cookieParams.httponly) { cookie += '; httponly'; } this._response.headers.setHeader('p3p', 'CP="ALL DSP COR IVD IVA PSD PSA TEL TAI CUS ADM CUR CON SAM OUR IND"', true); this._response.setCookie(cookie); } return this; }; /** * Handles OPAL HTTP response * * @param {dl.opal.OpalLoader} loader OpalLoader instance * @type NsessManager * @private */ NsessManager.prototype._addEventListeners = function (loader) { loader.addEventListener(OpalLoader.Event.HTTP_RESPONSE, function (e) { var json = e.data.getBody().getBody(), body; try { json = json.toUTF8String(); body = new JsonRpcResponse(JSON.parse(json)); } catch (err) { loader.dispatchEvent(new ErrorEvent(Loader.Event.ERROR, {json: json.toString(), error: (err.message || err)}, -4, 'JSON parse error')); return this; } e.data.setBody(body); loader.dispatchEvent(new Event(OpalLoader.Event.JSON_RESPONSE, e.data)); }); return this; }; /** * Handles error response * * @param {core.event.ErrorEvent|core.jsonrpc.JsonRpcError} error Error instance * @param {String} [errorEventType] Type of additional error event * @param {String} [eventType] Type of additional event * @returrns {core.event.ErrorEvent|null} Error event */ NsessManager.prototype._handleError = function (error, errorEventType, eventType) { var referer, event; error = error.toJson(); if (error.code == NsessManager.Error.NO_DATA_FOUND) { this._opened = true; this._sessionId = null; this._data = null; this._setCookie(); this.dispatchEvent(new Event(NsessManager.Event.OK, this._data)); if (eventType) { this.dispatchEvent(new Event(eventType, this._data)); } this._execute(null); return null; } referer = this._request.getHeader('referer'); console.error('NSess: ' + this._sessionId + ' ' + JSON.stringify(error) + (process.env.OPAL_IDENTITY ? ' application=' + process.env.OPAL_IDENTITY + ';' : '') + ' url=' + this._request.getUrl() + (referer ? '; referer=' + referer : '')); event = new ErrorEvent(NsessManager.Event.ERROR, error.data, error.code, error.message); this.dispatchEvent(event); if (errorEventType) { event = new ErrorEvent(errorEventType, error.data, error.code, error.message); this.dispatchEvent(event); } return event; }; NsessManager.Event = {}; /** * Session access on storage has been successful * @static * @constant */ NsessManager.Event.OK = 'NsessManager_OK'; /** * Session access on storage has failed * @static * @constant */ NsessManager.Event.ERROR = 'NsessManager_ERROR'; /** * Session read from storage has been successful * @static * @constant */ NsessManager.Event.READ_OK = 'NsessManager_READ_OK'; /** * Session read from storage has failed * @static * @constant */ NsessManager.Event.READ_ERROR = 'NsessManager_READ_ERROR'; /** * Session write to storage has been successful * @static * @constant */ NsessManager.Event.WRITE_OK = 'NsessManager_WRITE_OK'; /** * Session write to storage has failed * @static * @constant */ NsessManager.Event.WRITE_ERROR = 'NsessManager_WRITE_ERROR'; /** * Session destroy on storage has been successful * @static * @constant */ NsessManager.Event.DESTROY_OK = 'NsessManager_DESTROY_OK'; /** * Session destroy on storage has failed * @static * @constant */ NsessManager.Event.DESTROY_ERROR = 'NsessManager_DESTROY_ERROR'; NsessManager.Exception = {}; /** * Session was already read from storage * @static * @constaant */ NsessManager.Exception.OPENED = 'Session is already opened.'; /** * Session was already written to storage * @static * @constaant */ NsessManager.Exception.CLOSED = 'Session is already closed.'; NsessManager.Error = {}; /** * No data found * @static * @constant */ NsessManager.Error.NO_DATA_FOUND = -300; /** * Session storage * @static * @constant */ NsessManager.OPAL = 'application.sessions.onetapi.pl'; /** * {core.http.Loader} timeout [ms] * @static */ NsessManager.TIMEOUT = 900; exports.NsessManager = NsessManager;