From b3cc8da315c0017ff78ef36136390e020b30bb26 Mon Sep 17 00:00:00 2001 From: ansuz Date: Fri, 10 Mar 2017 18:03:52 +0100 Subject: [PATCH] implement basic clientside RPC infrastructure and provide a basic example for testing it --- www/common/rpc.js | 88 ++++++++++++++++++++++++++++++------- www/examples/rpc/index.html | 13 ++++++ www/examples/rpc/inner.html | 8 ++++ www/examples/rpc/main.js | 35 +++++++++++++++ 4 files changed, 129 insertions(+), 15 deletions(-) create mode 100644 www/examples/rpc/index.html create mode 100644 www/examples/rpc/inner.html create mode 100644 www/examples/rpc/main.js diff --git a/www/common/rpc.js b/www/common/rpc.js index 3d15843cc..8cb7c6f8a 100644 --- a/www/common/rpc.js +++ b/www/common/rpc.js @@ -3,26 +3,73 @@ define([ '/bower_components/tweetnacl/nacl-fast.min.js', ], function (Encode) { + var MAX_LAG_BEFORE_TIMEOUT = 30000; - var getHistoryKeeperName = function (network) { - var wc = network.webChannels[0]; - if (!wc) { - throw new Error("ERROR: no joined webchannels so we can't get the history keeper name"); - } - if (!wc.history_keeper) { throw new Error("ERROR: no history keeper"); } - return wc.history_keeper; + var uid = function () { + return Number(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)) + .toString(32).replace(/\./g, ''); }; - var sendMsg = function (ctx, cb) { - var hkn = getHistoryKeeperName(ctx.network); + /* +types of messages: + pin -> hash + unpin -> hash + getHash -> hash + getTotalSize -> bytes + getFileSize -> bytes + + */ + +/* RPC communicates only with the history keeper + messages have the format: + [TYPE, txid, msg] +*/ + var sendMsg = function (ctx, type, msg, cb) { + var network = ctx.network; + var hkn = network.historyKeeper; + var txid = uid(); + + ctx.pending[txid] = cb; + + return network.sendto(hkn, JSON.stringify([txid, type, msg])); }; + var parse = function (msg) { + try { + return JSON.parse(msg); + } catch (e) { + return null; + } + }; + +/* Returning messages have the format: + [txid, {}] +*/ var onMsg = function (ctx, msg) { - console.log(msg); + var parsed = parse(msg); + + if (!parsed) { + // TODO handle error + console.log(msg); + return; + } + + var txid = parsed[0]; + var pending = ctx.pending[txid]; + var response = parsed.slice(1); + + if (typeof(pending) === 'function') { + if (response[0] === 'ERROR') { + return void pending(response[1]); + } + pending(void 0, response); + } else { + console.log("No callback provided"); + } }; var cookie = function (ctx, cb) { - // TODO + // TODO txid }; var signMsg = function (msg, secKey) { @@ -31,16 +78,27 @@ define([ var create = function (network, edPrivateKey) { if (!/[0-9a-f]{64}/.test(edPrivateKey)) { - throw new Error("private signing key is not valid"); + //throw new Error("private signing key is not valid"); } var ctx = { - privateKey: Encode.hexToUint8Array(edPrivateKey), + //privateKey: Encode.hexToUint8Array(edPrivateKey), seq: new Date().getTime(), - network: network + network: network, + timeouts: {}, // timeouts + pending: {}, // callbacks + }; + + var pin = function (channel, cb) { }; + + var send = function (type, msg, cb) { + return sendMsg(ctx, type, msg, cb); }; - network.on('message', function (msg) { onMsg(ctx, msg); }); + network.on('message', function (msg, sender) { + onMsg(ctx, msg); + }); return { cookie: function (cb) { cookie(ctx, cb); }, + send: send, }; }; diff --git a/www/examples/rpc/index.html b/www/examples/rpc/index.html new file mode 100644 index 000000000..e17a68143 --- /dev/null +++ b/www/examples/rpc/index.html @@ -0,0 +1,13 @@ + + + + CryptPad + + + + +
+ +
+ + diff --git a/www/examples/rpc/inner.html b/www/examples/rpc/inner.html new file mode 100644 index 000000000..9680685b7 --- /dev/null +++ b/www/examples/rpc/inner.html @@ -0,0 +1,8 @@ + + + + + + + +

PEWPEW diff --git a/www/examples/rpc/main.js b/www/examples/rpc/main.js new file mode 100644 index 000000000..528290d66 --- /dev/null +++ b/www/examples/rpc/main.js @@ -0,0 +1,35 @@ +require.config({ paths: { 'json.sortify': '/bower_components/json.sortify/dist/JSON.sortify' } }); +define([ + '/common/cryptpad-common.js', + '/common/rpc.js', + '/bower_components/jquery/dist/jquery.min.js', +], function (Cryptpad, RPC) { + var $ = window.jQuery; + var APP = window.APP = { + Cryptpad: Cryptpad, + }; + + $(function () { + Cryptpad.ready(function (err, env) { + var network = Cryptpad.getNetwork(); + var rpc = RPC.create(network); // TODO signing key + + var payload = { + a: Math.floor(Math.random() * 1000), + b: 7, + }; + + // console.log(payload); + rpc.send('ECHO', payload, function (e, msg) { + if (e) { return void console.error(e); } + console.log(msg); + }); + + // test a non-existent RPC call + rpc.send('PEWPEW', ['pew'], function (e, msg) { + if (e) { return void console.error(e); } + console.log(msg); + }); + }); + }); +});