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 a heap *HttpApp (mutable across the registration calls); free it with http_app_free. 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.
: HttpApp: HttpApp {
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 recover_panics // wrap dispatch in `recover`, turning a panic into 500
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)
}
@ http_app_new → *HttpApp@ http_app_free * HttpApp a → v@ http_app_workers * HttpApp a i n → vServe on a worker pool of n threads (0 = single-threaded keep-alive).
@ http_app_idle_ms * HttpApp a i ms → vKeep-alive idle timeout in milliseconds (0 = server default).
@ http_app_body_max * HttpApp a i bytes → vRequest body byte cap (parser rejects larger with 413). The stdlib default is 10 MiB — raise it for upload endpoints, lower it for API-only servers.
@ http_app_head_max * HttpApp a i bytes → vRequest head byte cap (default 8 KiB).
@ http_app_max_keepalive * HttpApp a i n → vPer-connection keep-alive request cap (0 = close after one request).
@ http_app_request_timeout * HttpApp a i ms → vPer-request wall-clock budget in ms; overrun sends a stock 504 and closes the connection (0 = disabled).
@ http_app_recover * HttpApp a b on → vToggle panic→500 recovery (on by default). A handler that panics then yields a 500 instead of tearing down the connection loop.
@ http_app_logging * HttpApp a → vLog every request (method path → status) to stderr.
@ http_app_cors * HttpApp a → vPermissive CORS: reflect *, answer OPTIONS preflight with 204.
@ http_app_quiet * HttpApp a → vSuppress the startup banner on stderr.
@ http_app_static_dir * HttpApp a s dir → vServe files from dir for any GET/HEAD the router leaves unmatched (404). Path traversal is rejected by the underlying serve_static.
@ http_app_get * HttpApp a s pattern ( @ HttpResponse HttpRequest Params ) handler → v@ http_app_post * HttpApp a s pattern ( @ HttpResponse HttpRequest Params ) handler → v@ http_app_put * HttpApp a s pattern ( @ HttpResponse HttpRequest Params ) handler → v@ http_app_patch * HttpApp a s pattern ( @ HttpResponse HttpRequest Params ) handler → v@ http_app_delete * HttpApp a s pattern ( @ HttpResponse HttpRequest Params ) handler → v@ http_app_route * HttpApp a s method s pattern ( @ HttpResponse HttpRequest Params ) handler → v@ http_app_router * HttpApp a → RouterThe embedded router, for advanced use (mounting sub-routers, tests).
@ http_app_use_router * HttpApp a Router r → vAdopt a pre-built router as the app's router, freeing the default empty one. For servers that assemble their routes elsewhere (e.g. a *_service_router → Router that stays testable without a socket): build the router, hand it to the app, and let the facade own the serving glue.
@ http_app_listen * HttpApp a s host i port → iBind host:port and serve until the listener is closed (SIGINT/SIGTERM or error). Returns a process exit code (0 clean, 1 on bind/serve error).
@ http_app_listen_tls * HttpApp a s host i port s cert s key → iSame, over TLS. cert/key are PEM paths (EC or RSA leaf; a fullchain PEM is accepted for cert).