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.