Chapter 14 / 51
Volume 3 · Protocol Development

HTTP basics: the view struct and the three-way return

What RequestProc receives is not a wrapped object but a set of native xrt pointers you can act on directly. This chapter completes the map of that view — the next four chapters all work on it.

Overview

Positioning: the map chapter of volume 3. ch15 (responses), ch16 (bodies) and ch17 (routing) each unfold a different side of the view; this chapter first builds the whole picture plus the two iron rules (borrowing, three-way return).

14.1 Intro

Writing HTTP business code needs three things: knowing what the request is (method, path, headers, body), knowing where to write the response (the transport), and knowing who takes over after you. Most frameworks knead all three into a Request/Response object, trading abstraction for convenience; XServer hands you the three things apart — the six fields of XS_HttpReq are their addresses.

14.2 XS_HttpReq field by field

XS_HttpReq (xsbase.h, verbatim)source: src/sdk/xsbase.h
typedef struct {
    xnetstream*       tcp;    /* exactly one of tcp/tls non-null: raw transport */
    xtlsstream*       tls;    /*   the full xrt stream/send API applies      */
    const xhttp1head* head;   /* incl. MethodCode; view valid in the callback */
    xhttp1body*       body;   /* framing machine: fixed / chunked / close     */
    const XS_HostInfo*  host;
    XS_ServerInfo*    server;
} XS_HttpReq;
FieldPurposeUse in this chapter
tcp / tlsresponse outletsending response bytes (ch15)
headrequest identityMethodCode, path, field views (14.3)
bodyrequest bodyincremental reading (ch16)
host / serverownershiptopology info, Custom knobs

Why is there no Response field? — because the response is not an object; it is the byte stream you write to the transport. That is "zero wrapping" taken to its conclusion: no hidden response buffer, and therefore no "when does the framework flush" class of problems.

14.3 Reading request headers

Method, path and fields
static XS_RequestResult RequestProc(XS_HttpReq* pReq)
{
    /* method: a bit mask — single method and set element at once (ref-enums) */
    if ( pReq->head->MethodCode == XHTTP_METHOD_GET ) { ... }

    /* path and field views: xstrview = pointer + length, valid in the callback */
    /* the concrete accessors (field lookup / path splitting) are in xrt · http1 */
    return XS_FALLBACK;
}

Two iron rules for reading the head: first, the field views of head are valid only within this callback's stack — XServer copies the request head in the PinHead phase to prevent ring-buffer reuse, but it still expires after the generation swap / request ends; second, never compare paths with C string functions — views carry lengths, compare by length (the xrtStrView tooling).

14.4 Timing & threading

connection established → request head parsed → RequestProc (worker thread, concurrent) │ ┌────────────────────┼────────────────────┐ ▼ XS_OK ▼ XS_FALLBACK ▼ XS_TAKEOVER driver drains the body GET/HEAD → static app takes the connection continue keep-alive else → 404 (ch25)
  • Once per request; called per request on keep-alive connections
  • workers concurrency — RequestProc must be reentrant (no cross-request mutable statics, or lock yourself)
  • A byte-consumption cross-check runs between driver and script: a mismatch between your declared consumption and the wire severs the connection — the script is treated as an untrusted boundary

14.5 Contract

  • Borrowing: all six fields are valid inside the callback; values may be copied out, pointers may not.
  • Three-way: finish the response before XS_OK; one byte written means no FALLBACK; after TAKEOVER the driver steps out.
  • Concurrency: RequestProc runs concurrently on workers — reentrancy is a hard requirement.

Common mistakes

Comparing path views with strcmp
Symptom
Sporadic wrong path decisions, especially with query strings or non-NUL-terminated buffers.
Cause
An xstrview is not a C string — it may carry no NUL terminator; the length is authoritative.
✗ Wrong
if ( strcmp(pPath, "/api") == 0 )
✓ Right
/* compare by view length (xrtStrView tools) */
Cross-request mutable statics inside RequestProc
Symptom
Garbled counts under concurrency, sporadic crashes.
Cause
Forgetting workers concurrency — statics read and written by several requests at once.

Exercises

BasicA method triage desk

Write a RequestProc: GET prints "GET", POST prints "POST", everything else returns XS_FALLBACK.

Acceptance: three methods tested; logs and fallback behavior match.
AdvancedA concurrency-safe counter

Count processed requests (hint: atomics, the xrtAtomic family).

Acceptance: after 10k requests the count is exactly 10k — no losses, no tearing.
ChallengeProving view lifetime

Store a head view pointer in a global and read it on the next request — observe and explain; then switch to copying inside the callback.

Acceptance: you can explain the crash or corruption via chapter 3's generation/borrowing model.

Summary

PointConclusion
View structtcp/tls (outlet) + head (identity) + body (payload) + host/server (ownership)
No Response objectthe response is a byte stream into the transport (ch15)
Header rulesviews valid in the callback; compare by length, never as C strings
Concurrencyreentrant on workers; consumption cross-checked against the wire
xrt further reading