Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Keep stream errors and progress

Use std.io.typed when a transfer needs the original cause and the number of bytes delivered before failure. Its copy error distinguishes reading from writing. Existing std.io streams can enter this API through bridges:

import std.http.typed

import std.bytes
import std.io
import std.io.typed
from std.http.typed import fixed_body
from std.io.typed import CopyFailure, copy_all

def main() -> Result[(), AllocError | IoError | std.http.typed.FramingError[std.io.StreamError] | std.bytes.BytesError]:
    mut bytes = std.bytes.with_capacity(3)?
    bytes.resize(3, 65u8)?
    mut scratch = std.bytes.with_capacity(2)?
    scratch.resize(2, 0u8)?
    mut body = fixed_body(std.io.typed.reader(std.io.cursor(bytes)), 3)?
    mut output = std.io.typed.writer(std.io.cursor(std.bytes.with_capacity(3)?))
    match copy_all(&mut body, &mut output, &mut scratch):
        case Ok(count):
            print(count)?
        case Err(CopyFailure.Read(count, cause)):
            print("read failed after")?
            print(count)?
            drop(cause)
        case Err(CopyFailure.Write(count, cause)):
            print("write failed after")?
            print(count)?
            drop(cause)
    cursor = std.io.typed.unwrap_writer(output)
    print(cursor.position())?
    Ok(())
3
3

The fixed body assumes an already validated Content-Length; it does not parse HTTP. close_body instead finishes at EOF and requires a connection that will never be reused. The copy helper retries explicit interruption and preserves other transport causes. A write failure can leave unwritten data from the last chunk in scratch; the delivered count alone is not a resume token.

For an HTTP message, use typed head parsing followed by request_body or response_body. They check framing fields and select the body decoder with explicit limits. This in-memory response uses chunked framing. Keep the entire buffered reader when handing it to the body, so prefetched bytes are preserved:

import std.bytes
import std.http
import std.io
import std.io.typed
from std.http.typed import FramingError, read_response, response_body
from std.io.typed import CopyFailure, Partial, ReadFailure, copy_all

type TransportCause = ReadFailure[std.io.StreamError]
type HttpCause = FramingError[TransportCause]

def main() -> Result[(), AllocError | IoError | std.bytes.BytesError | std.io.StreamError | std.http.HttpError | Partial[HttpCause] | HttpCause | CopyFailure[HttpCause, std.io.StreamError]]:
    wire = std.bytes.from_text("HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n3\r\nABC\r\n0\r\nX-Tag: sample\r\n\r\n")?
    limits = std.http.Limits(64, 128, 4, 1024)
    mut conn = std.io.typed.buffered_reader(std.io.typed.reader(std.io.cursor(wire)), 128)?
    head = read_response(&mut conn, &limits)?.ok_or(std.http.HttpError.Truncated)?
    # Our GET request allows keep-alive; the response must also allow it.
    mut body = response_body(conn, &head, "GET", True, &limits)?
    mut output = std.io.typed.writer(std.io.cursor(std.bytes.with_capacity(3)?))
    mut scratch = std.bytes.with_capacity(2)?
    scratch.resize(2, 0u8)?
    print(copy_all(&mut body, &mut output, &mut scratch)?)?
    print(body.is_done(), body.trailers()[0].value)?
    # finish checks completion and persistence before handing back ownership.
    recovered = body.finish()?
    drop(recovered)
    Ok(())
3
True sample

Only decoded payload contributes to the copy count. The decoder keeps partial framing state across interruption and stops after the trailers; it does not parse an HTTP head. The parsed-head constructor validates framing; finish checks whether this completed message permits recovering its connection. Dropping an unfinished or failed body drops its connection. Close-delimited, closing, upgraded and successful CONNECT connections cannot be recovered by this API. The reference describes the remaining caller responsibilities and the lower-level chunked_body adapter.

For real requests, use std.http.client, which uses these typed paths and also handles deadlines, connection ownership and reuse. typed_response(response) adapts its response to the typed reader protocol while preserving ClientError; unwrap it before client.finish. response.read_all_progress is convenient when collecting a body and retaining both its original error and the number of bytes appended before failure.

See the typed streams reference for nested error payloads and the framing reference for terminal-state rules.