Json-rpc.js
Материал из support.qbpro.ru
Версия от 15:40, 4 августа 2013; imported>Supportadmin (Новая страница: «оригинал https://gist.github.com/869907 ==КОД== <nowiki>var sys = require('sys'); var http = require('http'); //===----------------------------------…»)
оригинал https://gist.github.com/869907
КОД
var sys = require('sys');
var http = require('http');
//===----------------------------------------------------------------------===//
// Server Client
//===----------------------------------------------------------------------===//
var Client = function(port, host, user, password) {
this.port = port;
this.host = host;
this.user = user;
this.password = password;
this.call = function(method, params, callback, errback, path) {
var client = http.createClient(port, host);
// First we encode the request into JSON
var requestJSON = JSON.stringify({
'jsonrpc': '2.0',
'id': '' + (new Date()).getTime(),
'method': method,
'params': params
});
var headers = {};
if (user && password) {
var buff = new Buffer(this.user + ":" + this.password)
.toString('base64');
var auth = 'Basic ' + buff;
headers['Authorization'] = auth;
}
// Then we build some basic headers.
headers['Host'] = host;
headers['Content-Length'] = requestJSON.length;
// Now we'll make a request to the server
var request = client.request('POST', path || '/', headers);
request.write(requestJSON);
request.on('response', function(response) {
// We need to buffer the response chunks in a nonblocking way.
var buffer = '';
response.on('data', function(chunk) {
buffer = buffer + chunk;
});
// When all the responses are finished, we decode the JSON and
// depending on whether it's got a result or an error, we call
// emitSuccess or emitError on the promise.
response.on('end', function() {
var decoded = JSON.parse(buffer); // TODO: Check for invalid response from server
if(decoded.hasOwnProperty('result')) {
if (callback)
callback(null, decoded.result);
} else {
// Call error handler if it is set, otherwise call callback with error parameters
if (errback) {
errback(decoded.error);
} else if(callback) {
callback(decoded.error, null);
}
}
});
});
};
}
//===----------------------------------------------------------------------===//
// Server
//===----------------------------------------------------------------------===//
function Server() {
var self = this;
this.functions = {};
this.scopes = {};
this.defaultScope = this;
this.server = http.createServer(function(req, res) {
Server.trace('<--', 'accepted request');
if(req.method === 'POST') {
self.handlePOST(req, res);
}
else {
Server.handleNonPOST(req, res);
}
});
}
//===----------------------------------------------------------------------===//
// exposeModule
//===----------------------------------------------------------------------===//
Server.prototype.exposeModule = function(mod, object, scope) {
var funcs = [];
for(var funcName in object) {
var funcObj = object[funcName];
if(typeof(funcObj) == 'function') {
this.functions[mod + '.' + funcName] = funcObj;
funcs.push(funcName);
if (scope) {
this.scopes[mod + '.' + funcName] = scope;
}
}
}
Server.trace('***', 'exposing module: ' + mod + ' [funs: ' + funcs.join(', ')
+ ']');
return object;
}
//===----------------------------------------------------------------------===//
// expose
//===----------------------------------------------------------------------===//
Server.prototype.expose = function(name, func, scope) {
Server.trace('***', 'exposing: ' + name);
this.functions[name] = func;
if (scope) {
this.scopes[name] = scope;
}
}
//===----------------------------------------------------------------------===//
// trace
//===----------------------------------------------------------------------===//
Server.trace = function(direction, message) {
sys.puts(' ' + direction + ' ' + message);
}
//===----------------------------------------------------------------------===//
// listen
//===----------------------------------------------------------------------===//
Server.prototype.listen = function(port, host) {
this.server.listen(port, host);
Server.trace('***', 'Server listening on http://' + (host || '127.0.0.1') +
':' + port + '/');
}
//===----------------------------------------------------------------------===//
// handlePOST разбор запроса
//===----------------------------------------------------------------------===//
Server.prototype.handlePOST = function(req, res) {
var buffer = '';
var self = this;
var handle = function (buf) {
//первоначальный разбор JSON тела запроса, узнать вообще JSON это или нет
var decoded = "";
try {
decoded = JSON.parse(buf);
} catch (e) {
return Server.handleError(-32700, "Parse Error", null, req, res);
}
// Check for the required fields, and if they aren't there, then
// dispatch to the handleError function.
//Проверка наличия полей запроса (метод, параметры, id задания)
if(!(decoded.method && decoded.params && decoded.id)) {
if (typeof(id) == "undefined") {
var id = null;
}
return Server.handleError(-32600, "Invalid Request", decoded.id, req, res);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if(!self.functions.hasOwnProperty(decoded.method)) {
return Server.handleError(-32601, "Method not found", decoded.id, req, res);
}
// Build our success handler
var onSuccess = function(funcResp) {
Server.trace('-->', 'response (id ' + decoded.id + '): ' +
JSON.stringify(funcResp));
var encoded = JSON.stringify({
'jsonrpc': '2.0',
'result': funcResp,
'error': null,
'id': decoded.id
});
res.writeHead(200, {'Content-Type': 'application/json',
'Content-Length': encoded.length});
res.write(encoded);
res.end();
};
Server.trace('<--', 'request (id ' + decoded.id + '): ' +
decoded.method + '(' + decoded.params.join(', ') + ')');
// Try to call the method, but intercept errors and call our
// onFailure handler.
var method = self.functions[decoded.method];
var callback = function(result, errormessage) {
if (errormessage) {
Server.handleError(-32602, errormessage, decoded.id, req, res);
} else {
onSuccess(result);
}
};
var scope = self.scopes[decoded.method] || self.defaultScope; //новая область видимости для вызываемого метода
// Other various information we want to pass in for the handler to be
// able to access.
var opt = {
req: req,
server: self
};
try {
method.call(scope, decoded.params, opt, callback);
} catch (err) {
return Server.handleError(-32603, err, decoded.id, req, res);
}
} // function handle(buf)
req.addListener('data', function(chunk) {
buffer = buffer + chunk;
});
req.addListener('end', function() {
handle(buffer);
});
}
//===----------------------------------------------------------------------===//
// handleError
//===----------------------------------------------------------------------===//
Server.handleError = function(code, message, id, req, res) {
var encoded = JSON.stringify({
'jsonrpc': '2.0',
'error': {
'code':code,
'message':message
},
'id': id
});
res.writeHead(400, {'Content-Type': 'text/plain',
'Content-Length': encoded.length,
'Allow': 'POST'});
res.write(encoded);
res.end();
Server.trace('-->', 'Failure: ' + code + ': ' + message);
}
//===----------------------------------------------------------------------===//
// handleNonPOST
//===----------------------------------------------------------------------===//
Server.handleNonPOST = function(req, res) {
var encoded = JSON.stringify({
'jsonrpc': '2.0',
'error': {
'code':-32600,
'message':"Only POST is allowed."
},
'id': null
});
res.writeHead(405, {'Content-Type': 'text/plain',
'Content-Length': encoded.length,
'Allow': 'POST'});
res.write(encoded);
res.end();
}
module.exports.Server = Server;
module.exports.Client = Client;
Модуль
function jsonrps(buf) {
var trigger_error=0;
this.functions = {}; //объект, содержащий все доступные функции, здесь надо читать все методы из БД в functions
this.scopes = {}; //объект, содержащий все новые области видимости для вызываемых методов
this.defaultScope = this; //новые область видимости по умолчинию //если нет области видимости в методе - надо сделать другой
function error(code, message, id){
var encoded = JSON.stringify({
'jsonrpc': '2.0',
'error': {
'code':code,
'message':message
},
'id': id
});
return encoded;
}
//первая проверка: валидность JSON вообще
//если JSON не валидный генерируем ошибку -32700 и прекращаем функцию
//если JSON валидный дальше обрабатываем переменную is_json
var is_json = "";
try {
is_json = JSON.parse(buf);
}
catch (e) {
trigger_error=-32700;
return this.error(-32700, "Parse Error", null);
}
//вторая проверка: формат запроса по стандарту
//Наличие полей запроса (метод, параметры) id задания может отсутствовать в случае уведомления
//если id отсутствует, подставляем null всесто отсутствующего id
//При отсутствии полей запроса (метод, параметры) генерируем ошибку -32600
if(!(is_json.method && is_json.params && is_json.id)) {
if (typeof(id) == "undefined") {
var id = null;
}
return this.error(-32600, "Invalid Request", is_json.id);
}
//третья проверка на наличие в functions указанного в запросе метода
//в случае ошибки генерируем ошибку -32601.
if(!self.functions.hasOwnProperty(decoded.method)) {
return Server.handleError(-32601, "Method not found", decoded.id, req, res);
}
