Expand description
trillium handler that rewrites html using
lol-html through the lol-async library.
HtmlRewriter wraps another handler and transforms its response body as it streams to the client,
using lol-html’s CSS-selector-based rewriting API. It’s well suited to
sitting in front of a Proxy or other handlers that you don’t
have direct control over, letting you inject tags, rewrite attributes, or strip elements from an
outbound HTML body without buffering the whole response.
use trillium_html_rewriter::{
HtmlRewriter, Settings,
html::{element, html_content::ContentType},
};
let handler = (
|conn: trillium::Conn| async move {
conn.with_response_header("content-type", "text/html")
.with_status(200)
.with_body("<html><body><p>body</p></body></html>")
},
HtmlRewriter::new(|| Settings {
element_content_handlers: vec![element!("body", |el| {
el.prepend("<h1>title</h1>", ContentType::Html);
Ok(())
})],
..Settings::new_send()
}),
);
use trillium_testing::{TestServer, block_on};
block_on(async move {
let app = TestServer::new(handler).await;
app.get("/")
.await
.assert_ok()
.assert_body("<html><body><h1>title</h1><p>body</p></body></html>");
});§Behavior
-
Only HTML responses are rewritten. Before touching anything, the handler checks the outgoing
Content-Type: a response is rewritten only if its mime subtype ishtml(e.g.text/html). Responses with any other content type — or noContent-Typeat all — pass through untouched, so it’s safe to place in front of a handler that serves a mix of HTML, JSON, and binary data. -
It transforms the response in
before_send, so composition order is forgiving. The rewriting runs in the handler’sbefore_sendphase, which trillium runs for every handler in a tuple regardless of whether an earlier handler halted. SoHtmlRewriterrewrites whatever HTML body the rest of the tuple produced. An example using trillium-proxy:use trillium_html_rewriter::{HtmlRewriter, html::{Settings, element, html_content::ContentType}}; use trillium_proxy::Proxy; use trillium_rustls::RustlsConfig; use trillium_smol::ClientConfig; trillium_smol::run(( Proxy::new(RustlsConfig::<ClientConfig>::default(), "https://example.com"), HtmlRewriter::new(|| Settings { element_content_handlers: vec![element!("body", |el| { el.prepend("<h1>rewritten</h1>", ContentType::Html); Ok(()) })], ..Settings::new_send() }), )); -
The response becomes a stream of unknown length. Because rewriting is streaming, the rewritten body’s length isn’t known up front, so any
Content-Lengthheader is removed and the response is sent with chunked transfer encoding (HTTP/1.1) or the equivalent for HTTP/2 and HTTP/3.
§Constructing settings
HtmlRewriter::new takes a Fn() -> Settings, not a Settings value, because lol-html’s content
handlers are single-use: the closure is invoked once per rewritten response to build a fresh set of
handlers. Use Settings::new_send() as the base (its handlers are
Send, as required to move between trillium’s tasks) and fill in element_content_handlers /
document_content_handlers. See the lol-html docs for the full
rewriting API — element!,
comments!,
text!, and the
Element API for
inspecting and mutating matched elements.
Re-exports§
pub use lol_async::html;
Structs§
- Html
Rewriter - A trillium
Handlerthat rewrites HTML response bodies withlol-html, usinglol-async.
Type Aliases§
- Settings
SettingsforSendableHtmlRewriters.