Skip to content

HTTP

Make buffered or streaming HTTP requests and serve connections.

Updated View as Markdown

Http provides suspending client and server operations. Transport failures are HttpError; an HTTP 4xx or 5xx response is still a successful HttpResponse, so inspect status.

HttpError distinguishes connection refusal, connection and request timeouts, DNS failure, TLS failure, invalid URLs, redirect exhaustion, generic network failure, and other host errors.

Buffered requests

fn fetch(url: string): HttpResponse ! HttpError {
    response := Http.get(url)?
    if response.status >= 400 {
        report_status(response.status)
    }
    return response
}

Static methods include get, post, put, delete, and head. Request bodies are []byte. Responses contain status, final url, body bytes, and a normalized raw header block. has_header(name) performs case-insensitive name lookup; direct value extraction is not yet a complete helper.

response := Http.post(url, payload)?
if response.has_header("content-type") {
    inspect(response.headers)
}

The final URL may differ from the requested URL after redirects. Body bytes are not implicitly decoded as text; call decode_utf8() and handle None when a text response must be valid UTF-8.

The static helpers do not currently accept arbitrary request headers. Use a host integration when authentication or content negotiation requires a header surface not represented by the installed prelude.

Clients

Http.client(base_url) creates a reusable HttpClient with relative-path get, post, put, and delete methods. It closes automatically through Drop; call close() for early release.

Reuse a client for multiple requests to the same service so the host can reuse connection state:

client := Http.client("https://api.example.test")
profile := client.get("/v1/profile")?
events := client.get("/v1/events")?

The current client surface has no head method and no per-request header builder.

Streaming

Http.get_stream(url) returns HttpBodyStream. Repeated next() calls return byte chunks; an empty chunk marks end-of-body.

body := Http.get_stream(url)?

mut done := false
for !done {
    chunk := body.next()?
    done = chunk.is_empty()
    if !done {
        consume(chunk)
    }
}
body.close()

pump_into(stream) forwards body chunks into Stream[[]byte] with backpressure.

chunks := Stream.new[[]byte](8)
body.pump_into(chunks)

for Some(chunk) := chunks.recv() {
    consume(chunk)
}

The adapter closes the destination at EOF, when the consumer abandons it, or after a transport error. It does not deliver that transport error to the consumer. Drive next() directly when clean EOF must be distinguished from a truncated response.

An empty chunk from next() means EOF and is never body content. close() is idempotent and releases an undrained connection; Drop performs the same cleanup on scope exit.

WebSockets

WebSocket.connect opens a client. send/receive use text; send_bytes/receive_bytes use binary data. close and Drop release the resource.

socket := WebSocket.connect("wss://events.example.test/socket")?
socket.send("subscribe")?
message := socket.receive()?

The installed receive methods model the next text or binary payload directly; they do not expose ping, pong, or close-frame metadata. Resource close() releases the host halves but does not promise an application-level graceful close handshake.

Servers

Http.listen(address) returns HttpListener. accept() yields HttpConnection, which can read a buffered request and respond, or use request-head/chunk and response-begin/chunk/end streaming methods. A connection can also upgrade to WebSocket.

Server loops must define cancellation, connection limits, and error policy at the application boundary.

The buffered HttpRequest currently contains method, url, and body. Header access is not part of that record. The streaming server path separates request_head() from repeated receive_chunk() calls and separates respond_begin, send_chunk, and respond_end.

Every listener, connection, client, body stream, and socket is a scoped host resource with Drop. Explicit close is useful for long-lived scopes; ordinary returns and propagated errors still release resources.

Effects

Network operations that wait for I/O carry the Suspend effect. Callers inherit that effect through inference. Closing resources is synchronous so cleanup can run during ordinary scope exit without introducing a new suspension point.

See Suspension for the effect boundary and Streams for backpressure semantics.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close