Various updates.

This commit is contained in:
Julien Fontanet 2013-07-08 18:36:53 +02:00
parent 479474d10a
commit d3361a3f1d
5 changed files with 117 additions and 84 deletions

View File

@ -16,50 +16,42 @@ function deprecated(fn)
function Api(xo)
{
if ( !(this instanceof Api) )
{
return new Api(xo);
}
this.xo = xo;
}
Api.prototype.exec = function (session, request, response) {
var method = this.get(request.method);
Api.prototype.exec = function (session, request) {
/* jshint newcap: false */
var method = this.getMethod(request.method);
if (!method)
{
response.sendError(Api.err.INVALID_METHOD);
return;
return Q.reject(Api.err.INVALID_METHOD);
}
try
{
var result = method.call(this.xo, session, request, response); // @todo
var result = method.call(this.xo, session, request);
if (undefined === result)
if (Q.isPromise(result))
{
/* jshint noempty:false */
}
else if (Q.isPromise(result))
{
result.then(
function (result) {
response.sendResult(result);
},
function (error) {
response.sendError(error);
}
).done();
}
else
{
response.sendResult(result);
return result;
}
return Q(result);
}
catch (e)
{
response.sendError(e);
return Q.reject(e);
}
};
Api.prototype.get = function (name) {
Api.prototype.getMethod = function (name) {
/* jshint noempty: false */
var parts = name.split('.');
@ -81,15 +73,13 @@ Api.prototype.get = function (name) {
// It's a (deprecated) alias.
if (_.isString(current))
{
return deprecated(this.get(current));
return deprecated(this.getMethod(current));
}
return undefined;
};
module.exports = function (xo) {
return new Api(xo);
};
module.exports = Api;
//////////////////////////////////////////////////////////////////////
@ -172,6 +162,7 @@ Api.fn.session = {
throw Api.err.INVALID_CREDENTIAL;
}
session.set('user_id', user.get('id'));
return true;
});
},
@ -195,11 +186,6 @@ Api.fn.session = {
throw Api.err.INVALID_CREDENTIAL;
}
// @todo How to disconnect when the token is deleted?
//
// @todo How to not leak the event callback when the
// connection is closed?
session.set('token_id', token.id);
session.set('user_id', token.user_id);
return true;
@ -288,7 +274,7 @@ Api.fn.token = {
throw Api.err.INVALID_PARAMS;
}
this.tokens.remove(p_token).then(function () {
return this.tokens.remove(p_token).then(function () {
return true;
});
},
@ -297,7 +283,7 @@ Api.fn.token = {
// Pool management.
Api.fn.server = {
'add': function (session, req) {
var p_host = req.params.host; // @todo p_ prefixes.
var p_host = req.params.host;
var p_username = req.params.username;
var p_password = req.params.username;

View File

@ -1,17 +1,29 @@
var _ = require('underscore');
var Response = require('./response');
var Session = require('./session');
//--------------------------------------
var xo = require('./xo')();
var api = require('./api')(xo);
var Api = require('./api')(xo);
var api = new Api(xo);
//////////////////////////////////////////////////////////////////////
function json_api_call(session, transport, message)
{
var req;
var req = {
'id': null,
};
function send_error(error)
{
transport(JSON.stringify({
'jsonrpc': '2.0',
'error': error,
'id': req.id,
}));
}
try
{
@ -21,10 +33,7 @@ function json_api_call(session, transport, message)
{
if (e instanceof SyntaxError)
{
new Response(transport, null).sendError(
api.err
);
return;
send_error(Api.err.INVALID_JSON);
}
}
@ -33,10 +42,7 @@ function json_api_call(session, transport, message)
|| (undefined === req.id)
|| ('2.0' !== req.jsonrpc))
{
new Response(transport, null).sendError(
-32600,
'the JSON sent is not a valid request object'
);
send_error(Api.err.INVALID_REQUEST);
return;
}
@ -45,9 +51,17 @@ function json_api_call(session, transport, message)
{
'method': req.method,
'params': req.params,
}
).then(
function (result) {
transport(JSON.stringify({
'jsonrpc': '2.0',
'result': result,
'id': req.id,
}));
},
new Response(transport, req.id)
);
send_error
).done();
}
//////////////////////////////////////////////////////////////////////
@ -60,13 +74,18 @@ require('socket.io').listen(8080).sockets.on('connection', function (socket) {
};
var session = new Session();
session.on('close', function () {
session.once('close', function () {
socket.disconnect();
});
socket.on('message', function (message) {
json_api_call(session, transport, message);
});
// @todo Ugly inter dependency.
socket.once('disconnect', function () {
session.close();
});
});
//////////////////////////////////////////////////////////////////////
@ -78,7 +97,7 @@ require('net').createServer(function (socket) {
socket.write(message); // @todo Handle long messages.
};
var session = new Session();
var session = new Session(xo);
session.on('close', function () {
socket.end(); // @todo Check it is enough.
});
@ -112,4 +131,9 @@ require('net').createServer(function (socket) {
// @todo Check it frees the memory.
buffer = buffer.slice(length);
});
// @todo Ugly inter dependency.
socket.once('close', function () {
session.close();
});
}).listen('<path>'); // @todo

View File

@ -1,31 +0,0 @@
function Response(transport, id)
{
this.transport = transport;
this.id = id;
}
Response.prototype.sendResult = function (value)
{
this.transport(JSON.stringify({
'jsonrpc': '2.0',
'result': value,
'id': this.id,
}));
// Prevents results/errors to be sent more than once.
delete this.transport;
};
Response.prototype.sendError = function (error)
{
this.transport(JSON.stringify({
'jsonrpc': '2.0',
'error': error,
'id': this.id,
}));
// Prevents results/errors to be sent more than once.
delete this.transport;
};
module.exports = Response;

View File

@ -1,5 +1,47 @@
module.exports = require('model').extend({
var Model = require('./model');
module.exports = Model.extend({
'constructor': function (xo) {
Model.call(this);
var self = this;
var close = function () {
self.close();
};
// If the user associated to this session is deleted or
// disabled, the session must close.
this.on('change:user_id', function (user_id) {
var event = 'user.revoked'+ user_id;
xo.on(event, close);
// Prevents a memory leak.
self.on('close', function () {
xo.removeListener(event, close);
});
});
// If the token associated to this session is deleted, the
// session must close.
this.on('change:token_id', function (token_id) {
var event = 'token.revoked'+ token_id;
xo.on(event, close);
// Prevents a memory leak.
self.on('close', function () {
xo.removeListener(event, close);
});
});
},
'close': function () {
this.emit('close');
if (!this.closed)
{
this.closed = true;
this.emit('close');
}
},
});

View File

@ -144,8 +144,20 @@ function Xo()
this.tokens = new Tokens();
this.users = new Users();
//
// This events are used to automatically close connections if the
// associated credentials are invalidated.
this.tokens.on('remove', function (token_ids) {
token_ids.each(function (token_id) {
this.emit('token.revoked:'+ token_id);
});
});
this.users.on('remove', function (user_ids) {
user_ids.each(function (user_id) {
this.emit('user.revoked:'+ user_id);
});
});
}
require('util').inherits(Collection, require('events').EventEmitter);
module.exports = function () {
return new Xo();