Skip to content
SEAMWAREPublic

About

REST server and client library (libmicrohttpd + curl) used by coraine

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

166 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

corRest — REST Server & HTTP Client Library

A compact REST layer for C: an HTTP server built on libmicrohttpd with a flat, verb-indexed service table and fast wildcard URL matching, plus a full HTTP client (blocking, parallel-multi, connection-pooled, TLS). It is the transport layer under the coraine NGSI-LD context broker, but has no NGSI-LD dependency of its own.

Features

  • Service table — register a flat array of (verb, url, routine) services; the library splits them by HTTP method into per-verb vectors at init.
  • Fast URL matching — character-sum hashing and pre-computed wildcard offsets avoid strcmp on the hot path; supports single (*) and greedy (**) wildcards.
  • Per-request state — a thread-local request context (corRest) bound for the duration of a connection.
  • HTTP client — request builder, JSON bodies, per-request timeouts, response header access.
  • Parallel multi-client — fire N requests and harvest them together (used for distributed-operation fan-out).
  • Connection pool + TLS — keep-alive pooling per host and an OpenSSL transport.
  • HTTP proxy (COR_WITH_CLIENT_PROXY) — both engines: https through a CONNECT tunnel, http with the absolute URI; set by the application, from the environment or given.

API reference

The umbrella header pulls in the whole API:

#include "corRest/corRest.h"

Types

// HTTP method — also the index into the per-verb service vectors (CorRestVerb.h)
typedef enum CorRestVerb {
  CorVerbGet = 0, CorVerbPut, CorVerbPost, CorVerbDelete,
  CorVerbPatch, CorVerbHead, CorVerbOptions
} CorRestVerb;

// A service routine returns true on success (CorRestService.h)
typedef bool (*CorRestServiceRoutine)(void);

// One service entry, as authored by the embedding app/plugin
typedef struct CorRestServiceSimplified {
  CorRestVerb            verb;
  const char*           url;             // path pattern, may contain * / **
  CorRestServiceRoutine  serviceRoutine;
  uint64_t              supportedParams; // bitmask of accepted URL params
  uint64_t              ldOp;            // atomic-op bit (0 for non-NGSI-LD routes)
  CorRestServiceRoutine  payloadCheck;    // optional body validator (NULL = none)
} CorRestServiceSimplified;

// URL-param name → bitmask bit (for supportedParams)
typedef struct CorRestParam { const char* name; uint64_t bit; } CorRestParam;

Server

int  corRestInit(CorRestServiceSimplified serviceV[], int services,
                unsigned short port, int poolSize);
void corRestStop(void);

void corRestStateInit(struct MHD_Connection* connection, const char* url, const char* method);
void corRestStateRelease(void);

corRestInit expands the flat service array into per-verb vectors and starts the microhttpd listener on port. corRestStateInit / corRestStateRelease manage the thread-local per-request state.

Client

int  corRestClientInit(int maxIdleConns, int idleTimeoutSec, const char* userAgent);

// Request builder
void corRestClientRequestInit(CorRestClientRequest* req, CorRestVerb verb, const char* url, CorAlloc* allocP);
void corRestClientRequestHeader(CorRestClientRequest* req, const char* name, const char* value);
void corRestClientRequestBody(CorRestClientRequest* req, const char* body, int bodyLen);
void corRestClientRequestTimeout(CorRestClientRequest* req, int connectMs, int requestMs);
int  corRestClientSend(CorRestClientRequest* req, CorRestClientResponse* resp);

const char* corRestClientResponseHeader(CorRestClientResponse* resp, const char* name);

A default per-request timeout is exposed as int corRestClientDefaultRequestTimeoutMs.

A response holds its own copy of what was read: its status text, headers and body stay valid after the connection has gone back to the pool or been closed. The copy comes from the request's allocator and lives as long as it; without an allocator it is malloc'd and released by corRestClientResponseCleanup (corRestClientMultiDestroy for a response of the multi engine). The body is zero-terminated.

Parallel multi-client

CorRestClientMulti*    corRestClientMultiCreate(int capacity);
int                   corRestClientMultiAdd(CorRestClientMulti* multi, CorRestVerb verb, const char* url, /* … */);
int                   corRestClientMultiPerform(CorRestClientMulti* multi, int timeoutMs);
CorRestClientResponse* corRestClientMultiResponse(CorRestClientMulti* multi, int index);
void                  corRestClientMultiDestroy(CorRestClientMulti* multi);

A connection pool (corRestClientPool*) and an OpenSSL TLS transport (corRestClientTls*) sit underneath for keep-alive and https://.

HTTP proxy

bool corRestClientProxyFromEnv(char* errBuf, int errBufLen);   // https_proxy, http_proxy, no_proxy (or upper case)
bool corRestClientProxySet(const char* httpUrl, const char* httpsUrl, const char* noProxy, char* errBuf, int errBufLen);

No proxy is used until the application calls one of them. A proxy is an http:// URL - [user:password@]host[:port], port 1080 by default, the user and password sent as Proxy-Authorization: Basic. An https request goes through a CONNECT host:port tunnel, TLS then to the host itself (its name for SNI and the certificate check); an http request goes to the proxy with its absolute URI. no_proxy: comma-separated - *, host names (a domain covers its hosts: .example.org or example.org), addresses; ports are ignored. A proxy that cannot be reached, or that refuses the tunnel, is the error detail - "Connection to the proxy host:port failed", the proxy's status line. COR_WITH_CLIENT_PROXY=0 builds without it (about 6.7 KB of code less): both engines connect straight to the host, and corRestClientProxy.h's functions are not in the library.

Usage example

#include "corRest/corRest.h"

static bool pingGet(void)
{
  // … build the response via the CorRestOut API …
  return true;
}

static CorRestServiceSimplified services[] =
{
  { CorVerbGet, "/ping",            pingGet, 0, 0, NULL },
  { CorVerbGet, "/things/*",        thingGet, 0, 0, NULL },   // single-wildcard
};

int main(void)
{
  if (corRestInit(services, sizeof(services)/sizeof(services[0]), 1026, 16) != 0)
    return 1;

  // … serve until shutdown …
  corRestStop();
  return 0;
}

Building

make            # build libcorRest.a (+ .so) and the test binary
make ci         # clean + install
make di         # debug + install
make clientLifetimeTest   # the lifetime of a client response, under AddressSanitizer
make COR_WITH_CLIENT_PROXY=0   # without the client's HTTP proxy

libcorRest.a links statically into its consumers. Sibling repos must be present (the build references ../<lib>/lib<lib>.a).

Dependencies

Sibling repos (one .a each):

  • corAlloc — arena allocator (CorAlloc)
  • corTree — the tree (CorNode)
  • corJson — JSON parsing and rendering
  • corBase — core utilities and the library log (COR_LIB_*)
  • corLog — logging and trace levels

System libraries: libmicrohttpd (HTTP server), openssl (ssl/crypto, TLS client), pthread, m.

About

REST server and client library (libmicrohttpd + curl) used by coraine

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages