Response

Constructors, header rules, cookies and what the server owns.

Updated 2 min read

record Response {
  var status: int = 200;
  var headers: Headers;
  var body: Bytes;
  var closeConnection: bool = false;
}

Constructors

jsonResponse(payload, status = 200)
htmlResponse(markup, status = 200)
textResponse(text, status = 200)
bytesResponse(payload, mime, status = 200)
redirect(location, status = 303)
noContent()
errorResponse(status, detail = "")

errorResponse escapes detail, because it is a public parameter and a handler could pass request-derived text into it.

Headers the server owns

Content-Length, Date, Server and Connection are written at serialisation time and cannot be overridden by a handler. Every other header name and value is validated on the way out: CR, LF and NUL in a value are rejected, so a handler cannot inject a header or split a response. An invalid field is dropped and logged rather than emitted.

Cookies

response.setCookie("session", token, maxAge = 3600);

setCookie defaults to HttpOnly, Secure, SameSite=Lax and Path=/, and percent-encodes the value. Those defaults are the safe ones; loosening them is an explicit argument at the call site, which is where such a decision should be visible.

Status codes from a page

Pages do not construct a Response. They set meta.status, and the generated handler builds the response around the rendered document:

meta.status = 404;

That keeps a page's return type a plain string while still letting it answer honestly.

Redirects

proc get(ctx: Context): Response {
  return redirect("/docs/introduction", 302);
}

A route that only redirects is an API route, not a page โ€” it has no markup to render. This wiki uses exactly that for /docs, because a catch-all does not match its own parent path.