http — the unified HTTP server interface library for NURL.
One include gives a consumer both:
response builder, keep-alive server with pools & DoS limits, router, static files, auth / jwt / multipart / middleware / websocket), via the http_full aggregator; and
HttpApp facade (app.nu) that wires those pieces intoa running server in a few calls.
Usage from a dependent package:
$ deps/http/src/http.nu
@ main → i { : *HttpApp a ( http_app_new ) ( http_get a / \ HttpRequest req Params p → HttpResponse { ^ ( response_text 200 hello ) } ) ^ ( http_listen a 127.0.0.1 8080 ) }
The intent is that this package is THE dependency anything needing an HTTP server reaches for — complete enough to stand in for hand-wired servers such as the anomaly service or the nurlapi playground server.
http/app.nu — the ergonomic App facade over the stdlib HTTP stack.
The stdlib already ships a complete HTTP toolkit (net sockets + TLS, request parser, response builder, keep-alive server with worker pools and DoS limits, router, static-file serving, middleware). What every server re-invents is the ~40 lines of glue that wire them together: bind → build a router → install a shutdown signal → wrap the handler in logging / CORS / panic-recovery → run the keep-alive loop.
HttpApp collapses that into one object:
: HttpApp a ( http_app_new ) ( http_app_get a /health \ HttpRequest req Params p → HttpResponse { ^ ( response_text 200 ok ) } ) ( http_app_static_dir a ./static ) // fallback file serving ( http_app_cors a ) ( http_app_logging a ) : i rc ( http_app_listen a 127.0.0.1 8080 ) // blocks; returns exit code
Everything below the App is the untouched stdlib implementation, reached through the umbrella include in http.nu — so HttpRequest, HttpResponse, response_json, Router, Params, the auth/jwt/multipart helpers, etc. are all in scope for the handler bodies.
Memory model: http_app_new returns an HttpApp handle (mutable across the registration calls); every copy of it is the same app, and its last owner releases it — nothing to free (http_app_free is an optional early release). The embedded Router holds a stable Vec handle, so route registrations through . a router accumulate correctly. http_app_listen MOVES the bound listener into the server and stops it on return.
: HttpAppImpl: HttpAppImpl {
Router router
i idle_ms // keep-alive idle timeout (ms) for the server
i workers // 0 → single-threaded server_run; >0 → server_run_pool(n)
b use_async // T → fiber-per-connection server_run_async (see http_app_async)
i async_workers // worker pthreads for the fiber runtime; 0 = one per core
b log_requests // access log (method/path/status) to stderr
b cors // permissive CORS + OPTIONS preflight
b quiet // suppress the "serving on host:port" banner
b has_static // static fallback enabled
String webroot // directory served for unmatched GET/HEAD when has_static
i body_max // request body byte cap; -1 → stdlib default (10 MiB)
i head_max // request head byte cap; -1 → stdlib default (8 KiB)
i max_keepalive // per-conn request reuse cap; -1 → server default (0 = close after one)
i req_timeout_ms // per-request wall-clock budget; -1 → server default (0 = disabled)
i http3 // 1 → a TLS listener also serves HTTP/3 on the same port over UDP (default)
String pq_cert // optional second identity for TLS listeners: ML-DSA chain PEM ("" = none)
String pq_key // ...and its PKCS#8 key PEM
// Zero or one user middleware: a function from the app's own
// dispatch to the handler actually served. A ( Vec ) so that "none"
// and "one" stay apart and so `http_app_use` reaches every copy of
// the handle (see McpMiddleware).
( Vec HttpMiddleware ) mw
}
: HttpApp { s ctl }An HttpApp is a handle on its state in an rcbox (stdlib/core/rcbox.nu): every copy is the same state, and the last owner releases it — the router, the strings and the middleware are dropped with it.
@ HttpApp_drop sink HttpApp h → v: HttpMiddleware: HttpMiddleware {
( @ ( @ HttpResponse HttpRequest ) ( @ HttpResponse HttpRequest ) ) f
}
A user middleware, boxed so it can live in a Vec — a closure is not spellable as a generic type argument.
@ http_app_free sink HttpApp a → vLet go of a now rather than at the end of its owner's scope.
@ http_app_recover HttpApp a b on → vDEPRECATED (0.3.2): panic→500 is an unconditional guarantee of the stdlib server itself — its keep-alive loop wraps every handler call (including this facade's whole dispatch + middleware chain) in recover and answers 500 on panic. The facade used to duplicate that wrapper here, which cost a throwaway 500-response build and a second recover on EVERY request for zero added safety. The knob is kept for API compatibility and is a no-op.
@ http_app_stream HttpApp a ( @ b TcpConn HttpRequest ) f → vStreaming routes (SSE / NDJSON / chunked). The handler sees every request BEFORE the router — return F to fall through to the normal routes, or write the whole response itself (response_begin_chunked / response_write_chunk / response_end_chunked on the TcpConn) and return T; the server then closes the connection when the handler returns (a streamed response is that connection's last — do not close the conn inside the handler). One hook per process: dispatch on . req path / . req method inside it.