dl
Version:
DreamLab Libs
571 lines (507 loc) • 17.8 kB
JavaScript
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;