std1.0.0-beta-7 SafeC docs

Source rpc/server_fn.sc

View in the repository

// SafeC Standard Library — Server functions implementation (see
// server_fn.h).
#pragma once
#include <std/rpc/server_fn.h>
#include <std/rpc/rpc.h>
#include <std/serial/value.h>
#include <std/serial/value.h>
#include <std/serial/json.h>
#include <std/serial/json.h>
#include <std/collections/string.h>
#include <std/collections/string.h>
#include <std/mem.h>

namespace std {

#define SERVER_FN_MAX 64

struct ServerFnEntry {
    struct String name;
    ServerFn handler;
};

static struct ServerFnEntry gServerFns[SERVER_FN_MAX];
static int gServerFnCount = 0;

void server_fn_register(const &char name, ServerFn handler) {
    if (gServerFnCount >= SERVER_FN_MAX) { return; }
    unsafe {
        gServerFns[gServerFnCount].name = String::from(name);
        gServerFns[gServerFnCount].handler = handler;
    }
    gServerFnCount = gServerFnCount + 1;
}

static ServerFn __server_fn_lookup(const char* name) {
    int i = 0;
    while (i < gServerFnCount) {
        int eq; unsafe { eq = gServerFns[i].name.eq_cstr(name); }
        if (eq) { ServerFn h; unsafe { h = gServerFns[i].handler; } return h; }
        i = i + 1;
    }
    return (ServerFn)0;
}

static struct RpcResult __server_fn_dispatch([]const unsigned char req) {
    struct RpcResult result;
    result.ok = 0;
    result.data = (unsigned char*)0;
    result.len = 0UL;
    result.error_message = String::new();

    struct String reqText;
    reqText = String::from_bytes(req);

    int parseOk;
    struct Value root;
    unsafe { root = json_parse((const char*)reqText.data, &parseOk); }
    unsafe { reqText.free(); }
    if (!parseOk) {
        unsafe { result.error_message = String::from("malformed JSON request"); root.free(); }
        return result;
    }

    struct Value* nameVal; unsafe { nameVal = root.object_get("fn"); }
    if (nameVal == (struct Value*)0) {
        unsafe { result.error_message = String::from("missing 'fn' field"); root.free(); }
        return result;
    }
    const char* fnName; unsafe { fnName = nameVal->as_string(); }
    ServerFn handler = __server_fn_lookup(fnName);
    if (handler == (ServerFn)0) {
        unsafe { result.error_message = String::from("unknown server function"); root.free(); }
        return result;
    }

    struct Value* argsField; unsafe { argsField = root.object_get("args"); }
    struct Value argsVal;
    if (argsField == (struct Value*)0) {
        unsafe { argsVal = Value::null(); }
    } else {
        unsafe { argsVal = argsField->clone(); }
    }

    struct Value resultVal;
    unsafe { resultVal = handler(&argsVal); }
    unsafe { argsVal.free(); }
    unsafe { root.free(); }

    struct String respText;
    unsafe { respText = value_to_json(&resultVal); }
    unsafe { resultVal.free(); }

    unsigned long respLen; unsafe { respLen = respText.len; }
    unsigned char* respBuf;
    unsafe {
        respBuf = (unsigned char*)alloc_raw_(respLen);
        unsigned long i = 0UL;
        while (i < respLen) { respBuf[i] = (unsigned char)respText.data[i]; i = i + 1UL; }
        respText.free();
    }

    result.ok = 1;
    result.data = respBuf;
    result.len = respLen;
    return result;
}

int server_fn_serve(unsigned short port) {
    rpc_register("/fn/call", __server_fn_dispatch);
    return rpc_serve(port);
}

struct Value server_fn_call(const &char host, unsigned short port, const &char name,
                             const ?&Value args, ?&int ok) {
    struct Value envelope;
    unsafe {
        envelope = Value::object();
        envelope.object_set("fn", Value::from_str(name));
        match (args) {
            case some(a): { envelope.object_set("args", a.clone()); }
            case null:    { envelope.object_set("args", Value::null()); }
        }
    }
    struct String reqText;
    unsafe { reqText = value_to_json(&envelope); envelope.free(); }

    unsigned long reqLen; unsafe { reqLen = reqText.len; }
    struct RpcResult rr;
    unsafe { rr = rpc_call(host, port, "/fn/call", ((const unsigned char*)reqText.data)[0..reqLen]); }
    unsafe { reqText.free(); }

    if (!rr.ok) {
        unsafe { *ok = 0; }
        unsafe { rr.free(); }
        return Value::null();
    }

    struct String respText;
    unsafe { respText = String::from_n_((const char*)rr.data, rr.len); }
    unsafe { rr.free(); }

    int parseOk;
    struct Value result;
    unsafe { result = json_parse((const char*)respText.data, &parseOk); respText.free(); }
    unsafe { *ok = parseOk; }
    return result;
}

} // namespace std