Fork me on GitHub

RPC - Remote Procedure Calls

MG-RPC frame format

Request frame format

{
  "method": "Math.Add",     // Required. Function name to invoke.
  "args": {                 // Optional. Call arguments
    "a": 1,
    "b": 2
  },
  "src": "joe/32efc823aa",  // Optional. Used with MQTT: response will be sent
                            // to that value followed by "/rpc", so in this
                            // case it'll be "joe/32efc823aa/rpc".
  "tag": "hey!",            // Optional. Any arbitrary string. Will be repeated in the response
  "id": 1772                // Optional. Numeric frame ID.
}

Successful response frame format

{
  "result": { ... },        // Required. Call result
  "tag": "hey!"             // Optional. Present if request contained "tag"
}

Failure response frame format

{
  "error": {
    "code": 123,
    "message": "oops"
  }
}

If the error key is present in the response, it's a failure. Failed response may also contain a result, in order to pass more specific information about the failure.

C API Reference mgos_rpc.h

#include "mgos_rpc.h"


bool mgos_rpc_common_init(void);

struct mg_rpc *mgos_rpc_get_global(void);

struct mg_rpc_cfg *mgos_rpc_cfg_from_sys(const struct sys_config *scfg);

void mgos_rpc_channel_ws_out_cfg_from_sys(
    const struct sys_config *scfg, struct mg_rpc_channel_ws_out_cfg *chcfg);

typedef void (*mgos_rpc_eh_t)(struct mg_rpc_request_info *ri, const char *args,
                              const char *src, void *user_data);

FFI-able signature of the function that receives RPC request

typedef void (*mgos_rpc_result_cb_t)(const char *result, int error_code,
                                     const char *error_msg, void *cb_arg);

FFI-able signature of the function that receives response to a request.

void mgos_rpc_add_handler(const char *method, mgos_rpc_eh_t cb, void *cb_arg);

FFI-able function to add an RPC handler

bool mgos_rpc_send_response(struct mg_rpc_request_info *ri,
                            const char *response_json);

FFI-able function to send response from an RPC handler

bool mgos_rpc_call(const char *dst, const char *method, const char *args_json,
                   mgos_rpc_result_cb_t cb, void *cb_arg);

FFI-able function to perform an RPC call

int mgos_print_sys_info(struct json_out *out);

Print system info JSON object. Return number of bytes written.

C API Reference mg_rpc.h

#include "mg_rpc.h"

Channel this request was received on. Will be used to route the response if present and valid, otherwise src is used to find a suitable channel.


struct mg_rpc_cfg {
  char *id;
  char *psk;
  int max_queue_length;
  int default_out_channel_idle_close_timeout;
};

struct mg_rpc_frame {
  int version;
  int64_t id;
  int error_code;
  struct mg_str src, dst, tag;
  struct mg_str method, args;
  struct mg_str result, error_msg;
  struct mg_str auth;
};

struct mg_rpc_authn {
  struct mg_str username;
};

struct mg_rpc *mg_rpc_create(struct mg_rpc_cfg *cfg);

Create mg_rpc instance. Takes over cfg, which must be heap-allocated.

void mg_rpc_add_channel(struct mg_rpc *c, const struct mg_str dst,
                        struct mg_rpc_channel *ch, bool is_trusted);

Adds a channel to the instance. If dst is empty, it will be learned when first frame arrives from the other end. A "default" channel, if present, will be used for frames that don't have a better match. If is_trusted is true, certain privileged commands will be allowed.

void mg_rpc_connect(struct mg_rpc *c);

Invokes connect method on all channels of this instance.

void mg_rpc_disconnect(struct mg_rpc *c);

Invokes close method on all channels of this instance.

struct mg_rpc_frame_info {
  const char *channel_type; /* Type of the channel this message arrived on. */
  bool channel_is_trusted;  /* Whether the channel is marked as trusted. */
};

Auxiliary information about the request or response.

typedef void (*mg_result_cb_t)(struct mg_rpc *c, void *cb_arg,
                               struct mg_rpc_frame_info *fi,
                               struct mg_str result, int error_code,
                               struct mg_str error_msg);

Signature of the function that receives response to a request.

struct mg_rpc_call_opts {
  struct mg_str dst; /* Destination ID. If not provided, cloud is implied. */
  struct mg_str tag; /* Frame tag. Gets copied verbatim to the response. */
  struct mg_str key; /* Frame key, used for preshared key based auth. */
  bool noqueue;      /* Dont enqueue frame if destination is unavailable */
};

RPC call options.

bool mg_rpc_callf(struct mg_rpc *c, const struct mg_str method,
                  mg_result_cb_t cb, void *cb_arg,
                  const struct mg_rpc_call_opts *opts, const char *args_jsonf,
                  ...);

Make an RPC call. The destination RPC server is specified by opts, and destination RPC service name is method. cb callback function is optional, in which case request is sent but response is not required. opts can be NULL, in which case the default destination is used. Example - calling a remote RPC server over websocket:

struct mg_rpc_call_opts opts = {.dst = mg_mk_str("ws://1.2.3.4/foo") };
mg_rpc_callf(mgos_rpc_get_global(), mg_mk_str("My.Func"), NULL, NULL, &opts,
             "{param1: %Q, param2: %d}", "jaja", 1234);

It is possible to call RPC services running locally. In this case, include the https://github.com/mongoose-os-libs/rpc-loopback library, and use MGOS_RPC_LOOPBACK_ADDR special destination address:

#include "mg_rpc_channel_loopback.h"
struct mg_rpc_call_opts opts = {.dst = mg_mk_str(MGOS_RPC_LOOPBACK_ADDR) };

bool mg_rpc_vcallf(struct mg_rpc *c, const struct mg_str method,
                   mg_result_cb_t cb, void *cb_arg,
                   const struct mg_rpc_call_opts *opts, const char *args_jsonf,
                   va_list ap);

Same as mg_rpc_callf, but takes va_list ap

struct mg_rpc_request_info {
  struct mg_rpc *rpc;
  int64_t id;           /* Request id. */
  struct mg_str src;    /* Id of the request sender, if provided. */
  struct mg_str tag;    /* Request tag. Opaque, should be passed back as is. */
  struct mg_str method; /* RPC Method */
  struct mg_str auth;   /* Auth JSON */
  struct mg_rpc_authn authn_info; /* Parsed authn info; either from the
                                     underlying channel or from RPC layer */
  const char *args_fmt;           /* Arguments format string */
  void *user_data; /* Place to store user pointer. Not used by mg_rpc. */

  /*
   * Channel this request was received on. Will be used to route the response
   * if present and valid, otherwise src is used to find a suitable channel.
   */
  struct mg_rpc_channel *ch;
};

Incoming request info. This structure is passed to request handlers and must be passed back when a response is ready.

typedef void (*mg_handler_cb_t)(struct mg_rpc_request_info *ri, void *cb_arg,
                                struct mg_rpc_frame_info *fi,
                                struct mg_str args);

Signature of an incoming request handler. Note that only request_info remains valid after return from this function, frame_info and args will be invalidated.

void mg_rpc_add_handler(struct mg_rpc *c, const char *method,
                        const char *args_fmt, mg_handler_cb_t cb, void *cb_arg);

Add a method handler.

typedef bool (*mg_prehandler_cb_t)(struct mg_rpc_request_info *ri, void *cb_arg,
                                   struct mg_rpc_frame_info *fi,
                                   struct mg_str args);

Signature of an incoming requests prehandler, which is called right before calling the actual handler.

If it returns false, the further request processing is not performed. It's called for existing handlers only.

void mg_rpc_set_prehandler(struct mg_rpc *c, mg_prehandler_cb_t cb,
                           void *cb_arg);

Set a generic method prehandler.

bool mg_rpc_send_responsef(struct mg_rpc_request_info *ri,
                           const char *result_json_fmt, ...);

Respond to an incoming request. result_json_fmt can be NULL, in which case no result is included. ri is freed by the call, so it's illegal to use it afterwards.

bool mg_rpc_send_errorf(struct mg_rpc_request_info *ri, int error_code,
                        const char *error_msg_fmt, ...);

Send and error response to an incoming request. error_msg_fmt is optional and can be NULL, in which case only code is sent. ri is freed by the call, so it's illegal to use it afterwards.

bool mg_rpc_send_error_jsonf(struct mg_rpc_request_info *ri, int error_code,
                             const char *error_json_fmt, ...);

Like mg_rpc_send_errorf, but uses JSON formatting, see json_printf(). NOTE: "error.message" will still be a string but will contain serialized JSON formatted accordingly to error_json_fmt.

bool mg_rpc_is_connected(struct mg_rpc *c);

Returns true if the instance has an open default channel.

bool mg_rpc_can_send(struct mg_rpc *c);

Returns true if the instance has an open default channel and it's not currently busy.

enum mg_rpc_event {
  MG_RPC_EV_CHANNEL_OPEN,   /* struct mg_str *dst */
  MG_RPC_EV_CHANNEL_CLOSED, /* struct mg_str *dst */
};

mg_rpc event observer.

typedef void (*mg_observer_cb_t)(struct mg_rpc *c, void *cb_arg,
                                 enum mg_rpc_event ev, void *ev_arg);

void mg_rpc_add_observer(struct mg_rpc *c, mg_observer_cb_t cb, void *cb_arg);

void mg_rpc_remove_observer(struct mg_rpc *c, mg_observer_cb_t cb,
                            void *cb_arg);

void mg_rpc_free_request_info(struct mg_rpc_request_info *ri);

void mg_rpc_free(struct mg_rpc *c);

void mg_rpc_add_list_handler(struct mg_rpc *c);

Enable RPC.List handler that returns a list of all registered endpoints

bool mg_rpc_parse_frame(const struct mg_str f, struct mg_rpc_frame *frame);

Parses frame f and stores result into frame. Returns true in case of success, false otherwise.

bool mg_rpc_check_digest_auth(struct mg_rpc_request_info *ri);

void mg_rpc_authn_free(struct mg_rpc_authn *authn);

JAVASCRIPT API Reference api_rpc.js

load("api_rpc.js");


RPC.addHandler(name, handler)

Add RPC handler. name is a string like 'MyMethod', handler is a callback function which takes args arguments object.

Return value: none.

Example:

RPC.addHandler('Sum', function(args) {
  return args.a + args.b;
});

The installed handler is available over Serial, Restful, MQTT, Websocket, for example over Websocket:

$ mos --port ws://192.168.0.206/rpc call Sum '{"a":1, "b": 2}'
3

Or, over familiar RESTful call:

$ curl -d '{"a":1, "b": 2}' 192.168.0.206/rpc/Sum
3

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:

RPC.call(RPC.LOCAL, 'Config.Save', {reboot: true}, function (resp, ud) {
  print('Response:', JSON.stringify(resp));
}, null);