Expand description
The reader view’s article body.
This is its own module because of the private field, for the reason
safe_link.rs gives: a private field is private to the MODULE, so a type
declared in web.rs could be built beside its own constructor there and the
guarantee would be a convention again.
Two things live here. SanitizedHtml is the type: markup that has been
through the ingest sanitizer in this process. BodyRenderer is the path a
stored body takes to become one at render, with the three bounds that keep
a pathological row from costing more than its own page view: a size cap, a
concurrency limit and a cache.
Why bounds, and why these. Real bodies re-clean cheaply: on 545 real
bodies from 20 public feeds, p50 31 µs, p99 1.1 ms, max 3.5 ms (a 561 KB
body). But the sanitizer is quadratic on shapes any feed can serve, and
ingest can store them (#226, fixed separately): a stored 2 MiB body of
nested <div>s takes ~37 s to re-clean, a 2 MiB & run 2.4 s, a U+00A0
run 3.4 s. Such rows may already exist in databases upgraded from ≤ 0.4.6,
and until #226’s fix lands any feed can add one; after it, bodies that
sanitize under its timeout can still be slow here. So nothing here assumes
a stored body is fast. The bounds cap what one slow body can cost
everyone else: it is cleaned at most once per process (cache + single
flight), holding one of two permits, so a second such body can be in the
sanitizer at the same time and no more; other readers’ uncached bodies
wait up to two seconds for a permit and then get a “temporarily
unavailable” note instead of a hung page. The slow body’s own readers pay
its full cost, once.
An earlier version of this module tried to predict the cost with a scan
that re-implemented html5ever’s tree-construction rules in front of
html5ever; three review rounds each found a mismatch, and the last found
the scan cutting real articles (li in li after a stripped <section>,
a 245 KB <pre> XML listing, <rt> inside a <span> in <ruby>). The
bounds here predict nothing.
Structs§
- Body
Renderer - The path from a stored
content_htmlrow to aBodyRender, with the three bounds. One process-wide instance,BodyRenderer::shared, serves the reader; tests build their own with smaller parameters. - Sanitized
Html - Article-body HTML that has been through the sanitizer in this process — and so can be emitted into a page without escaping.
Enums§
- Body
Render - What the reader gets back for a stored body: the markup, or one of two
reasons it is not shown. Both reasons are notes in
entry.htmlpointing at the original.
Constants§
- CACHE_
MAX_ BYTES - The most cleaned markup the render cache holds, in bytes of output.
- CACHE_
MAX_ ENTRIES - The most bodies the render cache holds. A reader paging through a list re-views far fewer than this before anything is evicted.
- MAX_
RENDER_ HTML_ BYTES - The longest stored body render will sanitize: ingest’s own stored bound
(
feed::MAX_CONTENT_HTML_BYTES), so no body ingest wrote is ever refused. Only a row that skippedfeed.rs, or one from before the bound existed (#224), can be longer, and such a row is not given to the sanitizer at all. - RENDER_
PERMITS - How many stored bodies may be in the sanitizer at once, process-wide.
- RENDER_
WAIT - How long a page view waits for a sanitizer permit before showing the “temporarily unavailable” note instead of the body.
- SLOW_
KEYS_ MAX - How many slow bodies render remembers (
SlowSet). 32 bytes each, so 128 KiB at most; far more slow bodies than any instance should ever hold.