Skip to content
Kavachकवच

HTTP

Also known as: web server client rest api request response endpoint route fetch curl express flask status code

● Intermediate6 minBefore this: JSON, TasksSpec 14.11

std.http calls web APIs and serves them: HTTP/1.1 and HTTP/2, with HTTP/3 for calls, written in Kavach itself.

use std.http
use std.net

fn hello(r: Request) -> Response {
    http.text(200, "hello from {r.path}")
}

fn main() ! Error {
    const listener = try net.listen("127.0.0.1:0")
    const _server = spawn http.serve(listener, hello)
    const page = try http.get("http://{listener.address()}/home")
    print(page.status)
    print(page.body.text() ?? "")
}

The program ran and printed:

kavach run server.kav

200hello from /home
server.kav: Kavach code, then what running it prints
  • hello is the handler: it takes a Request and gives a Response.
  • net.listen("127.0.0.1:0") lets the system pick a free port; listener.address() says which.
  • spawn http.serve(…) serves in a task of its own, and stops when main returns: a server can't be left running by mistake.
  • http.get calls it, here from the same program; a real client is any other program, in any language.

Every core at work

http.serve runs each connection in a task of its own, spawned with spawn cores, so in a release build (kavach build) handlers run at once on every core. kavach run answers them on one core, in turn, with the same responses.

  • A handler that computes answered in 2.5–3.0 s instead of 18–20 s on one core.
  • A handler that does nearly nothing answered in 131–221 ms instead of 461–633 ms on one core.
  • Measured on Windows (24 cores, 32 clients making 200 requests each on kept-open connections).
  • Handlers share nothing but what they reach through a Shared value or a channel. Each handler's printed lines come out in its own order, whole.

Every function

FunctionGives
http.get(url) -> Response ! HttpErrorGET url
http.post(url, body) -> Response ! HttpErrorPOST url with body (Bytes)
http.request(method, url, headers: [Header] = [], body: Bytes = Bytes([])) -> Response ! HttpErrorAny method, with the headers given and a body: a form post with basic auth, a bearer token, PUT, PATCH, DELETE, if-none-match. HTTP/2 when the server offers it with TLS, else HTTP/1.1; host, content-length and connection are the client's own, so ones given are left out
http.get3(url) -> Response ! HttpErrorGET url over HTTP/3
http.open(url) -> Session ! HttpErrorA session: one HTTP/1.1 connection to the server of url (http:// or https://; the path is not used), kept open for several requests
s.get(path), s.post(path, body) -> Response ! HttpErrorA request on session s's connection, waiting for its response
s.request(method, path, headers: [Header] = [], body: Bytes = Bytes([])) -> Response ! HttpErrorThe same as http.request, on session s's connection
s.close()Closes the session's connection
http.serve(listener, handler, limits: ServeLimits()) ! NetErrorServes HTTP on listener: each connection in a task of its own, on any core (spawn cores, 9.8), its requests answered by handler: fn(Request) -> Response, each client held to limits (below). It runs until the function that called it returns, which cancels it (9.2)
http.text(status, body) -> ResponseA response with text
http.get_json<T>(url) -> T ! HttpErrorGET url, the response's JSON body decoded into a T (json.decode<T>, 2.12)
http.post_json<T>(url, body) -> T ! HttpErrorPOST url with the Json value body, the response's JSON decoded into a T
http.json_response(status, value) -> ResponseA response with the Json value value (write json.of(order) for a typed value)
http.read_json<T>(request) -> T ! JsonErrorA request's JSON body decoded into a T

Network access needs the Net capability, so a pub function that calls an API says uses Net, and an agent must list it in its can: see Capabilities.

One page for each form

Go deeper the why, for readers who want it

HTTP is written in Kavach, on top of std.net: HTTP/1.1, HTTP/2 with its header compression, and HTTP/3 over QUIC. https:// addresses go through TLS, offering HTTP/2 first. Programs that speak HTTP/2 or 3 themselves, as gRPC does, use the parts in std.http.h2 and std.http.h3.

Check yourself

QUIZ 1 / 1pick one
What happens to a server started with spawn http.serve(…) when the function that started it returns?