From ddf259f8b77506be01b9ab029382f77a0f111e35 Mon Sep 17 00:00:00 2001 From: Julien Fontanet Date: Fri, 5 Jul 2013 13:09:20 +0200 Subject: [PATCH] Various updates. --- package.json | 2 +- src/api.js | 103 +++++++++++++++++++++++---------- src/main.js | 160 ++++++++++++++++++++++++++++----------------------- src/xo.js | 13 +++-- 4 files changed, 170 insertions(+), 108 deletions(-) diff --git a/package.json b/package.json index c2c8920..83d17b5 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ }, "main": "src/main.js", "dependencies": { - "backbone": ">=1.0.0", + "extendable": ">=0.0.3", "hashy": ">=0.1.0", "underscore": ">=1.4.4" }, diff --git a/src/api.js b/src/api.js index 8032660..1215ded 100644 --- a/src/api.js +++ b/src/api.js @@ -2,6 +2,17 @@ var _ = require('underscore'); ////////////////////////////////////////////////////////////////////// +function deprecated(fn) +{ + return function (session, req, res) { + console.warn(req.method +' is deprecated!'); + + return fn.call(this, session, req, res); + }; +} + +////////////////////////////////////////////////////////////////////// + function Api(xo) { this.xo = xo; @@ -41,9 +52,19 @@ Api.prototype.get = function (name) { ) {} - return _.isFunction(current) - ? current - : undefined + // Method found. + if (_.isFunction(current)) + { + return current; + } + + // It's a (deprecated) alias. + if (_.isString(current)) + { + return deprecated(this.get(current)); + } + + return undefined; ; }; @@ -64,14 +85,14 @@ function err(code, message) Api.err = { ////////////////////////////////////////////////////////////////// - // JSON errors. + // JSON-RPC errors. ////////////////////////////////////////////////////////////////// 'INVALID_JSON': err(-32700, 'invalid JSON'), 'INVALID_REQUEST': err(-32600, 'invalid JSON-RPC request'), - 'INVALID_METHOD': err(-326001, 'method not found'), + 'INVALID_METHOD': err(-32601, 'method not found'), 'INVALID_PARAMS': err(-32602, 'invalid parameter(s)'), @@ -100,6 +121,7 @@ Api.fn.api = { }, }; +// Session management Api.fn.session = { 'signInWithPassword': function (session, req, res) { var p_email = req.params.email; @@ -161,7 +183,7 @@ Api.fn.session = { return true; }, - 'getUser': function (session, req, res) { + 'getUser': deprecated(function (session, req, res) { var user_id = session.get('user_id'); if (undefined === user_id) { @@ -169,36 +191,18 @@ Api.fn.session = { } return _.pick(users.get(user_id), 'id', 'email'); + }); + + 'getUserId': function (session, req, res) { + return session.get('user_id', null); }; - 'createToken': function (session, req, res) { - var user_id = session.get('user_id'); - if ((undefined === user_id) - || session.has('token_id')) - { - throw Api.err.UNAUTHORIZED; - } + 'createToken': 'token.create' - // @todo Ugly. - var token = this.tokens.model.generate(user_id); - this.tokens.add(token); - - return token.id; - }, - - 'destroyToken': function (session, req, res) { - var p_token = req.params.token; - - if (!this.tokens.get(p_token)) - { - throw Api.err.INVALID_PARAMS; - } - - this.tokens.remove(p_token); - return true; - }, + 'destroyToken': 'token.delete', }; +// User management. Api.fn.user = { 'create': function (session, req, res) { var p_email = req.params.email; @@ -220,7 +224,9 @@ Api.fn.user = { }, 'delete': function (session, req, res) { + var p_id = req.params.id; + var user }, 'changePassword': function (session, req, res) { @@ -235,3 +241,38 @@ Api.fn.user = { }, }; + +// Token management. +Api.fn.token = { + 'create': function (session, req, res) { + var user_id = session.get('user_id'); + if ((undefined === user_id) + || session.has('token_id')) + { + throw Api.err.UNAUTHORIZED; + } + + // @todo Ugly. + var token = this.tokens.model.generate(user_id); + this.tokens.add(token); + + return token.id; + }, + + 'delete': function (session, req, res) { + var p_token = req.params.token; + + if (!this.tokens.get(p_token)) + { + throw Api.err.INVALID_PARAMS; + } + + this.tokens.remove(p_token); + return true; + }, +}; + +// VM +Api.fn.vm = { + +}; diff --git a/src/main.js b/src/main.js index 4231c7e..a5f48a9 100644 --- a/src/main.js +++ b/src/main.js @@ -1,5 +1,6 @@ -var events = require('events'); +var EventEmitter = require('events').EventEmitter; var util = require('util'); +var Session = require('session'); //-------------------------------------- @@ -8,34 +9,6 @@ var api = require('./api')(xo); ////////////////////////////////////////////////////////////////////// -function Session() -{ - this.data = {}; -} - -util.inherits(Session, events.EventEmitter); - -Session.prototype.close = function () { - session.emit('close'); -}; - -Session.prototype.get = function (name, def) { - if (undefined !== this.data[name]) - { - return this.data[name]; - } - - return def; -}; - -Session.prototype.has = function (name) { - return (undefined !== this.data[name]); -}; - -Session.prototype.set = function (name, value) { - this.data[name] = value; -}; - ////////////////////////////////////////////////////////////////////// function Response(transport, id) @@ -70,53 +43,98 @@ Response.prototype.sendError = function (error) ////////////////////////////////////////////////////////////////////// -require('socket.io') - .listen(8080) - .sockets.on('connection', function (socket) { +function json_api_call(session, transport, message) +{ + try + { + var req = JSON.parse(message.toString()); + } + catch (e if e instanceof SyntaxError) + { + new Response(transport, null).sendError( + api.err + ); + return; + } - // @todo comment - var transport = function (message) { - socket.send(data); - }; + if (!req.method || !req.params + || (undefined === req.id) + || ('2.0' !== req.jsonrpc)) + { + new Response(transport, null).sendError( + -32600, + 'the JSON sent is not a valid request object' + ); + return; + } - var session = new Session(); - session.on('close', function () { - socket.disconnect(); - }); + api.exec( + session, + { + 'method': req.method, + 'params': req.params, + }, + new Response(transport, req.id) + ); +} - // When a message is received. - socket.on('message', function (message) { - try +////////////////////////////////////////////////////////////////////// +// JSON-RPC over WebSocket. +////////////////////////////////////////////////////////////////////// + +require('socket.io').listen(8080).sockets.on('connection', function (socket) { + var transport = function (message) { + socket.send(data); + }; + + var session = new Session(); + session.on('close', function () { + socket.disconnect(); + }); + + socket.on('message', function (message) { + json_api_call(session, transport, message); + }); +}); + +////////////////////////////////////////////////////////////////////// +// JSON-RPC over TCP. +////////////////////////////////////////////////////////////////////// + +require('net').createServer(function (socket) { + var transport = function (message) { + socket.write(message); // @todo Handle long messages. + }; + + var session = new Session(); + session.on('close', function () { + socket.end(); // @todo Check it is enough. + }); + + var length = null; // Expected message length. + var buffer = new Buffer(); + socket.on('data', function (data) { + data.copy(buffer); + + // Read the message length. + if (!length) + { + var i = _.indexOf(buffer, 10); + if (-1 === i) { - var req = JSON.parse(message.toString()); - } - catch (e if e instanceof SyntaxError) - { - new Response(transport, null).sendError( - api.err - ); return; } - if (!req.method || !req.params - || (undefined === req.id) - || ('2.0' !== req.jsonrpc)) - { - new Response(transport, null).sendError( - -32600, - 'the JSON sent is not a valid request object' - ); - return; - } + length = +buffer.toString('ascii', 0, i); // @todo Handle NaN. + buffer = buffer.slice(i + 1); + } - api.exec( - session, - { - 'method': req.method, - 'params': req.params, - }, - new Response(transport, req.id) - ); - }); - }) -; + // We do not have received everything. + if (buffer.length < length) + { + return; + } + + json_api_call(session, transport, buffer.toString()); + }); +}).listen(''); // @todo diff --git a/src/xo.js b/src/xo.js index 5c92337..e628c72 100644 --- a/src/xo.js +++ b/src/xo.js @@ -1,8 +1,9 @@ var _ = require('underscore'); -var Backbone = require('backbone'); var crypto = require('crypto'); var hashy = require('hashy'); var Q = require('q'); +var Collection = require('collection'); +var Model = require('model'); ////////////////////////////////////////////////////////////////////// @@ -33,7 +34,7 @@ var check = function () { // @todo We could also give a permission level to tokens (<= // user.permission). -var Token = Backbone.Model.extend({ +var Token = Model.extend({ // Validates model attributes. 'validate': function (attr) { check(attr.id).len(10); @@ -52,7 +53,9 @@ var Token = Backbone.Model.extend({ }, }); -var User = Backbone.Model.extend({ +user.set('password', '123'); + +var User = Model.extend({ 'default': { 'permission': 'none', }, @@ -102,11 +105,11 @@ var User = Backbone.Model.extend({ // Collections ////////////////////////////////////////////////////////////////////// -var Tokens = Backbone.Collection.extend({ +var Tokens = Collection.extend({ 'model': Token, }); -var Users = Backbone.Collection.extend({ +var Users = Collection.extend({ 'model': User, });