std1.0.0-beta-7 SafeC docs

Source rpc/rpc.h

View in the repository

// SafeC Standard Library — RPC over HTTP (gRPC-inspired)
//
// Not wire-compatible with real gRPC: real gRPC requires HTTP/2 (its
// framing and multiplexing model, plus trailer-carried status), which this
// module doesn't implement — std::http (see std/http/http.h) is HTTP/1.1
// only. What IS reused from gRPC: the length-prefixed message envelope (1
// compression-flag byte + 4-byte big-endian length + message bytes) and
// the "/Service/Method" path-routing convention, both carried inside a
// plain HTTP/1.1 POST request/response instead of an HTTP/2 stream. A real
// grpc-go/grpc-python/etc. client or server cannot talk to this one, and
// vice versa. If genuine gRPC wire compatibility is a hard requirement,
// this module isn't it — treat it as a same-shaped, HTTP/1.1-only RPC
// mechanism instead.
//
// Message payloads are opaque bytes as far as this module is concerned —
// serialize/deserialize them yourself, e.g. with std::pb_write_*/
// std::PbReader (see std/serial/protobuf.h) for a real protobuf message,
// or any other format. Builds on std::http (see http.h's backend-selection
// note — a std/sched/io_nb_*.sc backend must be included by the caller
// before this file too).
#pragma once
#include <std/collections/string.h>

namespace std {

struct RpcResult {
    int                    ok;      // 1 = success, 0 = failure (see error_message)
    ?&heap unsigned char   data;    // heap-owned; empty (null) on failure, response bytes on success
    unsigned long          len;
    struct String          error_message; // meaningful when ok == 0
    void drop();

    // The response bytes (data[0..len]); empty when there are none.
    []const unsigned char bytes() const;
    void free() consume;   // = drop()
};

// A server-side method handler: given the raw (already-unframed) request
// bytes, return the raw response bytes to frame and send back. Set
// RpcResult::ok = 0 (with error_message) to report a failure — rpc_serve
// maps that to an HTTP error response rather than a 200.
typedef fn struct RpcResult([]const unsigned char data) RpcMethodHandler;

// ── Client ────────────────────────────────────────────────────────────────

// Calls 'path' (e.g. "/Greeter/SayHello") at host:port with
// 'request_data'. On a transport-level failure (connect, non-200
// response, malformed framing), the returned RpcResult has ok = 0 and
// error_message set; a handler-reported failure (server called
// RpcResult with ok=0) is surfaced the same way, with the handler's own
// error_message forwarded.
struct RpcResult rpc_call(const &char host, unsigned short port, const &char path,
                           []const unsigned char request_data);

// ── Server ────────────────────────────────────────────────────────────────

// Registers 'handler' to be dispatched to for requests whose path exactly
// matches 'path'. Must be called (for every method the service exposes)
// before rpc_serve() — there is no dynamic registration after the accept
// loop starts.
void rpc_register(const &char path, RpcMethodHandler handler);

// Blocking accept loop (like http_serve): for each request, looks up the
// registered handler for its path, unframes the body, calls the handler,
// frames its result, and sends the response back. A path with no
// registered handler gets HTTP 404. Returns -1 immediately if the
// listening socket can't be created; does not return otherwise.
int rpc_serve(unsigned short port);

} // namespace std