Skip to content

Instantly share code, notes, and snippets.

@pohy
Created Jun 4, 2019
Embed
What would you like to do?
Async mJS RPC Handler
load('rpc.js');
let testWiFi = ffi('bool mgos_captive_portal_wifi_setup_test(char*,char*,void(*)(bool,char*,char*,userdata),userdata)');
RPC.addHandlerAsync('ConnectToWifi', function(respond, args) {
if (
typeof args !== 'object'
|| typeof args.ssid !== 'string' || args.ssid.length < 1
|| typeof args.pass !== 'string' || args.pass.length < 1
) {
return respond(FHSConnectToWiFiError());
}
print('Testing wifi', args.ssid, args.pass);
testWiFi(
args.ssid,
args.pass,
function(success, ssid, pass) {
print('Test result', ssid, pass, JSON.stringify(success));
return respond({
ssid: ssid,
pass: pass,
success: success
});
},
null,
);
});
function FHSConnectToWiFiError() {
return {
error: -1,
message: 'Bad request. Expected: {"ssid": "wifi-ssid", "pass": "wifi-pass"}',
};
}
let RPC = {
_sdup: ffi('void *strdup(char *)'),
_ah: ffi('void *mgos_rpc_add_handler(void *, void (*)(void *, char *, char *, userdata), userdata)'),
_resp: ffi('bool mgos_rpc_send_response(void *, char *)'),
_call: ffi('bool mgos_rpc_call(char *, char *, char *, void (*)(char *, int, char *, userdata), userdata)'),
_err: ffi('bool mg_rpc_send_errorf(void *, int, char *, char *)'),
_ahcb: function(ri, args, src, ud) {
// NOTE(lsm): not using `this` here deliberately. Calleth from C.
let resp = ud.cb(JSON.parse(args || 'null'), src, ud.ud);
RPC._rh(ri, resp);
// NOTE: we don't call ffi_cb_free here because this handler might be used
// more than once
},
_ahcba: function(ri, args, src, ud) {
// TODO: Handle timeout
ud.cb(
function(resp) {
// TODO: Error when request has already been handled and respond function is called again
RPC._rh(ri, resp);
},
JSON.parse(args || 'null'),
src,
ud.ud
);
},
_rh: function (ri, resp) {
if (typeof(resp) === 'undefined') resp = null;
if (typeof(resp) === 'object' && typeof(resp.error) === 'number') {
RPC._err(ri, resp.error, '%s', resp.message || '');
} else {
RPC._resp(ri, JSON.stringify(resp));
}
},
_ccb: function(res, code, msg, ud) {
ud.cb(res ? JSON.parse(res) : null, code, msg, ud.ud);
ffi_cb_free(RPC._ccb, ud);
},
LOCAL: "RPC.LOCAL",
// ## **`RPC.addHandler(name, handler)`**
// Add RPC handler. `name` is a string like `'MyMethod'`, `handler`
// is a callback function which takes `args` arguments object.
// If a handler returns an object with a numeric `error` attribute and
// optional `message` string attribute, the caller will get a failure.
//
// Return value: none.
//
// Example:
// ```javascript
// RPC.addHandler('Sum', function(args) {
// if (typeof(args) === 'object' && typeof(args.a) === 'number' &&
// typeof(args.b) === 'number') {
// return args.a + args.b;
// } else {
// return {error: -1, message: 'Bad request. Expected: {"a":N1,"b":N2}'};
// }
// });
// ```
addHandler: function(name, cb, ud) {
let data = {cb: cb, ud: ud};
// TODO(lsm): get rid of this strdup() leak. One-off, but still ugly.
this._ah(this._sdup(name), this._ahcb, data);
},
addHandlerAsync: function(name, cb, ud) {
let data = {cb: cb, ud: ud};
this._ah(this._sdup(name), this._ahcba, data);
},
// ## **`RPC.call(dst, method, args, callback)`**
// Call remote or local RPC service.
// Return value: true in case of success, false otherwise.
//
// If `dst` is empty, connected server is implied. `method` is a string
// like "MyMethod", `callback` is a callback function which takes the following
// arguments: res (results object), err_code (0 means success, or error code
// otherwise), err_msg (error messasge for non-0 error code), userdata. Example:
//
// ```javascript
// RPC.call(RPC.LOCAL, 'Config.Save', {reboot: true}, function (resp, ud) {
// print('Response:', JSON.stringify(resp));
// }, null);
// ```
call: function(dst, name, args, cb, ud) {
let data = {cb: cb, ud: ud};
return this._call(dst, name, JSON.stringify(args), this._ccb, data);
},
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment