pub struct BodyRenderer(/* private fields */);Expand description
The path from a stored content_html row to a BodyRender, with the
three bounds. One process-wide instance, BodyRenderer::shared, serves
the reader; tests build their own with smaller parameters.
For a body of n bytes a render costs, in order:
- nothing but a length check if
n>MAX_RENDER_HTML_BYTES(BodyRender::TooLarge); - a SHA-256 of the body and a cache lookup, on the blocking pool;
- on a miss, single flight: if the same body is already being cleaned, this request waits for that result and takes no permit; otherwise it becomes the leader and spawns the clean as its own task, so a requester that disconnects neither cancels the clean nor leaves anyone waiting on a leader that is gone;
- the leader waits up to
RENDER_WAITfor one ofRENDER_PERMITSpermits (BodyRender::Unavailablefor everyone waiting if none comes), then cleans on the blocking pool holding the permit, caches the result and publishes it.
So each distinct body is cleaned at most once at a time; a fast body again
only after it leaves the cache; and a body that was slow to clean
(SLOW_CLEAN, 500 ms) at most once per process — after its result leaves the
cache it renders as BodyRender::TooSlow instead (SlowSet). A slow
body therefore costs one clean, holding one permit, per process. The
async worker is never blocked: the hash, the lookup and the clean run on
the blocking pool, and every wait is an async one.
What these bounds are for. A stored body may be slow to re-clean:
ingest can store one (#226; real bodies p50 31 µs, max 3.5 ms, but a
hostile feed’s 2 MiB of nested <div>s re-cleans in ~37 s, a & run in
2.4 s), and such rows may already exist in databases upgraded from
≤ 0.4.6. The bounds here cap what such a row can cost other readers: it is
cleaned once per process, holding one of two permits, and everyone else’s
uncached body waits at most RENDER_WAIT before a note. They do not
predict or reduce its own cost.
Implementations§
Source§impl BodyRenderer
impl BodyRenderer
Sourcepub fn new(
permits: usize,
wait: Duration,
cache_bytes: usize,
cache_entries: usize,
) -> Self
pub fn new( permits: usize, wait: Duration, cache_bytes: usize, cache_entries: usize, ) -> Self
A renderer with its own permits and cache. The reader uses
BodyRenderer::shared; this is for tests and for the shared
instance’s construction.
The process-wide renderer, with RENDER_PERMITS, RENDER_WAIT,
CACHE_MAX_BYTES and CACHE_MAX_ENTRIES.
Sourcepub async fn render(&self, raw: String) -> Result<BodyRender>
pub async fn render(&self, raw: String) -> Result<BodyRender>
A stored body, cleaned for this render — or the reason it is not.
Err only if the blocking pool failed to run the task (a panic in the
sanitizer, or runtime shutdown); the two bounded outcomes are values.
Sourcepub fn cache_size(&self) -> (usize, usize)
pub fn cache_size(&self) -> (usize, usize)
Bodies in the cache, and the bytes of cleaned markup they hold.
Trait Implementations§
Auto Trait Implementations§
impl !RefUnwindSafe for BodyRenderer
impl !UnwindSafe for BodyRenderer
impl Freeze for BodyRenderer
impl Send for BodyRenderer
impl Sync for BodyRenderer
impl Unpin for BodyRenderer
impl UnsafeUnpin for BodyRenderer
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> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<A, B, T> HttpServerConnExec<A, B> for Twhere
B: Body,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
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