X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?a=blobdiff_plain;f=docs%2Fprotocol-x-afb-ws-json1.md;h=3e3064f6cfdd8a24ce4c63dfbdba738c52055a5c;hb=7c67e966b8292961ca748e47d18ff437214cec1c;hp=cc5fe913f190442f5fcf667738aa845132720d83;hpb=0d3dc9722c610857c2f83c4f3bf06299f2426a95;p=src%2Fapp-framework-binder.git diff --git a/docs/protocol-x-afb-ws-json1.md b/docs/protocol-x-afb-ws-json1.md index cc5fe913..3e3064f6 100644 --- a/docs/protocol-x-afb-ws-json1.md +++ b/docs/protocol-x-afb-ws-json1.md @@ -1,5 +1,4 @@ -The websocket protocol x-afb-ws-json1 -===================================== +# The websocket protocol x-afb-ws-json1 The WebSocket protocol *x-afb-ws-json1* is used to communicate between an application and a binder. It allows access to all registered apis @@ -15,39 +14,36 @@ The registration to the IANA is still to be done, see: This document gives a short description of the protocol *x-afb-ws-json1*. A more formal description has to be done. - -Architecture ------------- +## Architecture The protocol is intended to be symmetric. It allows: - - to CALL a remote procedure that returns a result - - to push and receive EVENT - +- to CALL a remote procedure that returns a result +- to push and receive EVENT -Messages --------- +## Messages Valid messages are made of *text* frames that are all valid JSON. Valid messages are: Calls: -``` + +```txt [ 2, ID, PROCN, ARGS ] [ 2, ID, PROCN, ARGS, TOKEN ] ``` Replies (3: OK, 4: ERROR): -``` + +```txt [ 3, ID, RESP ] -[ 3, ID, RESP, TOKEN ] [ 4, ID, RESP ] -[ 4, ID, RESP, TOKEN ] ``` Events: -``` + +```txt [ 5, EVTN, OBJ ] ``` @@ -59,32 +55,41 @@ Where: | PROCN | string | The procedure name to call of the form "api/verb" | ARGS | any | Any argument to pass to the call (see afb_req_json that returns it) | RESP | any | The response to the call -| TOKEN | string | The token in case of refresh +| TOKEN | string | The authorisation token | EVTN | string | Name of the event in the form "api/event" | OBJ | any | The companion object of the event Below, an example of exchange: -``` +```txt C->S: [2,"156","hello/ping",null] S->C: [3,"156",{"response":"Some String","jtype":"afb-reply","request":{"status":"success","info":"Ping Binder Daemon tag=pingSample count=1 query=\"null\"","uuid":"ec30120c-6997-4529-9d63-c0de0cce56c0"}}] ``` +## History + +### 14 November 2019 + +Removal of token returning. The replies + +```txt +[ 3, ID, RESP, TOKEN ] +[ 4, ID, RESP, TOKEN ] +``` + +are removed from the specification. -Future ------- +## Future Here are the planned extensions: - - add binary messages with cbor data - - add calls with unstructured replies +- add binary messages with cbor data +- add calls with unstructured replies This could be implemented by extending the current protocol or by allowing the binder to accept either protocol including the new ones. - -Javascript implementation -------------------------- +## Javascript implementation The file **AFB.js** is a javascript implementation of the protocol. @@ -110,13 +115,13 @@ Here is that code: AFB = function(base, initialtoken){ if (typeof base != "object") - base = { base: base, token: initialtoken }; + base = { base: base, token: initialtoken }; var initial = { - base: base.base || "api", - token: base.token || initialtoken || "HELLO", - host: base.host || window.location.host, - url: base.url || undefined + base: base.base || "api", + token: base.token || initialtoken || "HELLO", + host: base.host || window.location.host, + url: base.url || undefined }; var urlws = initial.url || "ws://"+initial.host+"/"+initial.base; @@ -128,22 +133,22 @@ var urlws = initial.url || "ws://"+initial.host+"/"+initial.base; /*********************************************/ var AFB_context; { - var UUID = undefined; - var TOKEN = initial.token; - - var context = function(token, uuid) { - this.token = token; - this.uuid = uuid; - } - - context.prototype = { - get token() {return TOKEN;}, - set token(tok) {if(tok) TOKEN=tok;}, - get uuid() {return UUID;}, - set uuid(id) {if(id) UUID=id;} - }; - - AFB_context = new context(); + var UUID = undefined; + var TOKEN = initial.token; + + var context = function(token, uuid) { + this.token = token; + this.uuid = uuid; + } + + context.prototype = { + get token() {return TOKEN;}, + set token(tok) {if(tok) TOKEN=tok;}, + get uuid() {return UUID;}, + set uuid(id) {if(id) UUID=id;} + }; + + AFB_context = new context(); } /*********************************************/ /**** ****/ @@ -152,141 +157,141 @@ var AFB_context; /*********************************************/ var AFB_websocket; { - var CALL = 2; - var RETOK = 3; - var RETERR = 4; - var EVENT = 5; - - var PROTO1 = "x-afb-ws-json1"; - - AFB_websocket = function(on_open, on_abort) { - var u = urlws; - if (AFB_context.token) { - u = u + '?x-afb-token=' + AFB_context.token; - if (AFB_context.uuid) - u = u + '&x-afb-uuid=' + AFB_context.uuid; - } - this.ws = new WebSocket(u, [ PROTO1 ]); - this.url = u; - this.pendings = {}; - this.awaitens = {}; - this.counter = 0; - this.ws.onopen = onopen.bind(this); - this.ws.onerror = onerror.bind(this); - this.ws.onclose = onclose.bind(this); - this.ws.onmessage = onmessage.bind(this); - this.onopen = on_open; - this.onabort = on_abort; - } - - function onerror(event) { - var f = this.onabort; - if (f) { - delete this.onopen; - delete this.onabort; - f && f(this); - } - this.onerror && this.onerror(this); - } - - function onopen(event) { - var f = this.onopen; - delete this.onopen; - delete this.onabort; - f && f(this); - } - - function onclose(event) { - for (var id in this.pendings) { - try { this.pendings[id][1](); } catch (x) {/*TODO?*/} - } - this.pendings = {}; - this.onclose && this.onclose(); - } - - function fire(awaitens, name, data) { - var a = awaitens[name]; - if (a) - a.forEach(function(handler){handler(data);}); - var i = name.indexOf("/"); - if (i >= 0) { - a = awaitens[name.substring(0,i)]; - if (a) - a.forEach(function(handler){handler(data);}); - } - a = awaitens["*"]; - if (a) - a.forEach(function(handler){handler(data);}); - } - - function reply(pendings, id, ans, offset) { - if (id in pendings) { - var p = pendings[id]; - delete pendings[id]; - try { p[offset](ans); } catch (x) {/*TODO?*/} - } - } - - function onmessage(event) { - var obj = JSON.parse(event.data); - var code = obj[0]; - var id = obj[1]; - var ans = obj[2]; - AFB_context.token = obj[3]; - switch (code) { - case RETOK: - reply(this.pendings, id, ans, 0); - break; - case RETERR: - reply(this.pendings, id, ans, 1); - break; - case EVENT: - default: - fire(this.awaitens, id, ans); - break; - } - } - - function close() { - this.ws.close(); - this.ws.onopen = - this.ws.onerror = - this.ws.onclose = - this.ws.onmessage = - this.onopen = - this.onabort = function(){}; - } - - function call(method, request, callid) { - return new Promise((function(resolve, reject){ - var id, arr; - if (callid) { - id = String(callid); - if (id in this.pendings) - throw new Error("pending callid("+id+") exists"); - } else { - do { - id = String(this.counter = 4095 & (this.counter + 1)); - } while (id in this.pendings); - } - this.pendings[id] = [ resolve, reject ]; - arr = [CALL, id, method, request ]; - if (AFB_context.token) arr.push(AFB_context.token); - this.ws.send(JSON.stringify(arr)); - }).bind(this)); - } - - function onevent(name, handler) { - var id = name; - var list = this.awaitens[id] || (this.awaitens[id] = []); - list.push(handler); - } - - AFB_websocket.prototype = { - close: close, - call: call, - onevent: onevent - }; + var CALL = 2; + var RETOK = 3; + var RETERR = 4; + var EVENT = 5; + + var PROTO1 = "x-afb-ws-json1"; + + AFB_websocket = function(on_open, on_abort) { + var u = urlws; + if (AFB_context.token) { + u = u + '?x-afb-token=' + AFB_context.token; + if (AFB_context.uuid) + u = u + '&x-afb-uuid=' + AFB_context.uuid; + } + this.ws = new WebSocket(u, [ PROTO1 ]); + this.url = u; + this.pendings = {}; + this.awaitens = {}; + this.counter = 0; + this.ws.onopen = onopen.bind(this); + this.ws.onerror = onerror.bind(this); + this.ws.onclose = onclose.bind(this); + this.ws.onmessage = onmessage.bind(this); + this.onopen = on_open; + this.onabort = on_abort; + } + + function onerror(event) { + var f = this.onabort; + if (f) { + delete this.onopen; + delete this.onabort; + f && f(this); + } + this.onerror && this.onerror(this); + } + + function onopen(event) { + var f = this.onopen; + delete this.onopen; + delete this.onabort; + f && f(this); + } + + function onclose(event) { + for (var id in this.pendings) { + try { this.pendings[id][1](); } catch (x) {/*TODO?*/} + } + this.pendings = {}; + this.onclose && this.onclose(); + } + + function fire(awaitens, name, data) { + var a = awaitens[name]; + if (a) + a.forEach(function(handler){handler(data);}); + var i = name.indexOf("/"); + if (i >= 0) { + a = awaitens[name.substring(0,i)]; + if (a) + a.forEach(function(handler){handler(data);}); + } + a = awaitens["*"]; + if (a) + a.forEach(function(handler){handler(data);}); + } + + function reply(pendings, id, ans, offset) { + if (id in pendings) { + var p = pendings[id]; + delete pendings[id]; + try { p[offset](ans); } catch (x) {/*TODO?*/} + } + } + + function onmessage(event) { + var obj = JSON.parse(event.data); + var code = obj[0]; + var id = obj[1]; + var ans = obj[2]; + AFB_context.token = obj[3]; + switch (code) { + case RETOK: + reply(this.pendings, id, ans, 0); + break; + case RETERR: + reply(this.pendings, id, ans, 1); + break; + case EVENT: + default: + fire(this.awaitens, id, ans); + break; + } + } + + function close() { + this.ws.close(); + this.ws.onopen = + this.ws.onerror = + this.ws.onclose = + this.ws.onmessage = + this.onopen = + this.onabort = function(){}; + } + + function call(method, request, callid) { + return new Promise((function(resolve, reject){ + var id, arr; + if (callid) { + id = String(callid); + if (id in this.pendings) + throw new Error("pending callid("+id+") exists"); + } else { + do { + id = String(this.counter = 4095 & (this.counter + 1)); + } while (id in this.pendings); + } + this.pendings[id] = [ resolve, reject ]; + arr = [CALL, id, method, request ]; + if (AFB_context.token) arr.push(AFB_context.token); + this.ws.send(JSON.stringify(arr)); + }).bind(this)); + } + + function onevent(name, handler) { + var id = name; + var list = this.awaitens[id] || (this.awaitens[id] = []); + list.push(handler); + } + + AFB_websocket.prototype = { + close: close, + call: call, + onevent: onevent + }; } /*********************************************/ /**** ****/ @@ -294,8 +299,8 @@ var AFB_websocket; /**** ****/ /*********************************************/ return { - context: AFB_context, - ws: AFB_websocket + context: AFB_context, + ws: AFB_websocket }; }; -``` \ No newline at end of file +```