nhttp_stream (nhttp v1.0.0)

View Source

Streaming response constructors for nhttp.

Handlers return {stream, nhttp_stream:spec(), State} from nhttp_handler:handle_request/2 to start a chunked response. Build the spec() with producer/3.

A fun((SendChunk) -> _) runs in a dedicated worker process. SendChunk/1 blocks until the connection has accepted the chunk, applying end-to-end backpressure (TCP for HTTP/1.1, stream flow control for HTTP/2 and HTTP/3). On peer close SendChunk/1 returns {error, closed} so the producer can clean up via try/after.

handle_request(#{method := get, path := <<"/export.csv">>}, State) ->
    Producer = fun(SendChunk) ->
        SendChunk(<<"id,name\\n">>),
        Cursor = db:open_cursor(users),
        try
            stream_rows(Cursor, SendChunk)
        after
            db:close_cursor(Cursor)
        end
    end,
    Headers = [{<<"content-type">>, <<"text/csv">>}],
    Spec = nhttp_stream:producer(200, Headers, Producer),
    {stream, Spec, State}.

Trailers

Return {trailers, Headers} from the producer fun to send HTTP trailers after the body (RFC 9110 §6.5). Trailers terminate the response stream.

Producer = fun(SendChunk) ->
    _ = SendChunk(ResponseBody),
    {trailers, [{<<"grpc-status">>, <<"0">>}]}
end.

Trailers are wired natively on HTTP/2. On HTTP/1.1 the response is terminated with the chunked-encoding terminator (without the trailer fields, since most clients do not implement RFC 9112 §7.1.2 trailer parsing). On HTTP/3 the stream is closed with FIN.

Summary

Functions

Build a producer-form streaming spec.

Types

producer()

-type producer() :: fun((send_chunk_fun()) -> ok | {trailers, nhttp_lib:headers()} | {error, term()}).

send_chunk_fun()

-type send_chunk_fun() :: fun((iodata()) -> ok | {error, closed | timeout}).

spec()

-type spec() :: {producer, nhttp_lib:status(), nhttp_lib:headers(), producer()}.

Functions

producer(Status, Headers, Producer)

-spec producer(nhttp_lib:status(), nhttp_lib:headers(), producer()) -> spec().

Build a producer-form streaming spec.

Producer is a fun of arity 1 that receives a SendChunk fun and runs in a dedicated worker process. SendChunk(IoData) blocks until the chunk has been accepted by the connection (end-to-end backpressure). On peer close it returns {error, closed}.

The producer terminates the body by returning. Returning ok (or {error, _}) closes the stream cleanly. Returning {trailers, Headers} sends those trailers as the terminator.