Docs/Building applications

HTTP & networking

Serve a response, call another service, and choose a streaming API.

On this page

valk.http provides a request/response API for both servers and clients. A small server needs a handler and a listening address; the runtime handles accepting connections and scheduling requests.

Serve HTTP#

Save this as server.valk:

Valk
use valk.http

fn handle(req: http.Request) http.Response {
    return http.Response.html("<h1>Hello from Valk</h1>")
}

fn main() {
    let server = http.Server.new("127.0.0.1", 9000, handle)
    server.start() ! {
        println("Could not start the HTTP server")
        return
    }
}

Run valk build server.valk --run, then open http://127.0.0.1:9000 or make a request from another terminal:

Terminal
curl http://127.0.0.1:9000

The handler receives an http.Request and returns an http.Response. Binding to 127.0.0.1 makes this example accessible from your own machine. start() keeps serving until shutdown and uses the CPU thread count as its default worker count; pass a worker count explicitly when needed.

Make a request#

The following client expects the example server to be running:

Valk
use valk.http

fn main() {
    let response = http.request("GET", "http://127.0.0.1:9000") ! {
        println("Could not reach the server")
        return
    }
    println(response.status)
    println(response.body)
}

A completed HTTP exchange can still carry an application error such as a 404 or 500 status. Check the response status separately from handling a connection or request failure.

Use http.Options for request headers, a body, or query parameters. To send JSON, encode a value with json.encode and set an appropriate Content-Type header.

Work with headers#

Headers are not an ordinary map: a name can have multiple values, and names are case-insensitive.

Valk
use valk.http

fn main() {
    let headers = http.Headers { "Accept" => "application/json" }
    headers.append("X-Tag", "one")
    headers.append("x-tag", "two")
    println(headers.get_all("X-TAG").length) // 2
}

set replaces a name's values; append adds another field. get returns the first value or a lookup error, while get_all returns all of them. Use get_all("Set-Cookie") when processing multiple response cookies.

Choose how to receive a body#

The basic client keeps the response body in memory. For downloads, http.download(url, path) writes to a file. A request's http.Options.output can also direct the body into an io.Writer.

Use streaming for bodies that should not be collected into a single string. The same reader and writer interfaces work with files, memory buffers, and network connections.

Shut down and configure a server#

If the rest of the program must keep running alongside the server, start it with co server.start() and await that coroutine when shutting down. server.request_shutdown(5000) gives current requests up to five seconds to finish.

HTTP/2 is experimental and opt-in. Configure TLS with server.tls(certificate_path, key_path) and enable server.http2 before calling start. Check the HTTP API reference for configuration details.

For protocols below HTTP, valk.net provides TCP sockets and connections. Socket reads fill a supplied writable buffer and may return only part of a message; define framing in your protocol rather than assuming one read equals one message.