Introduction
The httpserver plugin provides a simple, callback-based HTTP server: `Open` binds a TCP port and forwards every incoming HTTP request to a script-level callback. The callback receives the RAW HTTP request bytes (the request line, headers, and body) and returns a raw HTTP response blob — the plugin sends it straight back to the client. Request parsing and response building are the script's job (or a helper plugin's), so the solution is minimal but fully flexible.
Relationship to asyncmeta
The plugin is fully built on the asyncmeta constants. `Capabilities()` returns exactly the `asyncmeta.ProfileRouteControl()` bitmask (=2442): WaitDrained | RouteMeta | RouteInfo | RouteClose | Known. `RouteState` returns `asyncmeta.RouteStateOpen` (=1) / `RouteStateClosed` (=0), and `RouteMeta(listenerId, key)` knows the `asyncmeta.RouteMeta*` keys (State, Timeout, Pending, Queued, Running, Drained, Known, CloseAccepted, ClosedQueued).
One listener = one route
It is important to understand the plugin's design choice: httpserver is NOT a URL router. Every `Open` call creates a single listener with a single route (= a single callback). Incoming HTTP request URLs are NOT resolved by the plugin — the entire raw request reaches the callback, and the callback decides what to do (path-based routing, static file, REST handler, etc.). For multiple route handlers, open several listeners on different ports, or branch in a single callback on the request line.
Two bind modes
The fourth (optional) argument of `Open` is the bind mode. `BindLocalOnly` (=0): listens only on the loopback (127.0.0.1) interface, safe for sandbox development and testing. `BindAny` (=1): listens on every interface (0.0.0.0), reachable from other hosts on the network — use it only when you intentionally publish a service. The default is `BindLocalOnly` if the argument is not provided.
Peer metadata: the extended callback forms
The handler may declare extra parameters, and the host fills them in with the client's identity — useful for access logs, IP-based filtering, or mTLS-based authorization:
callback i32 NAME(ref blob $Request)— payload only.callback i32 NAME(ref blob $Request, string $PeerHost, i32 $PeerPort)— plus the client's address and port.callback i32 NAME(ref blob $Request, string $PeerHost, i32 $PeerPort, string $PeerCertSubject)— plus the client certificate's subject when the connection is mTLS-verified (empty when there is no TLS, or when an optional-mode client presented no certificate).
The same forms work on the TCP and UDP callback families, so a handler moves between transports unchanged.
Loading the plugin
plugin "builtin:print"; plugin "../plugins/async_meta/AsyncMetaPlugin"; plugin "../plugins/http_server/HttpServerPlugin";
A typical flow — minimal HTTP server
callback i32 OnRequest(ref blob $Request) { // The callback receives the RAW HTTP request as a blob. // Modify the blob with the response, or return a new blob. return(1); } i32 main() { i64 $lid; $lid = httpserver.Open("OnRequest", -1, 8080, httpserver.BindLocalOnly()); // ... the server runs on the accept thread ... httpserver.Close($lid); return(0); }
Two-step shutdown: RouteClose, then Close
The plugin offers two shutdown phases. (1) `RouteClose(listenerId)` signals the route is to be closed: no new requests, but already running callbacks finish. `WaitRouteDrained(listenerId, timeoutMs)` can then be awaited. (2) `Close(listenerId)` is the full listener (TCP socket + accept thread) shutdown. In simpler cases just `Close` is enough — it implicitly performs the former too.
Listener- and route-level queries
Many query functions in the plugin distinguish two levels. The LISTENER level (Port, BindMode/Text, State/Text, Active, Pending, Queued, Running, Drained, WaitDrained, Info, OpenCount) describes the TCP socket and active/queued requests. The ROUTE level (RouteState/Text, RouteCallback, RouteTimeout, RouteKnown, RouteMeta, RouteInfo, RoutePending, RouteQueued, RouteRunning, RouteDrained, WaitRouteDrained, RouteCloseAccepted, RouteClosedQueued, RouteCloseInfo, RouteClose) works in the async meta model and uses asyncmeta constants for its key codes. With one listener, both levels show identical states; the distinction matters for the shared vocabulary with multi-route listeners (TCP/general).
What to know about every function (the basics)
The callback receives RAW HTTP request bytes as a blob; the response is a RAW HTTP response blob the plugin writes back to the socket directly.
`BindMode` codes: 0 = BindLocalOnly (default), 1 = BindAny. `BindModeText` in text: “local” or “any”.
State / RouteState return asyncmeta.RouteStateOpen (=1) / RouteStateClosed (=0); the *Text counterparts in text.
WaitDrained and WaitRouteDrained return 1 if drained occurs before the timeoutMs expires; 0 if it expired.
RouteClose closes the route layer; `RouteCloseAccepted` becomes 1 afterward. `Close` shuts down the whole listener (TCP socket + thread).
Two-step shutdown rule: if you first call RouteClose successfully (1), then Close, Close returns 0 (because the listener is no longer in active state when Close is called — the route layer was already closed).
port = 0 makes the OS pick the port; the actual port is queried with `Port($lid)`.
An error (stopping the script) is caused by a wrong argument count or type, an out-of-range port (0..65535) or timeoutMs, an invalid bind mode, an already-closed listener, a `bind`/`listen` failure, and a thread startup failure.
How to read the signatures
callbackName is the script-level request callback name (`callback i32 NAME(ref blob $X)`). timeoutMs is the callback's run-time limit in ms; -1 = infinite. port is the TCP port (0..65535; 0 = OS picks). bindMode is the integer returned by `httpserver.BindLocalOnly()` or `httpserver.BindAny()`. listenerId is the positive id given by Open. metaKey is one of the asyncmeta.RouteMeta* values. The type after the -> arrow is the return type.
Lifecycle and bind configuration
Plugin capability query, bind-mode constants, and listener open/close.
httpserver.Capabilities
httpserver.Capabilities() -> intReturns the listener's capability bitmask matching the asyncmeta constants.
This function takes no arguments.
asyncmeta.ProfileRouteControl (=2442): WaitDrained | RouteMeta | RouteInfo | RouteClose | Known.
printf("%d\n", httpserver.Capabilities()); printf("%d\n", (httpserver.Capabilities() == asyncmeta.ProfileRouteControl()));
2442 1
For backend capability checking with the Supports* helpers. Worker plugins (e.g. worker = 3701) and listener plugins (httpserver = 2442) return different bitmasks — so code can adapt automatically.
httpserver.BindLocalOnly
httpserver.BindLocalOnly() -> intReturns the constant value of the “loopback only” bind mode.
This function takes no arguments.
0.
printf("%d\n", httpserver.BindLocalOnly());
0
For the fourth argument of Open, when you want a safe local server (sandbox development, test environment). This is the default mode if the bindMode argument is not given.
httpserver.BindAny
httpserver.BindAny() -> intReturns the constant value of the “any interface” bind mode.
This function takes no arguments.
1.
printf("%d\n", httpserver.BindAny());
1
When the HTTP server should be reachable from other hosts (public service). Use it only as a conscious choice for security reasons — the loopback-only mode is safer by default.
httpserver.OpenTls
httpserver.OpenTls(certFile, keyFile, callbackName, timeoutMs, port?, bindMode?) -> listener idOpens an HTTPS listener in a SINGLE call. The listener is TLS from birth — unlike an Open + EnableTls pair, no plaintext window ever exists. The certificate, the key and the TLS provider are pre-flight-checked at open time, so a bad path fails here with a clean, script-visible error instead of silently dropping connections later.
| Parameter | Type | Description |
|---|---|---|
| certFile | string | Path to the server certificate chain (PEM). |
| keyFile | string | Path to the matching private key (PEM). |
| callbackName | string | The script-level callback to dispatch requests to. |
| timeoutMs | int | The callback's run-time limit in milliseconds. |
| port | int | Optional. 0 (or omitted) lets the OS pick; read it back with Port. |
| bindMode | int | Optional. BindLocalOnly (0, default) or BindAny (1). |
Returns a positive listener id, or a negative value on failure.
The TLS layer lives in the tls plugin, which this plugin loads on demand — the script needs no tls import of its own.
httpserver.EnableTls
httpserver.EnableTls(listenerId, certFile, keyFile) -> 0
Upgrades an already-open listener: connections accepted from then on are TLS-wrapped. The exact counterpart of tcpserver.EnableTls. Prefer OpenTls when you can — it leaves no plaintext window between Open and the upgrade.
| Parameter | Type | Description |
|---|---|---|
| listenerId | int | The listener id returned by Open. |
| certFile | string | Path to the server certificate chain (PEM). |
| keyFile | string | Path to the matching private key (PEM). |
Returns 0 on success.
httpserver.SetClientCa
httpserver.SetClientCa(listenerId, caFile, mode) -> 0
Turns on mTLS: requires (mode 2) or accepts (mode 1) a client certificate signed by the given CA. Call it BEFORE EnableTls / OpenTls-accepted connections — the TLS objects inherit the mode. With mTLS on, the 4-parameter callback form receives the verified client's certificate subject.
| Parameter | Type | Description |
|---|---|---|
| listenerId | int | The listener id. |
| caFile | string | Path to the CA bundle (PEM) that client certificates are verified against. |
| mode | int | 1 = optional (accept a certificate if offered), 2 = required (reject clients without a valid one). |
Returns 0 on success.
httpserver.Open
httpserver.Open(callbackName, timeoutMs, port?, bindMode?) ->
intOpens an HTTP listener on a TCP port and forwards incoming requests to the callback via an accept thread.
| Parameter | Type | Description |
|---|---|---|
| callbackName | string | The script-level callback name to call. Signature: `callback i32 NAME(ref blob $X)`. |
| timeoutMs | int | The callback's run-time limit in ms. -1 = infinite; otherwise 0..2147483647. |
| port | int | Optional TCP port (0..65535). 0 lets the OS pick. Default: 0. |
| bindMode | int | Optional bind mode: httpserver.BindLocalOnly() (0, default) or httpserver.BindAny() (1). |
The listenerId (positive i64). A bad argument, a bind failure, or a thread startup failure raises a runtime error.
callback i32 OnRequest(ref blob $X) { return(1); } i64 $lid; $lid = httpserver.Open("OnRequest", -1, 0, httpserver.BindLocalOnly()); printf("%d\n", ($lid > 0));
1
For starting a local HTTP service (debug server, local REST API, static file server). The 0-port is handy in CI/test environments. The returned listenerId is the input of every further operation.
httpserver.Close
httpserver.Close(listenerId) -> intCloses the listener: stops the accept thread, shuts down the socket, and releases resources.
| Parameter | Type | Description |
|---|---|---|
| listenerId | int | The positive listenerId from Open. |
1 if the listener was open and closed successfully; 0 if it was already closed (e.g. after a prior RouteClose). An unknown or non-positive listenerId raises a runtime error.
i64 $lid; $lid = httpserver.Open("OnRequest", -1, 0); printf("%d\n", httpserver.Close($lid));
1
For clean listener shutdown at the script's end. Close waits for in-progress callbacks to finish, so it gives a clean-state shutdown. If you called RouteClose first, Close returns 0 (the listener was no longer active).
Listener-level state
Current state of the active listener: bound port and mode, “open/closed” state, and a plugin-level OpenCount.
httpserver.Port
httpserver.Port(listenerId) -> intReturns the listener's actual, bound TCP port.
| Parameter | Type | Description |
|---|---|---|
| listenerId | int | The listener id. |
The port (1..65535).
i64 $lid; $lid = httpserver.Open("OnRequest", -1, 0); printf("%d\n", (httpserver.Port($lid) > 0));
1
Particularly useful when you passed port=0 to Open (asking for an OS-picked port). You query the returned port number with this to advertise to clients.
httpserver.BindMode
httpserver.BindMode(listenerId) -> intReturns the listener's bind-mode constant.
| Parameter | Type | Description |
|---|---|---|
| listenerId | int | The listener id. |
httpserver.BindLocalOnly() (=0) or httpserver.BindAny() (=1).
i64 $lid; $lid = httpserver.Open("OnRequest", -1, 0, httpserver.BindLocalOnly()); printf("%d\n", httpserver.BindMode($lid));
0
For programmatic bind-mode checking: compare against BindLocalOnly() / BindAny(). Useful for logging or security audits.
httpserver.BindModeText
httpserver.BindModeText(listenerId) -> string
Returns the bind mode in human-readable text.
| Parameter | Type | Description |
|---|---|---|
| listenerId | int | The listener id. |
“local” for BindLocalOnly; “any” for BindAny.
i64 $lid; $lid = httpserver.Open("OnRequest", -1, 0, httpserver.BindLocalOnly()); print.Line(httpserver.BindModeText($lid));
local
For logging, developer UI. Textual counterpart of BindMode.
httpserver.State
httpserver.State(listenerId) -> intReturns the listener's state using the asyncmeta route-state constants.
| Parameter | Type | Description |
|---|---|---|
| listenerId | int | The listener id. |
asyncmeta.RouteStateOpen (=1) if open; asyncmeta.RouteStateClosed (=0) if closed.
i64 $lid; $lid = httpserver.Open("OnRequest", -1, 0); printf("%d\n", httpserver.State($lid));
1
For programmatic state checking. Shared vocabulary with the callback_* family: comparing with asyncmeta.RouteStateOpen makes the code uniform and portable.
httpserver.StateText
httpserver.StateText(listenerId) -> string
Returns the listener's state as text.
| Parameter | Type | Description |
|---|---|---|
| listenerId | int | The listener id. |
“open” if open; “closed” if closed.
i64 $lid; $lid = httpserver.Open("OnRequest", -1, 0); print.Line(httpserver.StateText($lid));
open
For logging or UI display. Textual counterpart of State's numeric value.
httpserver.OpenCount
httpserver.OpenCount() -> intReturns the number of currently open HTTP listeners (plugin-level, global view).
This function takes no arguments.
The number of open listeners (0 if none).
printf("%d\n", httpserver.OpenCount()); i64 $lid; $lid = httpserver.Open("OnRequest", -1, 0); printf("%d\n", httpserver.OpenCount()); httpserver.Close($lid); printf("%d\n", httpserver.OpenCount());
0 1 0
For resource control and diagnostics: if it does not return to 0 after Close, it signals a resource leak.
Listener-level traffic
Current traffic metrics of the listener: active, pending, queued, running requests; drain state and blocking wait.
httpserver.Active
httpserver.Active(listenerId) -> intReturns the number of requests currently being processed.
| Parameter | Type | Description |
|---|---|---|
| listenerId | int | The listener id. |
The number of active requests (>= 0).
i64 $lid; $lid = httpserver.Open("OnRequest", -1, 0); printf("%d\n", httpserver.Active($lid));
0
For monitoring the server's current load. 0 with no traffic; a high value may signal congestion.
httpserver.Pending
httpserver.Pending(listenerId) -> intReturns the number of pending (not yet started) requests.
| Parameter | Type | Description |
|---|---|---|
| listenerId | int | The listener id. |
The number of pending requests.
i64 $lid; $lid = httpserver.Open("OnRequest", -1, 0); printf("%d\n", httpserver.Pending($lid));
0
An early signal of input congestion. A high Pending shows that callbacks are slow and requests pile up.
httpserver.Queued
httpserver.Queued(listenerId) -> intReturns the number of queued requests.
| Parameter | Type | Description |
|---|---|---|
| listenerId | int | The listener id. |
The number of queued requests.
i64 $lid; $lid = httpserver.Open("OnRequest", -1, 0); printf("%d\n", httpserver.Queued($lid));
0
For monitoring the internal queue length. Pending + Queued together give the full waiting picture.
httpserver.Running
httpserver.Running(listenerId) -> intReturns the number of currently running requests (in-callback).
| Parameter | Type | Description |
|---|---|---|
| listenerId | int | The listener id. |
The number of running requests.
i64 $lid; $lid = httpserver.Open("OnRequest", -1, 0); printf("%d\n", httpserver.Running($lid));
0
For monitoring parallelism: if too high, look at callback performance or lower timeoutMs.
httpserver.Drained
httpserver.Drained(listenerId) -> intTells whether the listener is “drained” (no active, pending, queued, or running request).
| Parameter | Type | Description |
|---|---|---|
| listenerId | int | The listener id. |
1 if drained; 0 if something is in progress.
i64 $lid; $lid = httpserver.Open("OnRequest", -1, 0); printf("%d\n", httpserver.Drained($lid));
1
Before clean shutdown: ensure Drained is 1 before calling Close. With traffic, WaitDrained blocks for this.
httpserver.WaitDrained
httpserver.WaitDrained(listenerId, timeoutMs) -> intBlocks until the listener is fully drained, or the timeoutMs expires.
| Parameter | Type | Description |
|---|---|---|
| listenerId | int | The listener id. |
| timeoutMs | int | Maximum wait in ms; -1 = infinite. |
1 if drain occurred before timeoutMs expired; 0 if it expired.
i64 $lid; $lid = httpserver.Open("OnRequest", -1, 0); printf("%d\n", httpserver.WaitDrained($lid, 50));
1
For a simple, correct shutdown pattern: call this after RouteClose so every in-progress callback can finish. On expired timeout, you can decide on a forced shutdown via Close.
httpserver.Info
httpserver.Info(listenerId) -> string
Returns the listener's aggregated key=value diagnostic state as a string.
| Parameter | Type | Description |
|---|---|---|
| listenerId | int | The listener id. |
A space-separated key=value list (id, state, callback, timeout, port, bind, bindCode, active).
i64 $lid; $lid = httpserver.Open("OnRequest", -1, 0); print.Line(httpserver.Info($lid));
id=1 state=open callback=OnRequest timeout=-1 port=<auto> bind=local bindCode=0 active=0
For detailed diagnostics in a single call. The keys are stable; easy to parse with str.* operations.
Route-level state
The state of the listener's route layer, in the asyncmeta vocabulary. One listener has one route; the Route* verbs use the async meta model's terminology.
httpserver.RouteState
httpserver.RouteState(listenerId) -> intReturns the route's state using the asyncmeta route-state constants.
| Parameter | Type | Description |
|---|---|---|
| listenerId | int | The listener id. |
asyncmeta.RouteStateOpen (=1) or RouteStateClosed (=0).
i64 $lid; $lid = httpserver.Open("OnRequest", -1, 0); printf("%d\n", httpserver.RouteState($lid));
1
Value equivalent to State (one listener, one route), but using the asyncmeta terminology in the Route* family. Use it when you want code uniform with other route-controlled backends.
httpserver.RouteStateText
httpserver.RouteStateText(listenerId) -> string
Returns the route's state in text.
| Parameter | Type | Description |
|---|---|---|
| listenerId | int | The listener id. |
“open” or “closed”.
i64 $lid; $lid = httpserver.Open("OnRequest", -1, 0); print.Line(httpserver.RouteStateText($lid));
open
For logging or UI display in the Route* family.
httpserver.RouteCallback
httpserver.RouteCallback(listenerId) -> string
Returns the callback name passed at Open.
| Parameter | Type | Description |
|---|---|---|
| listenerId | int | The listener id. |
The callback name as text.
i64 $lid; $lid = httpserver.Open("OnRequest", -1, 0); print.Line(httpserver.RouteCallback($lid));
OnRequest
For diagnostics or reflectivity: confirming the callback name bound to the listener, e.g. in log messages.
httpserver.RouteTimeout
httpserver.RouteTimeout(listenerId) -> intReturns the callback's run-time max value in milliseconds.
| Parameter | Type | Description |
|---|---|---|
| listenerId | int | The listener id. |
The timeoutMs (the value passed at Open); -1 for infinite wait.
i64 $lid; $lid = httpserver.Open("OnRequest", -1, 0); printf("%d\n", httpserver.RouteTimeout($lid));
-1
Reflective query of configuration. -1 means infinite wait — no forced shutdown time limit for the callback.
httpserver.RouteKnown
httpserver.RouteKnown(listenerId) -> intTells whether the route bound to the listener is known (= the listener is open).
| Parameter | Type | Description |
|---|---|---|
| listenerId | int | The listener id. |
1 if known; 0 if the listener is already closed or unknown.
i64 $lid; $lid = httpserver.Open("OnRequest", -1, 0); printf("%d\n", httpserver.RouteKnown($lid));
1
Defensive check before a Route* query. A 0 signals the listener is no longer active.
httpserver.RouteMeta
httpserver.RouteMeta(listenerId, metaKey) -> intReturns a specific meta field of the route by asyncmeta.RouteMeta* keys.
| Parameter | Type | Description |
|---|---|---|
| listenerId | int | The listener id. |
| metaKey | int | An asyncmeta.RouteMeta* value (State=1, Timeout=2, Pending=3, Queued=4, Running=5, Drained=6, Known=7, CloseAccepted=8, ClosedQueued=9). |
The value matching the field (see the asyncmeta documentation).
i64 $lid; $lid = httpserver.Open("OnRequest", -1, 0); printf("%d\n", httpserver.RouteMeta($lid, asyncmeta.RouteMetaState())); printf("%d\n", httpserver.RouteMeta($lid, asyncmeta.RouteMetaDrained()));
1 1
One uniform query on the route's state — with the exact keys from the asyncmeta vocabulary. If your code is portable across several backends, this form is the portable query.
httpserver.RouteInfo
httpserver.RouteInfo(listenerId) -> string
Returns the route's aggregated key=value diagnostic state as a string.
| Parameter | Type | Description |
|---|---|---|
| listenerId | int | The listener id. |
A space-separated key=value list (id, state, stateCode, callback, timeout, port, bind, bindCode, drained, pending, queued, running, active).
i64 $lid; $lid = httpserver.Open("OnRequest", -1, 0); printf("%d\n", (str.Len(httpserver.RouteInfo($lid)) > 0));
1
For a complete route state snapshot in one call. Broader than Info — also includes the traffic metrics.
Route-level traffic and shutdown
Traffic measured at the route layer (equivalent to the listener level, only with the asyncmeta terminology), and the route shutdown process.
httpserver.RoutePending
httpserver.RoutePending(listenerId) -> intReturns the number of route-level pending requests.
| Parameter | Type | Description |
|---|---|---|
| listenerId | int | The listener id. |
The number of pending requests.
i64 $lid; $lid = httpserver.Open("OnRequest", -1, 0); printf("%d\n", httpserver.RoutePending($lid));
0
Equivalent to listener-level Pending (with one route). The Route* family follows the asyncmeta terminology.
httpserver.RouteQueued
httpserver.RouteQueued(listenerId) -> intReturns the number of route-level queued requests.
| Parameter | Type | Description |
|---|---|---|
| listenerId | int | The listener id. |
The number of queued requests.
i64 $lid; $lid = httpserver.Open("OnRequest", -1, 0); printf("%d\n", httpserver.RouteQueued($lid));
0
Equivalent to listener-level Queued. For monitoring congestion.
httpserver.RouteRunning
httpserver.RouteRunning(listenerId) -> intReturns the number of route-level currently running requests.
| Parameter | Type | Description |
|---|---|---|
| listenerId | int | The listener id. |
The number of running requests.
i64 $lid; $lid = httpserver.Open("OnRequest", -1, 0); printf("%d\n", httpserver.RouteRunning($lid));
0
Equivalent to listener-level Running. For monitoring parallelism.
httpserver.RouteDrained
httpserver.RouteDrained(listenerId) -> intTells whether the route is “drained” (pending = queued = running = 0).
| Parameter | Type | Description |
|---|---|---|
| listenerId | int | The listener id. |
1 if drained; 0 if something is in progress.
i64 $lid; $lid = httpserver.Open("OnRequest", -1, 0); printf("%d\n", httpserver.RouteDrained($lid));
1
Equivalent to listener-level Drained. For pre-shutdown clean-state verification.
httpserver.WaitRouteDrained
httpserver.WaitRouteDrained(listenerId, timeoutMs) -> intBlocks until the route is fully drained, or the timeoutMs expires.
| Parameter | Type | Description |
|---|---|---|
| listenerId | int | The listener id. |
| timeoutMs | int | Maximum wait in ms; -1 = infinite. |
1 if drain occurred before timeoutMs expired; 0 if it expired.
i64 $lid; $lid = httpserver.Open("OnRequest", -1, 0); printf("%d\n", httpserver.WaitRouteDrained($lid, 50));
1
Route-level counterpart of WaitDrained. In the two-phase shutdown pattern, call AFTER RouteClose, before fully stopping with Close.
httpserver.RouteCloseAccepted
httpserver.RouteCloseAccepted(listenerId) -> intTells whether the RouteClose request has been accepted.
| Parameter | Type | Description |
|---|---|---|
| listenerId | int | The listener id. |
1 if yes; 0 if no (no RouteClose yet, or rejected).
i64 $lid; $lid = httpserver.Open("OnRequest", -1, 0); httpserver.RouteClose($lid); printf("%d\n", httpserver.RouteCloseAccepted($lid));
1
For confirming the two-phase shutdown: RouteClose returned 1, and this also returns 1 — shutdown is in progress.
httpserver.RouteClosedQueued
httpserver.RouteClosedQueued(listenerId) -> intTells whether the route shutdown has been queued (the background thread is processing it).
| Parameter | Type | Description |
|---|---|---|
| listenerId | int | The listener id. |
1 if yes; 0 if not yet.
i64 $lid; $lid = httpserver.Open("OnRequest", -1, 0); printf("%d\n", httpserver.RouteClosedQueued($lid));
0
For finer-grained shutdown phase tracking: after RouteClose acceptance, this becomes 1 when the background thread actually starts the shutdown.
httpserver.RouteCloseInfo
httpserver.RouteCloseInfo(listenerId) -> string
Returns the detailed key=value diagnostic state of the route shutdown process.
| Parameter | Type | Description |
|---|---|---|
| listenerId | int | The listener id. |
A space-separated key=value list (id, state, callback, timeout, port, bind, drained, pending, queued, running, active, closeAccepted, closedQueued).
i64 $lid; $lid = httpserver.Open("OnRequest", -1, 0); printf("%d\n", (str.Len(httpserver.RouteCloseInfo($lid)) > 0));
1
Extended version of RouteInfo: also contains shutdown process fields (closeAccepted, closedQueued). For detailed shutdown logging.
httpserver.RouteClose
httpserver.RouteClose(listenerId) -> intRequests the route shutdown: no new requests, in-progress ones finish.
| Parameter | Type | Description |
|---|---|---|
| listenerId | int | The listener id. |
1 if the plugin accepted the request; 0 if the route is already closed.
i64 $lid; $lid = httpserver.Open("OnRequest", -1, 0); printf("%d\n", httpserver.RouteClose($lid));
1
First step of the two-phase shutdown. Call this first, then WaitRouteDrained for in-progress requests to finish, finally Close for the full listener shutdown.
Practical notes
What the httpserver plugin is good for
Starting a local HTTP service for sandbox development, test environment, REST API mock.
Static file server: the callback returns file contents in an HTTP response blob based on the request URL.
Event-driven processing: every HTTP request is a callback invocation; the script can focus on the content.
Full control at the HTTP protocol level: the plugin does not interpret or restrict — the callback decides whether to parse or delegate.
Processing the callback blob
The callback receives the RAW HTTP request bytes — across the request line, headers, and (any) body. The callback's job is parsing the request (at least extracting the method, URL, and headers) and producing a RAW HTTP response blob. The response blob must be in proper HTTP format: `HTTP/1.1 200 OK\r\nContent-Type: ...\r\nContent-Length: N\r\n\r\n<body>`. The plugin sends the blob unchanged — there is no automatic header or status-code injection.
One listener with one route: the design choice
The plugin offers a single route layer per listener — it is not a URL router. This design choice follows the single-responsibility principle: the plugin only does TCP-level accept and callback forwarding; content-based routing is the callback's or a helper-router's job. For multiple route handlers, either open several listeners on different ports, or branch in a single callback based on the request line (method + URL).
Two-step shutdown pattern
(1) RouteClose: signals new requests are not accepted; in-progress ones may finish. (2) WaitRouteDrained(timeoutMs): blocks until every in-flight callback finishes, or the timeout expires. (3) Close: the full listener (TCP socket + accept thread) shutdown. In simpler cases, Close alone is enough — it implicitly performs the above, but doesn't give fine timing. For high-throughput servers, the two-step method is more flexible.
Listener- and route-level equivalence
A listener has a single route, so Pending == RoutePending, Queued == RouteQueued, etc. The two levels still exist for the shared vocabulary with bus / tcp_server: those plugins may have MULTIPLE routes (one listener with several path handlers), and there the two levels give different values. In httpserver, both show the same — pick the one that fits your code style.
Error handling
A wrong argument count or type, an out-of-range port (0..65535) or timeoutMs, an invalid bind mode, a failed `bind`/`listen`, a thread startup failure, and touching an already-closed listener are reported by the runtime as a runtime error, and the script stops. Close signals an open listener with 1 and an already-closed (or RouteClose-handled) one with 0 — but both are successful call results.