std/http

Types

ClientError

pub type ClientError =
    | HttpsUnsupported
    | InvalidUrl(String)
    | TimedOut
    | MalformedResponse(String)
    | Closed

Why a client request failed before a response arrived.

ClientError.message

pub fn ClientError.message(self) -> String

Client

pub type Client = {
    // Bounds each send and each response; connecting is bounded only by the OS.
    pub mut timeoutMillis i64
    // Responses larger than this fail instead of being buffered.
    pub mut maxBodyBytes usize
    // A response's status line and header fields, CRLFs included.
    pub mut maxHeaderBytes usize
    // private fields omitted
}

An HTTP/1.1 client over plain TCP. Resolving and connecting are direct-blocking, so they go through pool; reads and writes suspend the task instead.

Client.new

pub fn Client.new(n: net.Net, pool: task.BlockingPool) -> Client

Client.open

pub async fn Client.open(self, url: String) -> Session!

Opens a connection to the host and port of url (its path is ignored).

Client.send

pub async fn Client.send(self, url: String, req: Request) -> Response!

Opens a connection, sends req to url's path and closes it after the response.

Client.get

pub async fn Client.get(self, url: String) -> Response!

Client.post

pub async fn Client.post(self, url: String, contentType: String, body: Bytes) -> Response!

Client.postJson

pub async fn Client.postJson[T: json.Encode](self, url: String, value: T) -> Response!

Posts value encoded as JSON.

Session

pub type Session = resource {
    // private fields omitted
}

One connection that can carry several requests in turn (keep-alive). A send that fails leaves it closed, since a late response could otherwise answer the next request.

Session.send

pub async fn Session.send(mut self, req: Request) -> Response!

Sends req (its target as given) and reads the whole response; a response that closes the connection leaves the session unusable.

pub type Header = {
    pub name String
    pub value String
}

One header field as received or to be sent.

Headers

pub type Headers = {
    // private fields omitted
}

Header fields in arrival order; names compare case-insensitively and a name may repeat.

Headers.new

pub fn Headers.new() -> Headers

Headers.add

pub fn Headers.add(mut self, name: String, value: String)

Appends a field after any earlier one with the same name.

Headers.set

pub fn Headers.set(mut self, name: String, value: String)

Replaces every field named name with a single one holding value.

Headers.remove

pub fn Headers.remove(mut self, name: String)

Drops every field named name.

Headers.get

pub fn Headers.get(self, name: String) -> String?

The first value of name.

Headers.all

pub fn Headers.all(self, name: String) -> Array[String]

Every value of name, in arrival order.

Headers.len

pub fn Headers.len(self) -> usize

Headers.entries

pub fn Headers.entries(self) -> Array[Header]

The fields in arrival order.

Failure

pub type Failure = {
    pub status Int
    pub text String
}

A handler failure that names the status to answer with; the server sends text as the body. Any other error a handler fails with is answered with 500 and handed to the server's log instead.

Failure.message

pub fn Failure.message(self) -> String

Param

pub type Param = {
    pub name String
    pub value String
}

A path parameter a Router matched, such as id in /users/{id}.

Request

pub type Request = {
    pub method String
    // The request-target as sent, path plus any `?query`.
    pub target String
    // The target before `?`, still percent-encoded.
    pub path String
    // The target after `?`, or "".
    pub query String
    pub mut headers Headers
    pub mut body Bytes
    // Filled by the Router; decoded from percent-encoding.
    pub mut params Array[Param]
}

An HTTP request: what a handler receives on the server and what a Client sends.

Request.new

pub fn Request.new(method: String, target: String) -> Request

A request with no headers and an empty body.

Request.param

pub fn Request.param(self, name: String) -> String?

The path parameter name.

Request.header

pub fn Request.header(self, name: String) -> String?

The first value of the header name.

Request.queryParam

pub fn Request.queryParam(self, name: String) -> String?

The first name=value pair of the query string, percent-decoded with + read as a space.

Request.text

pub fn Request.text(self) -> String!

The body as UTF-8 text; invalid UTF-8 fails with a 400 Failure.

Request.json

pub fn Request.json[T: json.Decode](self) -> T!

Decodes the body as JSON into a T; a malformed or mismatched body fails with a 400 Failure.

Response

pub type Response = {
    pub mut status Int
    pub mut headers Headers
    pub mut body Bytes
}

An HTTP response: what a handler returns and what a Client receives.

Response.new

pub fn Response.new(status: Int) -> Response

A response with no headers and an empty body.

Response.ofText

pub fn Response.ofText(status: Int, text: String) -> Response

A text/plain; charset=utf-8 response.

Response.ofJson

pub fn Response.ofJson[T: json.Encode](status: Int, value: T) -> Response!

An application/json response holding value encoded.

Response.header

pub fn Response.header(self, name: String) -> String?

The first value of the header name.

Response.text

pub fn Response.text(self) -> String!

The body as UTF-8 text.

Response.json

pub fn Response.json[T: json.Decode](self) -> T!

Decodes the body as JSON into a T.

Router

pub type Router = {
    // private fields omitted
}

Routes by method and path pattern, built by chaining: Router.new().get("/users/{id}", show). A {name} segment matches one non-empty segment and is read back with Request.param.

Router.new

pub fn Router.new() -> Router

Router.handle

pub fn Router.handle(move self, method: String, pattern: String, handler: escaping fn(Request) -> Response!) -> Router

Adds a route; the first route registered for a matching method and path wins.

Router.get

pub fn Router.get(move self, pattern: String, handler: escaping fn(Request) -> Response!) -> Router

Router.post

pub fn Router.post(move self, pattern: String, handler: escaping fn(Request) -> Response!) -> Router

Router.put

pub fn Router.put(move self, pattern: String, handler: escaping fn(Request) -> Response!) -> Router

Router.delete

pub fn Router.delete(move self, pattern: String, handler: escaping fn(Request) -> Response!) -> Router

Router.use

pub fn Router.use(move self, middleware: escaping fn(escaping fn(Request) -> Response!) -> fn(Request) -> Response!) -> Router

Adds middleware around every request the router sees, unmatched ones included; the first one added is the outermost.

Router.handler

pub fn Router.handler(move self) -> fn(Request) -> Response!

The router as one handler: 404 when no pattern matches the path, 405 with an Allow header when one does but not for this method; HEAD falls back to GET.

Limits

pub type Limits = {
    // Request line plus header fields, CRLFs included; past it the answer is 431.
    pub mut maxHeaderBytes usize
    // Header field count; past it the answer is 431.
    pub mut maxHeaders usize
    // Decoded body size; past it the answer is 413 and the connection closes.
    pub mut maxBodyBytes usize
    // From a request's first byte to its last; past it the answer is 408.
    pub mut readTimeoutMillis i64
    // How long a kept-alive connection may wait for its next request before it is closed.
    pub mut idleTimeoutMillis i64
    // How long one response may take to send.
    pub mut writeTimeoutMillis i64
    // Connections served at once, at least 1; further ones wait in the listener's backlog.
    pub mut maxConnections usize
}

Bounds on what one connection may send and how long it may take.

Limits.defaults

pub fn Limits.defaults() -> Limits

8 KiB of head, 100 fields, 1 MiB of body, 10 s to read or write, 30 s idle, 1024 connections.

Server

pub type Server = {
    pub mut limits Limits
    // private fields omitted
}

An HTTP/1.1 server around one handler.

Server.new

pub fn Server.new(handler: escaping fn(Request) -> Response!, log: escaping fn(String) -> Void) -> Server

A server answering every request with handler. log receives one line for each handler error that is not a Failure (the client gets a bare 500) and for each failed accept; nothing else is logged.

Server.serve

pub async fn Server.serve(self, listener: net.Listener, stop: Shutdown) -> Void!

Serves each accepted connection on its own child task until stop is requested, then closes the listener and returns once every connection has finished its current exchange; cancelling abandons them at their next await.

Shutdown

pub type Shutdown = {
    // private fields omitted
}

A shutdown request shared between a Server and whoever stops it; clones see the same request. It is a single-thread cell, so a task holding one stays on its executor's thread.

Shutdown.new

pub fn Shutdown.new() -> Shutdown

Shutdown.clone

pub fn Shutdown.clone(self) -> Shutdown

Shutdown.request

pub fn Shutdown.request(self)

Asks every server watching this Shutdown to stop accepting and to close each connection once its current exchange is done.

Shutdown.requested

pub fn Shutdown.requested(self) -> Bool

Functions

failure

pub fn failure(status: Int, text: String) -> Failure

A Failure answering with status and text.

reason

pub fn reason(status: Int) -> String

The standard reason phrase of a status code, or "" for an unregistered one.

percentDecode

pub fn percentDecode(text: String) -> String?

Decodes %XX escapes; None for a broken escape or a result that is not UTF-8.