Connection takeover: XS_TAKEOVER, the third road
Some business never fits "one question, one answer": SOCKS proxies, upgraded private protocols, long-poll gateways. XS_TAKEOVER lets RequestProc take the whole connection over and run it yourself.
Overview
25.1 Intro
The three states were drawn in chapter 4: XS_OK returns the connection to keep-alive, XS_FALLBACK hands it to the static layer. The third state, XS_TAKEOVER, means "this connection is mine from now on" — the driver steps out of event processing, and everything after (I/O, timeouts, closing) is decided by application logic. Typical scenes: HTTP Upgrade to a private protocol, proxy tunnels, push gateways needing cross-request sessions.
25.2 Takeover semantics
- Takeover point: from the moment RequestProc returns TAKEOVER, the connection is no longer consumed by the HTTP driver
- Event table: after takeover you swap in your own event handling (never an illegal state)
- Reclamation contract: the connection object is still Destroyed by XServer in the end — you operate it but don't own its destruction; the Close terminal also releases the generation lease the connection holds
- Byte synchronization: bytes you consumed/wrote before takeover are still cross-checked by the driver — half an HTTP request + TAKEOVER is rejected as an inconsistency
25.3 Pull mode in practice
func_test carries a real takeover-echo case; the pattern (async pull I/O):
typedef struct TakeoverCtx {
XS_HostInfo* pHost; /* across threads, follow the ownership rules */
xnetstream* tcp; /* or tls: the transport after takeover */
} TakeoverCtx;
static XS_RequestResult RequestProc(XS_HttpReq* pReq)
{
if ( !IsUpgradeTarget(pReq) ) return XS_FALLBACK;
/* 1. first write the protocol-required reply bytes (e.g. 101 Switching Protocols) */
/* 2. build the takeover context */
TakeoverCtx* pCtx = xrtCalloc(1, sizeof(TakeoverCtx));
pCtx->tcp = pReq->tcp;
/* 3. hang an async read: data arrives into the callback (the test case uses a thread + RecvAsync) */
StartTakeoverLoop(pCtx);
return XS_TAKEOVER; /* the connection is mine */
}
What just happened: inside the takeover loop you do I/O on pCtx->tcp with the ordinary xrt stream APIs — exactly as in ch. 23. The only difference is this stream's "origin" was an HTTP connection, and you decide what protocol it speaks next.
25.4 Terminal state & reclamation
- You must eventually Close: takeover is a promise — a forgotten connection sits forever (idle_timeout is a backstop, not the designed path)
- Close uses the stream API's close semantics; XServer Destroys in the connection's terminal callback and releases the generation lease — so an old generation can wait for all taken-over connections to end before reclaiming (reload_matrix verified this ordering)
- The takeover context (pCtx) is freed alongside Close — the same registry-style cleanup as WS/TCP
25.5 Contract
- Takeover: finish the protocol reply bytes before returning TAKEOVER; after takeover the driver never consumes the connection again.
- Terminal: the application performs the final Close; Destroy and lease release are done by XServer at the terminal.
- Context: the takeover context's lifetime binds to the connection terminal — registry-style cleanup.
Common mistakes
- Symptom
- The connection table creeps upward; old generations delay reclamation (leases unreturned).
- Cause
- The takeover promise unfulfilled — every TAKEOVER needs a Close path (including error branches).
- Symptom
- The connection is severed immediately.
- Cause
- The driver cross-checks "bytes the script declared consumed vs the wire" — TAKEOVER with request bytes unconsumed is an inconsistent state. Consume fully, then take over.
Exercises
Implement /raw per 25.3's pattern: after takeover, switch to line-echo mode (as if the protocol "upgraded" from HTTP).
Implement the CONNECT method: after takeover, open a second stream to the target and pump both ways (xrt dual-stream forwarding).
Keep a taken-over connection active, submit a script reload, and verify the old generation waits for the takeover connection to end before reclaiming.
Summary
| Point | Conclusion |
|---|---|
| The third state | XS_TAKEOVER: the driver steps out, the application runs the whole connection |
| Scenarios | protocol upgrades / tunnels / proxies — not a daily business tool |
| Takeover discipline | finish the protocol reply bytes first; consumption checks still apply |
| Terminal discipline | you must eventually Close; Destroy and lease release are XServer's |
| Volume 3 closes | four protocol classes + routing/JSON/templates/vhosts in hand — on to volume 4's hot reload |
- xrt reference · future — completion-notification models for async I/O
- xrt reference · proxy — library-level SOCKS5 / HTTP CONNECT (for the advanced exercise)