Chapter 18: HTTP And Streams

Part III: Host Capabilities and Local App Surfaces

Why this chapter matters

HTTP connects Ricochet programs to services. Streams let programs handle data progressively instead of pretending every response is a small string.

What you will build

You will build a small API client that talks to a loopback test server, sends JSON, awaits HTTP tasks, reads a retained response stream in bounded chunks, and releases the stream when it is done.

Concepts in plain English

An HTTP request is data sent to a host. An HTTP response is data returned from that host. Streams represent data you read or write in pieces.

The chapter uses these concepts:

Vocabulary and commands

Primary coverage: http_request_new, http_header_put, http_bearer_auth, http_json_body, http_timeout, http_get, http_get_task, http_request, http_post_json, http_post_json_task, http_request_task, http_stream_start, http_streams, http_stream, http_stream_read, http_stream_cancel, and http_stream_release.

Guided example

Open examples/learn/18-http-streams/api-client.rco. The example expects a base URL, so run it through the local fixture:

codepowershell -ExecutionPolicy Bypass -File examples/learn/18-http-streams/run-local.ps1

The fixture starts a loopback HTTP server and runs the example with an explicit host allow-list:

coderco run --capability-profile trusted --http-allow-host 127.0.0.1 examples/learn/18-http-streams/api-client.rco http://127.0.0.1:PORT

Simple calls are the shortest path when you only need a URL and a small body:

code$baseUrl "/ping" concat http_get value getResponse var
$getResponse "status" at println

payload map
$payload "message" "hello from Ricochet" put drop
$baseUrl "/messages" concat $payload http_post_json value postResponse var

Use a structured request map when the request has policy, headers, or a prepared body:

code"POST" $baseUrl "/structured" concat http_request_new value request var
$request "X-Learn" "manual" http_header_put value request set
$request "learn-token" http_bearer_auth value request set
$request $payload http_json_body value request set
$request 3000 http_timeout value request set
$request http_request value structuredResponse var

The helper words return a new request map as a Result, so unwrap the value and store it back into the same variable before the next helper.

Task variants let you start HTTP work and await the result later:

code$baseUrl "/task" concat http_get_task getTask var
$getTask await value taskResponse var
$taskResponse "body" at println
$getTask release_task drop

Retained streams are for responses you want to inspect incrementally. Start with a structured request, keep the returned id, and read with an options map:

code"GET" $baseUrl "/stream" concat http_request_new value streamRequest var
$streamRequest 3000 http_timeout value streamRequest set
$streamRequest "max_response_bytes" 128 put drop
$streamRequest http_stream_start value stream var
$stream "id" at streamId var

readOptions map
$readOptions "max_bytes" 6 put drop
$streamId $readOptions http_stream_read value firstChunk var
$readOptions "offset" $firstChunk "next_offset" at put drop
$streamId $readOptions http_stream_read value secondChunk var
$readOptions "offset" $secondChunk "next_offset" at put drop

http_stream_read reports from_offset, next_offset, bytes_len, and done. Store next_offset as the next read’s offset to avoid rereading bytes. When the retained job has stopped running, release it:

code$streamId http_stream value streamState var
$streamState "running" at while
  25 sleep
  $streamId http_stream value streamState set
end

$streamId http_stream_release value println

If a retained request is no longer useful, call http_stream_cancel with the stream id. Cancellation asks the host to stop the work; still check the stream snapshot before release, because a running stream cannot be released.

How to read the example

Read the command first, then the code. The command grants the host powers the program may use. Inside the program, host calls usually return Result values or retained resource handles, so keep the same habit from Chapter 09: check the result, unwrap deliberately, and release or close resources when you are done.

Try it

Change the fixture response for /stream and rerun the script. Then lower the example’s max_response_bytes from 128 to 10 and watch the stream snapshot report truncation.

For a stricter run that does not use the stream polling sleep, you can run a simple request under the sandboxed profile:

coderco run --capability-profile sandboxed --http-allow-host 127.0.0.1 examples/learn/18-http-streams/api-client.rco http://127.0.0.1:PORT

The full fixture uses the trusted profile only because sleep is disabled in the sandboxed profile. The HTTP host is still restricted to loopback by --http-allow-host 127.0.0.1.

Check your understanding

Common mistakes

Safety notes

The runnable example uses a local loopback server. It does not call public services and it does not write files. Keep examples like this bounded: use timeouts, byte caps, explicit host allow-lists, and release retained handles. Do not print bearer tokens or resolved secrets.

Production guidance

Production clients should set timeout_ms, bound response size with max_response_bytes, and model failures as Result values. For external APIs, pair http_bearer_auth with secret_env or another secret reference instead of hard-coding tokens. Use task variants when an app can keep working while a request is in flight, and use retained streams when the response may be large or should be consumed incrementally.

What you know now

You know how to make simple and structured HTTP requests, run HTTP requests as tasks, read retained streams with offsets, cancel work that is no longer needed, and release completed streams.

Next step

Continue to Chapter 19: TCP And WebSocket Sockets.