Runtime And Web

std.http

HTTP envelope writing, request parsing, hosted fetch, and response metadata helpers.

When To Use std.http

In Zerolang, use std.http for HTTP request parsing, response envelope writing, hosted fetch, local listen support, and web API helpers.

Runnable today:

APIReturnNotes
std.http.parseMethod(text)HttpMethodParses a small HTTP method token.
std.http.client(net)HttpClientCreates hosted client metadata from a network capability.
std.http.server(net, address)HttpServerCreates hosted server metadata from a network capability and address.
std.http.listen(world)Void raises [Io]Starts a loopback HTTP listener from zero run, auto-selecting a development port from 3000 upward.
std.http.listen(world, port)Void raises [Io]Starts a loopback HTTP listener on exactly port, failing if the port is occupied.
std.http.fetch(client, request, response, timeout)HttpResultPerforms a hosted HTTP(S) request with a Duration timeout and writes response metadata, headers, and body into caller-owned storage.
std.http.resultOk(result)BoolTrue when transport succeeded and the status is 2xx.
std.http.resultStatus(result)u16Reads the HTTP status, or 0 when no status was available.
std.http.resultBodyLen(result)usizeReads the number of response body bytes written into the response buffer.
std.http.resultError(result)HttpErrorReads the transport/provider error.
std.http.errorNone()HttpErrorTransport succeeded.
std.http.errorInvalidUrl()HttpErrorThe request URL was invalid.
std.http.errorUnsupportedProtocol()HttpErrorThe URL protocol is not supported.
std.http.errorDns()HttpErrorDNS lookup failed.
std.http.errorConnect()HttpErrorConnecting to the remote host failed.
std.http.errorTls()HttpErrorTLS verification or connection setup failed.
std.http.errorTimeout()HttpErrorThe request timed out.
std.http.errorTooLarge()HttpErrorThe response did not fit in the caller-owned buffer.
std.http.errorProviderUnavailable()HttpErrorThe hosted HTTP provider is unavailable.
std.http.errorIo()HttpErrorThe provider reported an I/O failure.
std.http.errorInvalidRequest()HttpErrorThe request envelope was invalid.
std.http.errorName(error)StringReturns a stable label for a transport error.
std.http.responseLen(response)usizeReads the response byte count written after the internal metadata prefix.
std.http.responseHeadersLen(response)usizeReads the raw response header byte count.
std.http.responseBodyOffset(response)usizeReads the body start offset within the response buffer.
std.http.headerValue(response, name)HttpHeaderValueLocates a response header value by case-insensitive header name.
std.http.headerFound(value)BoolTrue when headerValue found a matching header.
std.http.headerOffset(value)usizeReads the header value byte offset within the response buffer.
std.http.headerLen(value)usizeReads the header value byte length.
std.http.tlsBoundary()StringNames the platform or C-library TLS boundary.
std.http.statusReason(status)StringReturns a common reason phrase for status-line writing.
std.http.statusIsInformational(status)BoolTrue for 1xx statuses.
std.http.statusIsSuccess(status)BoolTrue for 2xx statuses.
std.http.statusIsRedirect(status)BoolTrue for 3xx statuses.
std.http.statusIsClientError(status)BoolTrue for 4xx statuses.
std.http.statusIsServerError(status)BoolTrue for 5xx statuses.
std.http.writeRequest(buffer, startLine, body)Maybe<Span<u8>>Writes METHOD URL, optional content length, blank line, and body into caller storage.
std.http.writeRequestWithHeader(buffer, startLine, headerLine, body)Maybe<Span<u8>>Writes a request envelope with one validated name: value header line.
std.http.writeRequestWithHeaders(buffer, startLine, headers, body)Maybe<Span<u8>>Writes a request envelope with a validated newline-separated header block.
std.http.writeMethodRequest(buffer, method, target, body)Maybe<Span<u8>>Writes a request envelope from separate method and target spans.
std.http.writeGetRequest(buffer, target)Maybe<Span<u8>>Writes an empty GET request envelope.
std.http.writeHeadRequest(buffer, target)Maybe<Span<u8>>Writes an empty HEAD request envelope.
std.http.writeDeleteRequest(buffer, target)Maybe<Span<u8>>Writes an empty DELETE request envelope.
std.http.writeJsonRequest(buffer, startLine, body)Maybe<Span<u8>>Writes a JSON request envelope with content-type and content-length.
std.http.writeJsonMethodRequest(buffer, method, target, body)Maybe<Span<u8>>Writes a JSON request envelope from separate method and target spans.
std.http.writeJsonRequestWithHeader(buffer, startLine, headerLine, body)Maybe<Span<u8>>Writes a JSON request envelope with one validated extra header line.
std.http.writeJsonRequestWithHeaders(buffer, startLine, headers, body)Maybe<Span<u8>>Writes a JSON request envelope with a validated extra header block.
std.http.writePostJsonRequest(buffer, target, body)Maybe<Span<u8>>Writes a POST JSON request envelope.
std.http.writePutJsonRequest(buffer, target, body)Maybe<Span<u8>>Writes a PUT JSON request envelope.
std.http.writePatchJsonRequest(buffer, target, body)Maybe<Span<u8>>Writes a PATCH JSON request envelope.
std.http.writeResponse(buffer, status, body)Maybe<Span<u8>>Writes an HTTP/1.1 response envelope into caller storage.
std.http.writeResponseWithHeader(buffer, status, headerLine, body)Maybe<Span<u8>>Writes a response envelope with one validated name: value header line.
std.http.writeResponseWithHeaders(buffer, status, headers, body)Maybe<Span<u8>>Writes a response envelope with a validated newline-separated header block.
std.http.writeJsonResponse(buffer, status, body)Maybe<Span<u8>>Writes a JSON HTTP/1.1 response envelope into caller storage.
std.http.writeJsonResponseWithHeader(buffer, status, headerLine, body)Maybe<Span<u8>>Writes a JSON response envelope with one validated name: value header line.
std.http.writeJsonResponseWithHeaders(buffer, status, headers, body)Maybe<Span<u8>>Writes a JSON response envelope with a validated extra header block.
std.http.writeJsonResponseWithCookie(buffer, status, cookie, body)Maybe<Span<u8>>Writes a JSON response envelope with one Set-Cookie header value.
std.http.writeJsonError(buffer, status, code)Maybe<Span<u8>>Writes {"error":"code"} after validating the code is JSON-safe lower-case ASCII, digits, _, or -.
std.http.writeCorsPreflight(buffer, allowOrigin, allowMethods, allowHeaders)Maybe<Span<u8>>Writes a 204 CORS preflight response with caller-provided allow headers.
std.http.writeCorsJsonResponse(buffer, statusLine, body, allowOrigin)Maybe<Span<u8>>Writes a JSON response with access-control-allow-origin; statusLine is a fragment such as "200 OK".
std.http.writeTextResponse(buffer, status, body)Maybe<Span<u8>>Writes a text/plain; charset=utf-8 response envelope into caller storage.
std.http.writeTextOk(buffer, body)Maybe<Span<u8>>Writes a 200 plain-text response envelope into caller storage.
std.http.writeHtmlResponse(buffer, status, body)Maybe<Span<u8>>Writes a text/html; charset=utf-8 response envelope into caller storage.
std.http.writeHtmlOk(buffer, body)Maybe<Span<u8>>Writes a 200 HTML response envelope into caller storage.
std.http.writeRedirect(buffer, status, location)Maybe<Span<u8>>Writes a redirect response with a safe Location header; rejects non-3xx statuses and empty or control-character locations.
std.http.writeFound(buffer, location)Maybe<Span<u8>>Writes a 302 redirect response.
std.http.writeSeeOther(buffer, location)Maybe<Span<u8>>Writes a 303 redirect response.
std.http.writeMovedPermanently(buffer, location)Maybe<Span<u8>>Writes a 301 redirect response.
std.http.writePermanentRedirect(buffer, location)Maybe<Span<u8>>Writes a 308 redirect response.
std.http.contentTypeForPath(path)StringReturns a small static-file media type from a path suffix.
std.http.writeStaticResponse(buffer, status, path, body)Maybe<Span<u8>>Writes a response with content-type inferred from the path suffix.
std.http.writeJsonOk(buffer, body)Maybe<Span<u8>>Writes a 200 JSON response envelope into caller storage.
std.http.writeJsonCreated(buffer, body)Maybe<Span<u8>>Writes a 201 JSON response envelope into caller storage.
std.http.writeJsonBadRequest(buffer, body)Maybe<Span<u8>>Writes a 400 JSON response envelope into caller storage.
std.http.writeJsonUnauthorized(buffer, body)Maybe<Span<u8>>Writes a 401 JSON response envelope into caller storage.
std.http.writeJsonForbidden(buffer, body)Maybe<Span<u8>>Writes a 403 JSON response envelope into caller storage.
std.http.writeJsonNotFound(buffer, body)Maybe<Span<u8>>Writes a 404 JSON response envelope into caller storage.
std.http.writeJsonMethodNotAllowed(buffer, body)Maybe<Span<u8>>Writes a 405 JSON response envelope into caller storage.
std.http.writeJsonConflict(buffer, body)Maybe<Span<u8>>Writes a 409 JSON response envelope into caller storage.
std.http.writeJsonUnprocessable(buffer, body)Maybe<Span<u8>>Writes a 422 JSON response envelope into caller storage.
std.http.writeJsonTooManyRequests(buffer, body)Maybe<Span<u8>>Writes a 429 JSON response envelope into caller storage.
std.http.writeJsonInternalServerError(buffer, body)Maybe<Span<u8>>Writes a 500 JSON response envelope into caller storage.
std.http.writeNoContent(buffer)Maybe<Span<u8>>Writes a 204 response envelope with an empty body.
std.http.requestMethodName(request)Maybe<Span<u8>>Borrows the method token from a request envelope.
std.http.requestTarget(request)Maybe<Span<u8>>Borrows the raw target from a request envelope.
std.http.requestPath(request)Maybe<Span<u8>>Borrows the path from an absolute or origin-form request target.
std.http.pathSegmentCount(path)usizeCounts non-empty path segments in a path span.
std.http.pathSegment(path, index)Maybe<Span<u8>>Borrows a zero-based non-empty path segment from a path span.
std.http.pathMatchesPattern(path, pattern)BoolMatches path segments against literals, :params, and a trailing * wildcard.
std.http.pathParam(path, pattern, name)Maybe<Span<u8>>Borrows a named path parameter from a matched path pattern.
std.http.requestPathSegmentCount(request)usizeCounts non-empty path segments from a request envelope path.
std.http.requestPathSegment(request, index)Maybe<Span<u8>>Borrows a zero-based non-empty request path segment.
std.http.requestQuery(request)Maybe<Span<u8>>Borrows the query string from a request target.
std.http.requestQueryValue(request, name)Maybe<Span<u8>>Borrows a query value by name from a request envelope.
std.http.requestHeader(request, name)Maybe<Span<u8>>Borrows a case-insensitive request header value.
std.http.requestBearerToken(request)Maybe<Span<u8>>Borrows the bearer token from the Authorization request header.
std.http.requestCookie(request, name)Maybe<Span<u8>>Borrows a named cookie value from the Cookie request header.
std.http.requestContentLength(request)Maybe<usize>Parses the Content-Length request header.
std.http.requestContentType(request)Maybe<Span<u8>>Borrows the media type from Content-Type, excluding parameters.
std.http.requestAccepts(request, media)BoolChecks whether Accept allows an exact media type, type wildcard, or */*; absent Accept allows any media and q=0 rejects a range.
std.http.requestAcceptsJson(request)BoolChecks whether Accept allows application/json.
std.http.requestBody(request)Maybe<Span<u8>>Borrows the request body after the blank line.
std.http.requestBodyWithin(request, max)Maybe<Span<u8>>Borrows the request body only when it is at most max bytes.
std.http.requestHasJsonContentType(request)BoolTrue when the request content type is application/json, ignoring ASCII case and allowing parameters.
std.http.requestJsonBodyWithin(request, max)Maybe<Span<u8>>Borrows the body only when content type is JSON, body length is within max, and bytes validate as JSON.
std.http.requestJsonField(request, name, max)Maybe<Span<u8>>Borrows a top-level JSON field from a bounded JSON request body.
std.http.requestMatches(request, method, path)BoolTrue when a request envelope has the exact method and normalized path.
std.http.methodAllowed(method, allowed)BoolChecks a method against a comma-separated allow list.
std.http.requestMethodAllowed(request, allowed)BoolChecks a request method against a comma-separated allow list.
std.http.requestMethodIs(request, method)BoolTrue when a request envelope has the exact method.
std.http.requestIsGet(request, path)BoolTrue when a request envelope is GET for the normalized path.
std.http.requestIsHead(request, path)BoolTrue when a request envelope is HEAD for the normalized path.
std.http.requestIsOptions(request, path)BoolTrue when a request envelope is OPTIONS for the normalized path.
std.http.requestIsPost(request, path)BoolTrue when a request envelope is POST for the normalized path.
std.http.requestIsPut(request, path)BoolTrue when a request envelope is PUT for the normalized path.
std.http.requestIsPatch(request, path)BoolTrue when a request envelope is PATCH for the normalized path.
std.http.requestIsDelete(request, path)BoolTrue when a request envelope is DELETE for the normalized path.
std.http.requestPathStartsWith(request, prefix)BoolTrue when the normalized request path starts with prefix.
std.http.requestPathTailAfter(request, prefix)Maybe<Span<u8>>Borrows the normalized request path after prefix, or null when it does not match.
std.http.requestRouteMatches(request, method, pattern)BoolTrue when the method matches and the path matches a segment pattern.
std.http.requestRouteMethodAllowed(request, pattern, allowed)BoolTrue when the path pattern matches and the method is in a comma-separated allow list.
std.http.requestPathParam(request, pattern, name)Maybe<Span<u8>>Borrows a named request path parameter from a matched pattern.
std.http.headerBlockSafe(headers)BoolValidates a newline-separated header block before writing it.
std.http.headerBytes(response, value)Maybe<Span<u8>>Borrows a response header value after validating packed metadata.
std.http.responseBody(response, result)Maybe<Span<u8>>Borrows the response body when the transport result succeeded.
std.http.responseBodyBytes(response)Maybe<Span<u8>>Borrows the body bytes from a local response envelope written by response helpers.
std.http.responseStatus(response)Maybe<u16>Parses the status code from a local response envelope.
std.http.responseStatusIs(response, status)BoolChecks the status code in a local response envelope.
std.http.responseHeader(response, name)Maybe<Span<u8>>Borrows a case-insensitive header value from a local response envelope.
std.http.responseContentType(response)Maybe<Span<u8>>Borrows the media type from a local response envelope.
std.http.responseRedirectLocation(response)Maybe<Span<u8>>Borrows a redirect Location header from a local response envelope.
std.http.responseBodyEquals(response, expected)BoolCompares a local response body with expected bytes.
std.http.responseMatches(response, status, contentType, body)BoolChecks local response status, optional content type, and body bytes.
std.http.testRequest(buffer, method, target, body)Maybe<Span<u8>>Writes a synthetic request envelope for handler tests.
std.http.testJsonRequest(buffer, method, target, body)Maybe<Span<u8>>Writes a synthetic JSON request envelope for handler tests.

The WithHeader and WithHeaders writers reject header names they manage themselves. Request writers reject content-length and transfer-encoding; JSON request writers also reject content-type; response writers reject content-length, transfer-encoding, and connection; JSON response writers also reject content-type.

Metadata labels:

  • effects: net, memory, parse, or none for named error constants
  • allocation behavior: metadata helpers do not allocate; fetch writes into a caller-owned response buffer and uses the provider runtime internally
  • target support: request/response parsing and writing helpers are target-neutral; client/server require a net-capable target; fetch and listen run on supported Darwin arm64 and Linux x64 host executable targets
  • error behavior: metadata helpers are infallible; fetch returns status, body length, and error metadata so non-2xx responses are distinguishable from transport failures
  • ownership notes: HTTP helpers borrow network capability metadata and write only to caller-owned buffers
  • examples: conformance/native/pass/std-http-metadata-neutral.graph, conformance/native/pass/std-http-fetch.graph, conformance/native/pass/std-http-errors.graph, conformance/native/pass/std-http-response-helpers.graph, conformance/native/pass/std-http-api-helpers.graph, conformance/native/pass/std-http-cors-helpers.graph, conformance/native/pass/std-http-auth-helpers.graph, conformance/native/pass/std-http-path-segments.graph, examples/json-api-client.graph, examples/json-api-router.graph, examples/std-http-json.graph, examples/std-http-request.graph, examples/std-http-headers.graph

Example

Metadata helpers:

pub fn main(world: World) -> Void raises {    let net: Net = std.net.host()    let addr: Address = std.net.address("localhost", 8080_u16)    let _client: HttpClient = std.http.client(net)    let _server: HttpServer = std.http.server(net, addr)    let method: HttpMethod = std.http.parseMethod("GET")    if method == std.http.parseMethod("GET") && std.mem.len(std.mem.span("body")) == 4 {        check world.out.write("http ok\n")    }}

GET request:

pub fn main(world: World) -> Void raises {    let net: Net = std.net.host()    let client: HttpClient = std.http.client(net)    var response: [512]u8 = [0_u8; 512]    let request: Span<u8> = std.mem.span("GET https://example.com\n\n")    let result: HttpResult = std.http.fetch(client, request, response, std.time.ms(1000))    if std.http.resultOk(result) {        check world.out.write("http get ok\n")        return    }    check world.err.write("http get failed\n")}

Request with headers and body:

pub fn main(world: World) -> Void raises {    let net: Net = std.net.host()    let client: HttpClient = std.http.client(net)    var request_buf: [256]u8 = [0_u8; 256]    let request: Maybe<Span<u8>> = std.http.writeJsonRequestWithHeader(request_buf, "POST https://example.com/api", "accept: application/json", "{\"ping\":1}")    var response: [512]u8 = [0_u8; 512]    if request.has {        let result: HttpResult = std.http.fetch(client, request.value, response, std.time.ms(1000))        if std.http.resultOk(result) {            check world.out.write("http post ok\n")            return        }    }    check world.err.write("http post failed\n")}

Request routing:

pub fn main(world: World) -> Void raises {    let request: Span<u8> = std.mem.span("POST /users/7?tenant=demo\naccept: application/json\ncontent-type: application/json; charset=utf-8\n\n{\"id\":7}")    var response: [256]u8 = [0_u8; 256]    let id: Maybe<Span<u8>> = std.http.requestPathParam(request, "/users/:id", "id")    let body_id: Maybe<Span<u8>> = std.http.requestJsonField(request, "id", 64)    let tenant: Maybe<Span<u8>> = std.http.requestQueryValue(request, "tenant")    if std.http.requestRouteMatches(request, "POST", "/users/:id") && id.has && tenant.has && body_id.has && std.http.requestAcceptsJson(request) {        let written: Maybe<Span<u8>> = std.http.writeJsonResponseWithHeader(response, 201_u16, "location: /users/7", "{\"created\":true}")        if written.has {            check world.out.write("http route ok\n")            return        }    }    let failed: Maybe<Span<u8>> = std.http.writeJsonError(response, 400, "bad_request")    if failed.has {        check world.err.write("http route failed\n")    }}

CORS preflight and JSON response:

pub fn main(world: World) -> Void raises {    let request: Span<u8> = std.mem.span("OPTIONS /users\naccess-control-request-method: POST\n\n")    var response: [256]u8 = [0_u8; 256]    if std.http.requestIsOptions(request, "/users") {        let written: Maybe<Span<u8>> = std.http.writeCorsPreflight(response, "*", "GET, POST, OPTIONS", "content-type, authorization")        if written.has {            check world.out.write("http cors preflight ok\n")            return        }    }    let failed: Maybe<Span<u8>> = std.http.writeCorsJsonResponse(response, "400 Bad Request", "{\"error\":\"bad_request\"}", "*")    if failed.has {        check world.err.write("http cors failed\n")    }}

Authorization and session cookie helpers:

pub fn main(world: World) -> Void raises {    let request: Span<u8> = std.mem.span("GET /me\nauthorization: Bearer token-123\ncookie: sid=abc; theme=dark\n\n")    let token: Maybe<Span<u8>> = std.http.requestBearerToken(request)    let session: Maybe<Span<u8>> = std.http.requestCookie(request, "sid")    if token.has && session.has {        check world.out.write("http auth ok\n")    }}

Response body:

pub fn main(world: World) -> Void raises {    let maybe_request: Maybe<String> = std.args.get(1)    if !maybe_request.has {        check world.err.write("usage: pass HTTP request envelope\n")        return    }    let net: Net = std.net.host()    let client: HttpClient = std.http.client(net)    var response: [512]u8 = [0_u8; 512]    let result: HttpResult = std.http.fetch(client, std.mem.span(maybe_request.value), response, std.time.ms(5000))    let bytes: Maybe<Span<u8>> = std.http.responseBody(response, result)    var arena_buf: [16]u8 = [0_u8; 16]    var arena: FixedBufAlloc = std.mem.fixedBufAlloc(arena_buf)    var parsed: Maybe<JsonDoc> = null    if bytes.has {        parsed = std.json.parseBytes(arena, bytes.value)    }    if std.http.resultOk(result) && bytes.has && parsed.has {        check world.out.write("http response json ok\n")        return    }    check world.err.write("http response json failed\n")}

Loopback API server:

pub fn main(world: World) -> Void raises {    check std.http.listen(world)} fn handle(request: Span<u8>, response: MutSpan<u8>) -> Maybe<Span<u8>> {    if std.http.requestIsGet(request, "/ping") {        return std.http.writeJsonOk(response, "{\"message\":\"pong\"}")    }    if std.http.requestIsGet(request, "/robots.txt") {        return std.http.writeTextOk(response, "user-agent: *\nallow: /\n")    }    if std.http.requestIsGet(request, "/old") {        return std.http.writeMovedPermanently(response, "/ping")    }    return std.http.writeJsonError(response, 404, "not_found")}

Run it and use the port printed by the listener:

zero run .
# listening on http://127.0.0.1:3001
curl -sS -i http://127.0.0.1:3001/ping

When no port is passed, listen(world) starts at 3000 and increments by one until it finds a free loopback port. This lets small local services coexist on a development machine. When the program passes an explicit port, such as std.http.listen(world, 3000_u16), Zero tries exactly that port and reports the bind failure instead of auto-incrementing.

Synthetic handler check:

fn handle(request: Span<u8>, response: MutSpan<u8>) -> Maybe<Span<u8>> {    if std.http.requestRouteMethodAllowed(request, "/users/:id", "GET, HEAD") {        return std.http.writeJsonResponseWithHeaders(response, 200_u16, "cache-control: no-store", "{\"ok\":true}")    }    return std.http.writeJsonError(response, 404_u16, "not_found")} pub fn main(world: World) -> Void raises {    var request_storage: [128]u8 = [0_u8; 128]    var response_storage: [256]u8 = [0_u8; 256]    let request: Maybe<Span<u8>> = std.http.testRequest(request_storage, "GET", "/users/7", "")    if request.has {        let response: Maybe<Span<u8>> = handle(request.value, response_storage)        if response.has && std.http.responseMatches(response.value, 200_u16, "application/json", "{\"ok\":true}") {            check world.out.write("handler ok\n")            return        }    }    check world.err.write("handler failed\n")}

Design Notes

std.http.fetch is the outbound HTTP client primitive. The request argument is one byte envelope: METHOD URL, followed by zero or more Header-Name: value lines, a blank line, and optional request body bytes. Use writeRequest and writeJsonRequest when you want the standard library to write that envelope into caller storage. The timeout is a Duration, typically built with std.time.ms(...).

fetch supports http:// and https:// URLs on supported host executable targets, uses the C-library curl provider, does not follow redirects, and verifies TLS through the provider. TLS verification is always enabled. ZERO_HTTP_TEST_CA_BUNDLE can point the provider at an explicit CA bundle while keeping certificate verification on.

The response buffer starts with internal metadata, followed by raw response headers and then the response body. Use responseHeadersLen and responseBodyOffset rather than hard-coding offsets. headerValue scans the response buffer and returns packed offset/length metadata for the matching value; headerFound, headerOffset, and headerLen inspect that metadata without allocating. Prefer headerBytes and responseBody when you need a borrowed byte span and want metadata bounds checked in one call.

Compare resultError with the named HttpError helpers rather than raw numbers. Use errorName(resultError(result)) for diagnostics and logs. errorNone means the transport succeeded; HTTP non-2xx statuses still carry errorNone and can be inspected with resultStatus.

The module does not expose raw socket read/write APIs, streaming bodies, a global router, or a heap-allocated response object.