NURLNURL registrynurl-lang.org →

← all packages

http-client

owner @Hindurable

Install

[dependencies]
http-client = "^0.2.0"

Versions

Dependencies (latest)

None.

http-client — the unified HTTP client interface for NURL

One dependency for anything that needs to fetch over HTTP. NURL's stdlib already ships a complete client stack — pure-NURL TLS 1.3 (with X25519MLKEM768 hybrid key exchange and ML-DSA certificate chains), an HTTP/1.1 keep-alive client, a multiplexed HTTP/2 client, an RFC 6265 cookie jar, and gzip / deflate. What it doesn't ship is the glue every program re-invents: pick the protocol the server actually offers, pool the connection to reuse it, carry cookies, follow redirects, decode the body.

http-client is that glue, as an ergonomic, dependency-importable facade — the mirror image of the http server package.

Hello, request

$ `deps/http-client/src/http_client.nu`

@ main → i {
    : *HttpClient c ( http_client_new )
    ?? ( http_client_get c `https://example.org/` ) {
        T r → {
            ( nurl_print_int ( http_client_status r ) )
            ( http_response_free r )
        }
        F e → { ( nurl_eprintln ( http_client_err_name e ) ) }
    }
    ( http_client_free c )
    ^ 0
}

What one client gives you, with nothing configured

FeatureHow
Automatic protocolan https origin is dialled with ALPN h2 http/1.1; whichever the server picks is what the client speaks — HTTP/2 multiplexed, or HTTP/1.1 with keep-alive. Plaintext http is HTTP/1.1.
HTTP/3a response carrying Alt-Svc: h3=":443" moves the origin's next request onto QUIC (stdlib/ext/http3_client.nu, pure NURL: Retry, Version Negotiation, NEW_TOKEN, HANDSHAKE_DONE all honoured); http_client_set_h3 c 1 tries QUIC first without waiting for Alt-Svc, 2 never uses it. A QUIC attempt that fails (no UDP path, a middlebox, a server that stopped) falls back to TCP for that origin and stays there.
Post-quantum TLSthe stdlib client offers X25519MLKEM768 first and verifies ML-DSA (44/65/87) certificate chains, so a post-quantum server is used as such automatically. http_client_last_pq c reports it.
Connection poolingone live connection per (scheme, host, port) — the h2 connection is multiplexed, the h1 connection is kept alive and reused, the h3 connection carries every request as its own QUIC stream.
TLS session resumptiontickets are cached per host and offered on the next connection, so a repeat visit skips the certificate and the signature.
Redirects3xx are followed (up to max_redirects); a 303, and a 301/302 on a POST, become a bodyless GET, exactly as browsers do. Relative Locations resolve per RFC 3986 §5.2.
Cookiesa built-in RFC 6265 jar stores Set-Cookie and sends Cookie on matching requests.
CompressionAccept-Encoding: gzip, deflate is offered and the body is decoded transparently, with an output cap against decompression bombs.

Configuration

: *HttpClient c ( http_client_new )
( http_client_set_verify c F )          // pinned / self-signed / test servers
( http_client_set_h3 c 1 )              // QUIC first (0: after Alt-Svc — default; 2: never)
( http_client_set_timeout c 5000 )      // per read/write deadline, ms (0 = none)
( http_client_set_max_redirects c 3 )
( http_client_set_decompress c F )       // leave bodies as the wire carried them
( http_client_set_body_max c 1048576 )   // cap the decoded body; larger → HcTooLarge
( http_client_set_user_agent c `myapp/1.0` )

Verbs

( http_client_get    c url )
( http_client_head   c url )
( http_client_delete c url )
( http_client_post   c url body content_type )   // body is a ( Vec u ), borrowed
( http_client_put    c url body content_type )
( http_client_patch  c url body content_type )
( http_client_post_str c url `{"a":1}` `application/json` )
( http_client_request c method url headers body ) // full control; headers consumed

Every call returns !HttpResponse HttpClientErr — the same HttpResponse the http server package builds, with http_client_status, http_client_header and http_client_body_str helpers, freed with http_response_free.

Evidence

( http_client_last_proto c )   // 1 HTTP/1.1, 2 HTTP/2, 3 HTTP/3, 0 none yet
( http_client_last_pq c )      // T when the key exchange was post-quantum

Where the line is drawn

Following the same two-layer split as the http package: the stdlib owns the primitives a package cannot provide (the TLS client with an ALPN preference list and one full-knob connect, keep-alive and honoured timeouts in the HTTP/1.1 transport, the HTTP/2 client, the codecs); this package owns the glue (one HttpClient, per-origin pooling, protocol selection, the cookie jar wired in, redirects, decompression, a unified response and error type). The toolchain's own consumers (nurlpkg, hub) cannot depend on a package, so they benefit from the stdlib half; this package versions independently of the toolchain.

HTTP/3 over QUIC is not yet a client role in the stdlib (the server side is complete); when it lands, this facade gains it behind the same Alt-Svc-driven upgrade without an API change.

Tests

./tests/client_test.sh builds a server on the http package and drives the facade against it over plaintext, TLS (HTTP/2 by ALPN) and QUIC (HTTP/3 after Alt-Svc, and QUIC-first): protocol selection, connection reuse, redirects and the redirect cap, cookies, gzip decoding, POST bodies, post-quantum key exchange, and the fall-back to TCP when no UDP listener answers. Requires openssl (to mint a test certificate) and curl (readiness probe).

License

MIT OR Apache-2.0.