pub struct StreamingWriter { /* private fields */ }Expand description
Streaming ResponseWriter backed by a bounded tokio mpsc channel
of Bytes.
Coalesces small writes into ~4 KB chunks before flushing. The
underlying channel has a small bound
(DEFAULT_CHANNEL_CAPACITY) so a
slow consumer naturally backpressures the producer — the render
thread parks instead of queuing the entire response in memory.
A flush deadline (with_flush_timeout)
caps the maximum time the producer thread will park per flush,
bounding the slow-loris DoS surface. When the receiver is dropped
(typically client disconnect) or the deadline is exceeded, subsequent
ResponseWriter::write calls return a typed error
(HandlerError::ClientDisconnected / HandlerError::StreamTimeout)
so the handler can short-circuit the render.
§Example
use std::time::Duration;
use tokio::sync::mpsc;
use webui::streaming::StreamingWriter;
let (tx, mut rx) = mpsc::channel(StreamingWriter::DEFAULT_CHANNEL_CAPACITY);
actix_web::rt::task::spawn_blocking(move || {
let mut writer = StreamingWriter::new(tx)
.with_flush_timeout(Duration::from_secs(30));
handler.render(&protocol, &state, &opts, &mut writer);
let _ = ResponseWriter::end(&mut writer);
});
// … wrap rx in a Stream and pass to HttpResponse::streaming …Implementations§
Source§impl StreamingWriter
impl StreamingWriter
Sourcepub const CHUNK_TARGET: usize
pub const CHUNK_TARGET: usize
Default chunk-coalescing target. ~4 KB is a balance: large enough to amortise per-call channel + actix overhead, small enough that head/body content arrives before the body_end script.
Tunable via with_chunk_size.
Sourcepub const DEFAULT_CHANNEL_CAPACITY: usize = 4
pub const DEFAULT_CHANNEL_CAPACITY: usize = 4
Default bounded-channel capacity in chunks. With
CHUNK_TARGET = 4 KB, this caps in-flight memory at ~16 KB per
in-progress request.
Sourcepub fn new(tx: Sender<Bytes>) -> Self
pub fn new(tx: Sender<Bytes>) -> Self
Wrap a tokio mpsc sender. Each render allocates its own chunk
buffers via the system allocator. For pooled allocation across
requests, use new_pooled.
Sourcepub fn new_pooled(tx: Sender<Bytes>, pool: Arc<ChunkPool>) -> Self
pub fn new_pooled(tx: Sender<Bytes>, pool: Arc<ChunkPool>) -> Self
Wrap a tokio mpsc sender, drawing chunk buffers from the
shared pool. Recycled buffers eliminate per-flush allocation
in steady-state high-RPS workloads. The pool is shared via
Arc and is safe to use from any number of concurrent
StreamingWriter instances; release happens when the consumer
drops the Bytes, on whichever thread held the last reference.
chunk_target defaults to CHUNK_TARGET;
override with with_chunk_size. When
the pool’s chunk size disagrees with the writer’s target, the
writer grows the acquired buffer on first use (one-time cost).
Sourcepub fn with_chunk_size(self, bytes: usize) -> Self
pub fn with_chunk_size(self, bytes: usize) -> Self
Override the chunk-coalescing target. Larger chunks reduce channel + syscall overhead at the cost of higher first-byte latency. Values below 64 bytes are silently raised to 64.
Common sizes:
- 1 KB: minimise TTFB on small pages.
- 4 KB (default): balanced for ~24 KB SSR pages.
- 16 KB: match TLS record size for large SSR (>200 KB).
Sourcepub fn with_flush_timeout(self, timeout: Duration) -> Self
pub fn with_flush_timeout(self, timeout: Duration) -> Self
Cap the maximum time a flush may park on the channel before
returning HandlerError::StreamTimeout. None (default) means
flushes block indefinitely on slow consumers.
Production HTTP hosts should set this (e.g. 30 s) so a single slow-loris client cannot pin a render thread forever. The chosen timeout × concurrent-render-limit is the upper bound on resources an attacker can pin.
Requires an active tokio runtime to be in TLS (i.e. the writer
is being driven from a spawn_blocking task on a tokio runtime).
Without one, the timeout is silently ignored and a plain
blocking_send is performed.
Sourcepub fn is_terminated(&self) -> bool
pub fn is_terminated(&self) -> bool
True after the writer has been terminated by a disconnect or flush timeout.
Trait Implementations§
Source§impl Drop for StreamingWriter
impl Drop for StreamingWriter
Source§impl FlushWriter for StreamingWriter
impl FlushWriter for StreamingWriter
Auto Trait Implementations§
impl Freeze for StreamingWriter
impl RefUnwindSafe for StreamingWriter
impl Send for StreamingWriter
impl Sync for StreamingWriter
impl Unpin for StreamingWriter
impl UnsafeUnpin for StreamingWriter
impl UnwindSafe for StreamingWriter
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more