14 symbols · signatures checked line by line against xsbase.h

Contract Symbols Reference

The host looks up callbacks in the TCC compilation product by symbol name; all are optional exports, declared as needed. The symbol name is the protocol — no registration functions, no base classes, no macros.

The global contract

Views are borrowed

Pointers / views in callback parameters (XS_HttpReq, header structs, message views) are valid only within the callback's stack; values may be carried out, pointers must be copied first.

Unit runs at most once

ServiceUnit executes at most once in a generation's lifetime; a candidate whose initialization never completed will never have Unit mis-called.

Everything is optional

All 14 symbols are exported as needed. A host that does not export RequestProc is a pure static site; a ws host without WsText/WsBinary answers 503.

Threading at a glance: ServiceInit / ServiceSwap run on the assembly or reload control thread; RequestProc / Ws* / Event* run on engine worker threads (possibly concurrent); ServiceUnit runs on the generation GC thread. Synchronize any cross-thread shared state yourself.

Lifecycle callbacks (3)

ServiceInit lifecycle

Called once after this generation's scripts are loaded — the place to build route tables, open databases, initialize static state.

void ServiceInit(XS_HostInfo* pHost);
Timing: before the new generation is published Thread: assembly / reload control Export: optional

A new generation becomes visible only after Init completes; inside Init, take the handover data with xsSwapTake (see the XServer API).

Example · verified on a real machinesource: demo-single/main.c
static void ServiceInit(XS_HostInfo* pHost)
{
    DB_Init();          /* open SQLite, create tables */
    Page_Init();
    Template_Init();
    RouteHTTP_Init();   /* register and compile routes */
}

ServiceUnit lifecycle

The destructor when this generation retires — releases in reverse order of Init. Runs at most once in the whole lifetime.

void ServiceUnit(XS_HostInfo* pHost);
Timing: after the old generation's references hit zero (GC thread) Guarantee: at most once Export: optional

Two trigger points: the process's graceful-shutdown path, or the return of the old generation's last lease after a hot reload. A generation whose Init never completed will not have Unit called.

Example · verified on a real machinesource: demo-single/main.c (reverse-order release)
static void ServiceUnit(XS_HostInfo* pHost)
{
    RouteHTTP_Unit();   /* free patterns and the route table */
    Template_Unit();
    Page_Unit();
    DB_Unit();          /* close the database */
}

ServiceSwap lifecycle

Before a generation swap, export a non-destructive snapshot of cross-generation state; the new generation's ServiceInit takes it back via xsSwapTake.

bool ServiceSwap(XS_HostInfo* pHost, xvalue** ppShared);
Timing: after crossing the irreversible commit point Returns: true = snapshot valid, ready to submit Export: optional
ParameterDirectionNotes
pHostinputthe retiring old-generation host
ppSharedoutputreceives the xvalue* snapshot; ownership transfers through the host to the new generation

What non-destructive means: after exporting the snapshot, the old generation may keep serving (in extreme cases the commit can still be cancelled) — do not close resources inside Swap.

Example · the contract pattern
static bool ServiceSwap(XS_HostInfo* pHost, xvalue** ppShared)
{
    xvalue* pState = xrtValueObject();
    xrtValueObjectSetNew(pState, XRT_STR_LITERAL("counter"),
        xrtValueInt64(G_iCounter));
    *ppShared = pState;
    return true;
}

HTTP callback (1)

RequestProc HTTP

The entry point of every HTTP request. Its three-way return declares "who owns the rest of this request's life".

XS_RequestResult RequestProc(XS_HttpReq* pReq);
Timing: after the request headers are parsed Thread: engine worker (may run concurrently) Export: optional (absent = pure static host)
Return valueSemanticsWhat the driver does next
XS_OK (0)the full response is writtenconsume the remaining body bytes, continue keep-alive
XS_FALLBACK (1)unhandledGET/HEAD falls to the static layer, the rest gets 404
XS_TAKEOVER (2)take over the whole connectionthe driver withdraws; the application sends/receives on its own and finally Closes

XS_HttpReq is a view struct: exactly one of tcp/tls is non-null (the full xrt stream API is available), head is the parsed request header (with MethodCode), body is the framing state machine, host/server the owners. Field by field in the structs reference.

Example · verified on a real machinesource: demo-single/route_http/basic.h
/* the demo's routing frameworks all converge on RequestProc */
static XS_RequestResult RequestProc(XS_HttpReq* pReq)
{
    if ( RouteHTTP_Dispatch(pReq) ) return XS_OK;
    return XS_FALLBACK;   /* no match -> wwwroot */
}

WebSocket callbacks (6)

XServer performs the handshake validation and xrtWsStreamAttach; masking, fragment reassembly, control frames, and permessage-deflate are all handled in the xrt layer. Sending uses the xrtWsStreamText / Binary families.

WsOpen WebSocket

Fires when the connection is established (101 done, stream attached).

void WsOpen(XS_HostInfo* pHost, xwsstream* pWs);
Thread: workerExport: optional
Example
static void WsOpen(XS_HostInfo* pHost, xwsstream* pWs)
{
    (void)xrtWsStreamText(pWs, xrtStrView("connected"));
}

WsText WebSocket

Fires after a whole text message is reassembled; tText is a view valid within this callback.

void WsText(XS_HostInfo* pHost, xwsstream* pWs, xstrview tText);
Thread: workerIf not exported: the host answers 503
Example · echo
static void WsText(XS_HostInfo* pHost, xwsstream* pWs, xstrview tText)
{
    (void)xrtWsStreamText(pWs, tText);
}

WsBinary WebSocket

Fires for a whole binary message; exceeding ws_message_limit cumulatively ends the connection with close 1009.

void WsBinary(XS_HostInfo* pHost, xwsstream* pWs, xbytesview tData);
Thread: workerIf not exported: the host answers 503

WsPing WebSocket

A Ping frame arrived. The automatic Pong reply is handled in the xrt layer — this callback is for business observation (heartbeat stats and the like).

void WsPing(XS_HostInfo* pHost, xwsstream* pWs, xbytesview tData);
Thread: workerExport: optional

WsPong WebSocket

A Pong frame arrived — the place to measure round-trip latency.

void WsPong(XS_HostInfo* pHost, xwsstream* pWs, xbytesview tData);
Thread: workerExport: optional

WsClose WebSocket

The connection closed; carries the status code and a view of up to 123 bytes of UTF-8 reason.

void WsClose(XS_HostInfo* pHost, xwsstream* pWs, uint16 iCode, xstrview tReason);
Thread: workerExport: optional
Common iCodeMeaning
1000normal closure
1009message over the limit (ws_message_limit)
1011server-internal error (e.g. OOM)

TCP stream callbacks (3)

XS_StreamConn is a view struct: exactly one of tcp / tls is non-null; use the matching send API on the non-null side (under tcps, bypassing tls to write tcp directly is forbidden). The connection's lifetime is held by the host — the application never calls Destroy.

EventOpen TCP

Fires after a stream connection is established (the moment to send a welcome banner).

void EventOpen(XS_HostInfo* pHost, XS_StreamConn* pConn);
Thread: workerExport: optional
Example · banner
static void EventOpen(XS_HostInfo* pHost, XS_StreamConn* pConn)
{
    if ( pConn->tcp != NULL )
        (void)xrtNetStreamSend(pConn->tcp, "hello\n", 6);
}

EventData TCP

Data arrived. pBuffer is a zero-copy buffer chain (borrowed within the callback); query its size with xrtNetBufSize, consume the data through the xrt buffer APIs.

void EventData(XS_HostInfo* pHost, XS_StreamConn* pConn, xnetbuf* pBuffer);
Thread: workerBorrowing: pBuffer is valid only within the callback

EventClose TCP

The connection-closed terminal state; includes the network result code and an optional error object (borrowed).

void EventClose(XS_HostInfo* pHost, XS_StreamConn* pConn, xnetresult iResult, const xerror* pError);
Thread: workerResources: the host destroys the connection

UDP callback (1)

EventDgram UDP

Fires once per datagram; there is no connection state — every packet takes the current generation's lease through the listener slot.

void EventDgram(XS_HostInfo* pHost, xnetudp* pUdp, const xnetudpmessage* pMsg);
Thread: workerBorrowing: pMsg is valid only within the callback
pMsg fieldNotes
Remotethe source address (convert to text with the xrtNetAddr family)
Data / Sizepayload and length
FlagsXNET_UDP_MESSAGE_TRUNCATED and friends
Example · echo back to the source
static void EventDgram(XS_HostInfo* pHost, xnetudp* pUdp,
    const xnetudpmessage* pMsg)
{
    (void)xrtNetUdpSendTo(pUdp, &pMsg->Remote, "ack", 3);
}