UNPKG

core

Version:

Core Libs

367 lines (325 loc) 11.2 kB
/** * @overview JsonRpcRequestProcessor * @copyright Dreamlab Onet.pl sp. z. o o 2012 */ var RequestProcessor = require('../http/RequestProcessor.js').RequestProcessor; var JsonRpcAbstract = require('../jsonrpc/JsonRpcAbstract.js').JsonRpcAbstract; var JsonRpcResponse = require('../jsonrpc/JsonRpcResponse.js').JsonRpcResponse; var JsonRpcRequest = require('../jsonrpc/JsonRpcRequest.js').JsonRpcRequest; var JsonRpcMethod = require('../jsonrpc/JsonRpcMethod.js').JsonRpcMethod; var Types = require('../common/Types.js').Types; var BinaryData = require('../data/BinaryData.js').BinaryData; /** * @class JsonRpcRequestProcessor * @classdesc JsonRpcRequestProcessor * @extends RequestProcessor * * @requires RequestProcessor * @requires JsonRpcAbstract * @requires JsonRpcResponse * @requires JsonRpcMethod * @requires Types * @requires BinaryData * * @param {Server} server * @returns {JsonRpcRequestProcessor} */ var JsonRpcRequestProcessor = exports.JsonRpcRequestProcessor = function (server) { this._request = null; this._response = null; this._responseData = []; this._isBatchRequest = false; this._methodCount = 0; RequestProcessor.call(this, server); }; JsonRpcRequestProcessor.prototype = Object.create(RequestProcessor.prototype); /** * Processes request and generates response * @param {Request} request * @param {Response} response * @returns {undefined} * @method */ JsonRpcRequestProcessor.prototype.process = function (request, response) { RequestProcessor.prototype.process.call(this, request, response); this._request = request; this._response = response; var jsonRpcRequests = this._getRequest(); var methods = []; this._methodCount = jsonRpcRequests.length; for (var i = 0, tmpMethodCount = jsonRpcRequests.length; i < tmpMethodCount; i++ ) { this._processSingle(jsonRpcRequests[i]); try { methods.push(jsonRpcRequests[i].getMethod()); } catch (e) { console.error('JsonRpcRequestProcessor/process: error:', e); } } //XXX: dziala jesli jest process.nextTick this._requestLine = '"' + this._request.getMethod() + ' ' + this._request.getPath() + ' method:' + methods.join(':') + ' ' + this._request.getHttpVersion() + '"'; }; /** * Returns Method object for specified request * * @param {JsonRpcRequest} request * @returns {JsonRpcMethod} * @method * @private */ JsonRpcRequestProcessor.prototype._getMethodClass = function (requestedMethod) { return this.server.getMethod(requestedMethod, this._request); }; /** * Return reposne to caller * @private */ JsonRpcRequestProcessor.prototype._prepareResponse = function () { var tmp_rep = null; var that = this; if (--this._methodCount <= 0) { // jezeli ktos ustawil explicite statusCode lub body do zwrocenia // to znaczy ze chce nadpisac cala odpowiedz jsonRpc // i bierze na siebie odpowiedzialnosc poskladania poprawnie calego response // jedyne co wtedy robimy to odsylamy odpowiedz w nastepnym ticku // jezeli nic nie zmieniono: wartosciami sa getStatusCode()==null i getBody()==null if (!this._response.getStatusCode() || !this._response.getBody()) { this._response.setStatusCode(200); this._response.setHeaders({ 'Content-Type': 'application/json; charset=utf8', 'Access-Control-Allow-Origin': '*' }, true); var responseData; if (this._isBatchRequest === true) { responseData = this._responseData; } else { responseData = this._responseData.pop(); } this._response.setBody(new BinaryData( this._getBody(JSON.stringify(responseData)), BinaryData.Encoding.TEXT, BinaryData.CharacterEncoding.UTF8)); } process.nextTick(function() { that._response.send(); }); } }; /** * Processes single request and generates response * @param {Request} request * @param {Response} response * @param {Number} index * @returns {undefined} * @method */ JsonRpcRequestProcessor.prototype._processSingle = function(jsonRpcRequest) { var method; var methodClass; var requestedMethod; var requestId = jsonRpcRequest.getId(); var that = this; try { requestedMethod = jsonRpcRequest.getMethod(); methodClass = this._getMethodClass(requestedMethod); } catch (ex) { console.error('_processSingle error', ex); this._throwError(JsonRpcRequestProcessor.Errors.INVALID_METHOD, ex, requestId); } if (!methodClass) { console.warn('JsonRpcRequestProcessor/process/methodNotFound', requestedMethod); var e = new Error(JsonRpcRequestProcessor.Errors.METHOD_NOT_FOUND); this._throwError(JsonRpcRequestProcessor.Errors.METHOD_NOT_FOUND, e, requestId); return; } method = new methodClass(this._request, this._response, this.server); if (!Types.isInstanceOf(method, JsonRpcMethod)) { var error = new Error(JsonRpcRequestProcessor.Errors.METHOD_NOT_FOUND); console.error('JsonRpcRequestProcessor/process/methodClassError/' + requestedMethod); this._throwError(JsonRpcRequestProcessor.Errors.METHOD_NOT_FOUND, error, requestId); return; } //FIXME: tutaj trzebaby sie zastanowic jak to zrobic bo raz przekazujemy Exception a raz ErrorEvent method.addEventListener(JsonRpcMethod.Event.WRONG_PARAMS, function (e) { that._handleError(e, requestId); }, this); method.addEventListener(JsonRpcMethod.Event.ERROR, function(e) { that._handleError(e, requestId); }, this); method.addEventListener(JsonRpcMethod.Event.OK, function(e) { that._success(e, requestId); }, this); try { method.execute(jsonRpcRequest.getParams()); } catch (e) { console.error('JsonRpcRequestProcessor/execute/uncaughtException/method/' + requestedMethod, e, e.stack); this._throwError(JsonRpcRequestProcessor.Errors.INTERNAL_ERROR, e, requestId); } }; /** * Returns jsonRpc response to the caller * @param {Event} e * @returns {undefined} * @method * @private */ JsonRpcRequestProcessor.prototype._success = function(e, requestId) { var data; if (e.data) { data = e.data; if (!Types.isInstanceOf(e.data, JsonRpcResponse)) { data = new JsonRpcResponse(); data.setResult(e.data); } data.setId(requestId); this._responseData.push(data.toJson()); } return this._prepareResponse(); }; /** * Handles errors from executed JsonRpc Method * @param {Event} e * @returns {undefined} * @method * @private */ JsonRpcRequestProcessor.prototype._handleError = function (e, requestId) { if (e.type == JsonRpcMethod.Event.WRONG_PARAMS) { this._throwError(JsonRpcRequestProcessor.Errors.INVALID_PARAMS, e, requestId); } else if (e.type == JsonRpcMethod.Event.ERROR) { this._throwError(e.code || -1, e, requestId); } else { this._throwError(JsonRpcRequestProcessor.Errors.INTERNAL_ERROR, e, requestId); } }; /** * Retrieves jsonrpc request from request body * @returns [JsonRpcRequest] * @method * @private */ JsonRpcRequestProcessor.prototype._getRequest = function () { //tutaj mozna dodac pewnie jakas walidacje czy request jest POSTem var raw_requests; var requests = []; try { raw_requests = JSON.parse(this._request.getBody().toBuffer().toString()); } catch (e) { var ex = new Error(JsonRpcAbstract.Exceptions.PARSE_ERROR); console.error('JsonRpcRequestProcessor/process/createJsonRpcRequest', e); this._throwError(JsonRpcRequestProcessor.Errors.PARSE_ERROR, ex, 0); return requests; } if (Types.isArray(raw_requests) === true) { this._isBatchRequest = true; } else { this._isBatchRequest = false; raw_requests = [raw_requests]; } for (var i = 0, raw_lenght = raw_requests.length; i < raw_lenght; i++) { try{ requests.push(new JsonRpcRequest(raw_requests[i])); } catch(e) { console.error('JsonRpcRequestProcessor/process/createBathJsonRpcRequest', e); if (e.message == JsonRpcAbstract.Exceptions.INPROPER_JSON) { this._throwError(JsonRpcRequestProcessor.Errors.PARSE_ERROR, e); } else if (e.message == JsonRpcAbstract.Exceptions.INVALID_JSON_RPC) { this._throwError(JsonRpcRequestProcessor.Errors.INVALID_REQUEST, e); } else { this._throwError(JsonRpcRequestProcessor.Errors.INTERNAL_ERROR, e); } } } return requests; }; /** * Retrieves body from given JsonRpcResponse * @method * @private * @param {JsonRpcResponse} response * @return {String} */ JsonRpcRequestProcessor.prototype._getBody = function (response) { return response.toString(); }; /** * Returns jsonRpc error to the caller * @param {Number|ErrorEvent} code * @param {Exception|ErrorEvent} exception * @param {Number} id * @returns {undefined} * @method * @private */ JsonRpcRequestProcessor.prototype._throwError = function (code, exception, id) { var errorCode = null; var errorMsg = null; var loggingFunc = 'warn'; var jsonRpcResponse = new JsonRpcResponse(); jsonRpcResponse.setId(id ? id : "undefined"); if (Types.isObject(code)) { errorCode = code.code; errorMsg = code.message; } else { errorCode = code; errorMsg = exception.message; } // Error codes range pre-defined for JSON-RPC if (errorCode >= -32768 && errorCode <= -32000) { loggingFunc = 'error'; } console[loggingFunc]('JsonRpcRequestProcessor/_throwError/ code: ' + errorCode + " msg: " + errorMsg); jsonRpcResponse.setError(parseInt(errorCode, 10), errorMsg || "", exception.data ? exception.data : null); this._responseData.push(jsonRpcResponse.toJson()); return this._prepareResponse(); }; /** * @static * @constant * @namespace */ JsonRpcRequestProcessor.Errors = {}; /** * @static * @constant * @type {String} * @default */ JsonRpcRequestProcessor.Errors.PARSE_ERROR = { code: -32700, message: 'Parse error' }; /** * @static * @constant * @type {Object} * @default */ JsonRpcRequestProcessor.Errors.INVALID_REQUEST = { code: -32600, message: 'Invalid Request' }; /** * @static * @constant * @type {Object} * @default */ JsonRpcRequestProcessor.Errors.METHOD_NOT_FOUND = { code: -32601, message: 'Method not found' }; /** * @static * @constant * @type {Object} * @default */ JsonRpcRequestProcessor.Errors.INVALID_PARAMS = { code: -32602, message: 'Invalid params' }; /** * @static * @constant * @type {Object} * @default */ JsonRpcRequestProcessor.Errors.INTERNAL_ERROR = { code: -32603, message: 'Internal error' }; /** * @static * @constant * @type {Object} * @default */ JsonRpcRequestProcessor.Errors.INVALID_METHOD = { code: -32604, message: 'Invalid method' };