mod async_body
srcReactor-integrated external streaming source.
The synchronous ChunkSource (flare.http.body) is a pull: each
next returns the next chunk or None for EOF. That is exactly
right for an in-process FIFO (e.g. SseChannel) but it can never
wait -- it has no way to say "nothing yet, the bytes are coming on
another fd." A streaming proxy whose chunks arrive on a UDS / pipe /
eventfd needs that third state, or every front re-implements an epoll
loop and a hand-rolled fd->client copy.
This module adds the missing state as a small, total API:
ChunkPoll-- a tri-state poll result. Exactly one of: a ready chunk, "pending, wake me on this fd", or EOF. The three states are constructed by named factories (ready/pending/eof) and read by predicates, so an illegal combination (a chunk and a wait fd) is unrepresentable.AsyncChunkSource-- the async sibling ofChunkSource: one method,poll(cancel) -> ChunkPoll. Synchronous sources keep usingChunkSource; this is purely additive.UpstreamChunkSource-- the concrete impl a streaming proxy needs: a single framed logical stream over a non-blocking connection. It composes the frame codec (FrameDemux) for parsing and reportspending(fd)on EAGAIN so the reactor parks instead of busy-polling.
Composition with the typed streaming surface
A handler drives an AsyncChunkSource with no bespoke reactor code,
no file descriptors, and no per-connection bookkeeping -- attach the
source and let the framework pump it::
def on_open(mut self, mut conn: StreamConn) raises:
conn.attach_upstream(UpstreamChunkSource.connect(self.worker))
def on_upstream(mut self, mut conn: StreamConn) raises:
conn.relay_upstream() # drains ready chunks, EOF -> close
attach_upstream takes the source (it reads the fd to watch
internally and owns the source for the connection's lifetime, closing
it on teardown), and relay_upstream is the standard drain loop:
pull ready chunks into the client with backpressure, request close on
EOF, park on pending. A front that needs custom per-chunk handling can
still drive the source directly via conn.upstream().poll(...).
The reactor calls on_upstream only when the attached fd is readable,
so the pending branch is a genuine park, not a poll loop (no
busy-poll between gaps). Watermark backpressure additionally gates the
upstream read interest when the client is write-blocked.
Structs
| struct ChunkPoll | The result of one ``AsyncChunkSource.poll`` -- a total tri-state. |
| struct UpstreamChunkSource | One framed logical stream over a non-blocking connection. |
Traits
| trait AsyncChunkSource | A byte-chunk source whose chunks may arrive on a registered fd. |
Structs
struct ChunkPoll §
struct ChunkPoll
The result of one ``AsyncChunkSource.poll`` -- a total tri-state.
Exactly one state holds:
- ready: a chunk is available now (
is_ready(); take it withtake_chunk()). - pending: nothing yet; the caller should wait until
wait_fd()is readable and poll again (is_pending()). No busy-poll. - eof: the stream is complete; no more chunks (
is_eof()).
Built only through ready / pending / eof so a chunk can
never coexist with a wait fd.
Methods
| fn __init__ | Internal: prefer the ``ready`` / ``pending`` / ``eof`` factories. |
| fn ready | A chunk is available now. |
| fn pending | Nothing yet; wake and re-poll when ``fd`` is readable. |
| fn eof | The stream is complete. |
| fn is_ready | True if a chunk is available now. |
| fn is_pending | True if the source is waiting on ``wait_fd()``. |
| fn is_eof | True if the stream is complete. |
| fn wait_fd | The fd to wait on (valid only when ``is_pending()``; ``-1`` otherwise). |
| fn take_chunk | Move the ready chunk out (valid only when ``is_ready()``; leaves the poll holding an empty chunk). |
| fn consume | Collapse the ready/eof split into one move-out. |
fn wait_fd §
def wait_fd(self) -> c_int
The fd to wait on (valid only when ``is_pending()``; ``-1`` otherwise).
Args
| self | Self |
Returns
| c_int |
fn consume §
Collapse the ready/eof split into one move-out.
Returns Some(chunk) when a chunk is ready (moved out, as
take_chunk) and None on EOF. Raises on pending: a
pending poll must be parked on wait_fd() and re-polled, never
consumed. This is the one-call form for callers that drain to EOF
and have already handled the pending branch (the reactor only
delivers an upstream edge when the fd is readable, so a relay loop
sees only ready / eof).
Args
| self mut | Self |
Raises
May raise an exception.
struct UpstreamChunkSource §
struct UpstreamChunkSource
One framed logical stream over a non-blocking connection.
Owns a UnixStream to a worker and reads frames for a single
request_id off it. poll drains any buffered CHUNK frame,
else does one non-blocking read: a completed CHUNK -> ready;
a DONE / connection EOF -> eof; EAGAIN -> pending(fd)
so the reactor waits on the fd instead of spinning; ERROR raises.
The frame parsing is the same FrameDemux the multiplexed
FrameMux uses, so a worker can speak one wire shape to both
the single-stream and multiplexed fronts.
ponytail: one source owns one connection (a dedicated framed link).
The many-streams-over-one-connection case is the handler owning a
FrameMux as shared state and driving it directly; a future
FrameMux.open returning a lightweight per-stream handle is the
multiplexed evolution (it needs a shared-mux reference the fixed
trait method cannot carry in the current Mojo).
Fields
| conn | UnixStream | The owned framed upstream connection (set non-blocking at init). |
| demux | FrameDemux | Frame reassembly for the inbound byte stream. |
| request_id | UInt64 | The single logical stream this source reads. |
Methods
| fn __init__ | Adopt ``conn`` (switched to non-blocking) for ``request_id``. |
| fn connect | Open a dedicated framed upstream over the UDS at ``path``. |
| fn fd | The upstream fd to register / wait on (for ``attach_upstream``). |
| fn send_cancel | Emit a CANCEL frame for this ``request_id`` upstream. |
| fn poll | Drain a ready frame, else one non-blocking read; never blocks. |
fn __init__ static §
def __init__(out self, var conn: UnixStream, request_id: UInt64 = UInt64(1))
Adopt ``conn`` (switched to non-blocking) for ``request_id``.
request_id defaults to 1 -- a dedicated single-stream link
carries one logical stream, so the id is meaningful only when a
front multiplexes several streams over one connection.
Args
| conn var | UnixStream | |
| request_id | UInt64 |
Default: UInt64(1)
|
| self out | Self |
Returns
| Self |
Raises
May raise an exception.
fn connect static §
Open a dedicated framed upstream over the UDS at ``path``.
The one-call convenience: dials the worker and adopts the
connection, so a front never assembles a UnixStream by hand
just to feed it here. Mirrors UnixStream.connect /
TcpStream.connect.
var src = UpstreamChunkSource.connect("/run/backend.sock")
Returns
| Self |
Raises
May raise an exception.
fn fd §
def fd(self) -> c_int
The upstream fd to register / wait on (for ``attach_upstream``).
Args
| self | Self |
Returns
| c_int |
fn send_cancel §
def send_cancel(mut self)
Emit a CANCEL frame for this ``request_id`` upstream.
Tells the backend to stop producing tokens nobody will read --
e.g. the client disconnected mid-generation. Idempotent and
best-effort framed (a 13-byte CANCEL fits one write); after this
the source is EOF. Call it from a front's on_close when the
connection's cancel is set, and poll calls it too when it
observes cancellation on an upstream edge.
Args
| self mut | Self |
Raises
May raise an exception.
Traits
trait AsyncChunkSource §
trait AsyncChunkSource
A byte-chunk source whose chunks may arrive on a registered fd.
The async sibling of ChunkSource: instead of a blocking pull,
poll returns immediately with one of the three ChunkPoll
states. A source that has data returns ready; a source still
waiting for bytes on its fd returns pending(fd) (the caller parks
the response on that fd); a finished source returns eof.
Implementors must not block in poll -- that is the whole point.
The cancel token lets a source short-circuit on client FIN /
deadline / drain.
Required Methods
| fn __init__ | Create a new instance of the value by moving the value of another. |
| fn poll | Return the next chunk, a pending-on-fd signal, or EOF. |
fn __init__ static §
def __init__(out self: _Self, *, deinit move: _Self)
Create a new instance of the value by moving the value of another.
Args
| move deinit | _Self |
The value to move. |
| self out | _Self |
Returns
| _Self |