Files
wehub-resource-sync e7738de6d2
CI / Deep Native Runtime Cases (1/6) (push) Has been skipped
CI / Native Preflight (push) Failing after 1s
CI / Native Runtime Cases (1/2) (push) Failing after 0s
CI / Native Runtime Cases (2/2) (push) Failing after 1s
CI / Native Metadata Reports (push) Failing after 0s
CI / Native Direct Backend Artifacts (push) Failing after 0s
CI / Native Sanitizer Smoke (push) Failing after 1s
CI / Command Contract Snapshots (push) Failing after 1s
CI / Deep Conformance Suite (push) Has been skipped
CI / Graph Build Perf (push) Failing after 1s
CI / Deep Native Preflight (push) Has been skipped
CI / Deep Native Runtime Cases (2/6) (push) Has been skipped
CI / Deep Native Runtime Cases (3/6) (push) Has been skipped
CI / Conformance Suite (push) Failing after 1s
CI / Workspace Checks (push) Failing after 0s
CI / Deep Native Runtime Cases (5/6) (push) Has been skipped
CI / Deep Native Runtime Cases (6/6) (push) Has been skipped
CI / Deep Native Runtime Cases (4/6) (push) Has been skipped
CI / Deep Graph Build Perf (push) Has been skipped
chore: import upstream snapshot with attribution
2026-07-13 12:29:30 +08:00

407 lines
27 KiB
Markdown

## 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:
| API | Return | Notes |
| --- | --- | --- |
| `std.http.parseMethod(text)` | `HttpMethod` | Parses a small HTTP method token. |
| `std.http.client(net)` | `HttpClient` | Creates hosted client metadata from a network capability. |
| `std.http.server(net, address)` | `HttpServer` | Creates 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)` | `HttpResult` | Performs a hosted HTTP(S) request with a `Duration` timeout and writes response metadata, headers, and body into caller-owned storage. |
| `std.http.resultOk(result)` | `Bool` | True when transport succeeded and the status is 2xx. |
| `std.http.resultStatus(result)` | `u16` | Reads the HTTP status, or `0` when no status was available. |
| `std.http.resultBodyLen(result)` | `usize` | Reads the number of response body bytes written into the response buffer. |
| `std.http.resultError(result)` | `HttpError` | Reads the transport/provider error. |
| `std.http.errorNone()` | `HttpError` | Transport succeeded. |
| `std.http.errorInvalidUrl()` | `HttpError` | The request URL was invalid. |
| `std.http.errorUnsupportedProtocol()` | `HttpError` | The URL protocol is not supported. |
| `std.http.errorDns()` | `HttpError` | DNS lookup failed. |
| `std.http.errorConnect()` | `HttpError` | Connecting to the remote host failed. |
| `std.http.errorTls()` | `HttpError` | TLS verification or connection setup failed. |
| `std.http.errorTimeout()` | `HttpError` | The request timed out. |
| `std.http.errorTooLarge()` | `HttpError` | The response did not fit in the caller-owned buffer. |
| `std.http.errorProviderUnavailable()` | `HttpError` | The hosted HTTP provider is unavailable. |
| `std.http.errorIo()` | `HttpError` | The provider reported an I/O failure. |
| `std.http.errorInvalidRequest()` | `HttpError` | The request envelope was invalid. |
| `std.http.errorName(error)` | `String` | Returns a stable label for a transport error. |
| `std.http.responseLen(response)` | `usize` | Reads the response byte count written after the internal metadata prefix. |
| `std.http.responseHeadersLen(response)` | `usize` | Reads the raw response header byte count. |
| `std.http.responseBodyOffset(response)` | `usize` | Reads the body start offset within the response buffer. |
| `std.http.headerValue(response, name)` | `HttpHeaderValue` | Locates a response header value by case-insensitive header name. |
| `std.http.headerFound(value)` | `Bool` | True when `headerValue` found a matching header. |
| `std.http.headerOffset(value)` | `usize` | Reads the header value byte offset within the response buffer. |
| `std.http.headerLen(value)` | `usize` | Reads the header value byte length. |
| `std.http.tlsBoundary()` | `String` | Names the platform or C-library TLS boundary. |
| `std.http.statusReason(status)` | `String` | Returns a common reason phrase for status-line writing. |
| `std.http.statusIsInformational(status)` | `Bool` | True for 1xx statuses. |
| `std.http.statusIsSuccess(status)` | `Bool` | True for 2xx statuses. |
| `std.http.statusIsRedirect(status)` | `Bool` | True for 3xx statuses. |
| `std.http.statusIsClientError(status)` | `Bool` | True for 4xx statuses. |
| `std.http.statusIsServerError(status)` | `Bool` | True 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)` | `String` | Returns 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)` | `usize` | Counts 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)` | `Bool` | Matches 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)` | `usize` | Counts 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)` | `Bool` | Checks 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)` | `Bool` | Checks 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)` | `Bool` | True 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)` | `Bool` | True when a request envelope has the exact method and normalized path. |
| `std.http.methodAllowed(method, allowed)` | `Bool` | Checks a method against a comma-separated allow list. |
| `std.http.requestMethodAllowed(request, allowed)` | `Bool` | Checks a request method against a comma-separated allow list. |
| `std.http.requestMethodIs(request, method)` | `Bool` | True when a request envelope has the exact method. |
| `std.http.requestIsGet(request, path)` | `Bool` | True when a request envelope is `GET` for the normalized path. |
| `std.http.requestIsHead(request, path)` | `Bool` | True when a request envelope is `HEAD` for the normalized path. |
| `std.http.requestIsOptions(request, path)` | `Bool` | True when a request envelope is `OPTIONS` for the normalized path. |
| `std.http.requestIsPost(request, path)` | `Bool` | True when a request envelope is `POST` for the normalized path. |
| `std.http.requestIsPut(request, path)` | `Bool` | True when a request envelope is `PUT` for the normalized path. |
| `std.http.requestIsPatch(request, path)` | `Bool` | True when a request envelope is `PATCH` for the normalized path. |
| `std.http.requestIsDelete(request, path)` | `Bool` | True when a request envelope is `DELETE` for the normalized path. |
| `std.http.requestPathStartsWith(request, prefix)` | `Bool` | True 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)` | `Bool` | True when the method matches and the path matches a segment pattern. |
| `std.http.requestRouteMethodAllowed(request, pattern, allowed)` | `Bool` | True 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)` | `Bool` | Validates 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)` | `Bool` | Checks 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)` | `Bool` | Compares a local response body with expected bytes. |
| `std.http.responseMatches(response, status, contentType, body)` | `Bool` | Checks 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:
```zero
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:
```zero
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:
```zero
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:
```zero
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:
```zero
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:
```zero
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:
```zero
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:
```zero
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:
```sh
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:
```zero
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.