Skip to content

Response

An HTTP response.

A response may hold an open connection while its body is still pending, so it owns a resource until it is closed. Every method that reads the body whole -- body, text, json -- consumes the response and closes it; the streaming methods hand ownership of the body to the iterator they return. Once closed, most methods raise.

The reliable way to bound that lifetime is the trailing block a request accepts, which closes the response however the block exits:

get https://api.example.com/users do |response|
  echo $response.status

Fields

headers @ Map[Str, Str | time.DateTime] [get]

The response headers.

A value that parses as an HTTP-date is returned as a time.DateTime; every other value is a Str.

Example
let response = get https://api.example.com/users
echo $response.headers["content-type"]
status @ Int [get]

The HTTP status code.

url @ url.Url [get]

The final URL, after any redirects.

Methods

body() -> Bin

Reads the whole body as bytes.

Reading the body consumes it: body, text, json, io, chunks and lines each take it, and a second raises StateError. The status and headers stay readable until the response is closed.

chunks() -> Iter[Bin]

Streams the body as byte chunks of at most 64 KiB each, as io().chunks() does, so a large response need not be held in memory whole.

Example

get https://api.example.com/large-file do |response|
  let total = 0
  for chunk = response.chunks()
    total = (total + chunk.len)
  echo "Downloaded $total bytes"

close()

Closes the response, releasing the connection, and closes any stream io, chunks or lines returned.

Closing an already-closed response does nothing.

events() -> Iter[Event]

Streams the body as a Server-Sent Events stream -- the shape LLM responses, log tails and other text/event-stream feeds arrive in.

Errors

Exception Condition
StateError The body was read or the response closed
Error The underlying body read fails
DataError The stream contains invalid UTF-8

Example

http.get https://api.example.com/stream do |response|
  for :type :data ... = response.events()
    echo "[$type] $data"

io() -> io.Io

Returns the body as a byte stream.

The stream can be passed to any function that accepts an io.Io, such as compress.decompress. Closing the response closes it.

A failure reading the body raises from the stream's methods as an I/O error, not an Error.

Example

get https://example.com/data.gz do |response|
  compress.decompress $response.io() do |stream|
    echo (stream.read_all_str())

json() -> json.DecodeValue

Reads the whole body and parses it as JSON.

Errors

Exception Condition
StateError The body was read or the response closed
Error A transport or protocol failure
DataError The body is not valid JSON

Example

let response = get https://api.example.com/users
let data = response.json()
echo $data["users"][0]["name"]

lines() -> Iter[Str]

Streams the body as lines, split on \n or \r\n with the line ending stripped.

The lines are read through an io.Buffered over io().

Example

get https://api.example.com/logs do |response|
  for line = response.lines()
    if (line.contains "ERROR")
      echo $line

text() -> Str

Reads the whole body as text, replacing invalid UTF-8 with U+FFFD.

Errors

Exception Condition
StateError The body was read or the response closed
Error A transport or protocol failure

throw_for_status()

Raises Status when the status is outside 200..=299, and does nothing otherwise.

This is what a request does for you unless status: :IGNORE: was passed, so it is only needed on a response fetched with the check suppressed.