Expand description
Streaming ResponseWriter helpers for actix-web (or any) HTTP host.
webui-handler writes through a push-based ResponseWriter trait —
every Raw fragment, attribute, signal value, route element open/close,
CSS preload <link>, and template assignment is a separate write()
call (~hundreds per render). The default host pattern collects them all
into a String, then serves the whole HTML body in one shot — which
delays first-byte until the entire render finishes and forces the
browser to wait for everything before parsing.
The helpers here let a host flush bytes to the network as soon as they’re written:
-
StreamingWriter— coalesces small writes into ~4 KB chunks and pushes them through a boundedtokio::sync::mpsc::Sender. The bound (DEFAULT_CHANNEL_CAPACITY = 4chunks ≈ 16 KB) provides backpressure: when a slow client cannot keep up, the producer parks on the channel until the receiver drains, instead of queuing the entire response in memory. A configurable flush deadline (viawith_flush_timeout) caps the maximum time a producer thread can be parked, bounding the slow-loris DoS surface totimeout × concurrent_renders. When the receiver is dropped (client disconnect) or the deadline elapses,writereturns a typed error so the handler aborts instead of doing wasted CPU work. -
ChunkPool— lock-free shared pool of chunk buffers. Used viaStreamingWriter::new_pooledto recycle the per-flushVec<u8>across requests, eliminating per-flush heap allocation in steady-state high-RPS workloads.
Hot-path allocation profile:
StreamingWriter::new()(unpooled): oneVec::reserveper ~4 KB flush (the previous buffer is moved zero-copy intobytes::Byteswhenlen < cap; whenlen == cap,Bytes::from(Vec)is still a move viainto_boxed_slice). Plus one smallBox<Shared>for the refcount metadata.StreamingWriter::new_pooled(): zero per-flush heap allocation in steady state — chunk buffers come from the pool and return onBytesdrop. Single atomic CAS per acquire/release.
§Per-render HTML injection
Hosts that need to splice HTML at the structural </head> or </body>
boundaries (image preload <link> tags, dev livereload <script>,
CSP nonce reflections, analytics, etc.) should use
RenderOptions::with_head_inject / RenderOptions::with_body_inject
on the handler side — NOT a writer-level scanner. The parser already
synthesises head_end / body_end signal fragments at the exact
structural boundaries; the handler emits the inject HTML there with
zero scan cost and no risk of mis-firing on </body> literals
appearing inside HTML comments, <iframe srcdoc>, or inline scripts.
Structs§
- Chunk
Pool - Lock-free shared pool of
Vec<u8>buffers used to recycle chunk allocations acrossStreamingWriterinstances. - Streaming
Writer - Streaming
ResponseWriterbacked by a bounded tokio mpsc channel ofBytes.