Skip to main content

Module sanitized_html

Module sanitized_html 

Source
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§

BodyRenderer
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.
SanitizedHtml
Article-body HTML that has been through the sanitizer in this process — and so can be emitted into a page without escaping.

Enums§

BodyRender
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.html pointing 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 skipped feed.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.