Relative date insertion using ESI

Generate relative time datelines like "3 minutes ago" at the Edge instead of in JavaScript or at origin. Better caching, faster rendering, fewer reflows.

VCL

Use this solution in your VCL service (click RUN below to test this solution or clone it to make changes):

Compute@Edge

Use this solution in your Compute@Edge service:

  1. Rust
use quick_xml::{
events::{BytesText, Event},
Reader, Writer,
};
use std::time::{Duration, UNIX_EPOCH};
fn parse_since(since: &str) -> String {
match since.parse::<u64>() {
Ok(secs) => {
match (UNIX_EPOCH + Duration::from_secs(secs))
.elapsed()
.map(|d| d.as_secs())
.unwrap_or(0)
{
n if n < 60 => format!("{}s ago", n),
n if n < 3600 => format!("{}m ago", n / 60),
n if n < 96 * 3600 => format!("{}h ago", n / 3600),
n => format!("{}d ago", n / 86400),
}
}
_ => "".to_string(),
}
}
#[fastly::main]
fn main(mut req: Request) -> Result<Response, Error> {
// Make sure the origin server does not gzip the response. We
// can't process ESIs in gzipped content.
req.remove_header(header::ACCEPT_ENCODING);
let mut beresp = req.send("backend_name")?;
// This example only inspects HTML pages, but the same code could
// be used in non-HTML content as well.
match beresp.get_content_type() {
Some(mime) if mime.essence_str() == "text/html" => return Ok(beresp),
_ => (),
}
let mut reader = Reader::from_reader(beresp.get_body_mut());
reader
.trim_markup_names_in_closing_tags(false)
.check_end_names(false);
let mut writer = Writer::new(Body::new());
let mut buf = Vec::new();
loop {
buf.clear();
match reader.read_event(&mut buf) {
// We only handle ESI include here, but other ESI tags are possible.
Ok(Event::Empty(elem)) if elem.name() == b"esi:include" => {
if let Some(path) = elem.attributes().find_map(|attr| {
if attr.as_ref().unwrap().key == b"src" {
attr.unwrap().unescape_and_decode_value(&reader).ok()
} else {
None
}
}) {
// We assume here that the source page's ESI includes are
// in the form /__since/<unix-timestamp>, and we need to
// convert that into a relative time.
if let Some(since) = path.strip_prefix("/__since/") {
writer.write_event(Event::Text(BytesText::from_escaped_str(
parse_since(since),
)))?;
}
}
}
Ok(Event::Eof) => break,
Ok(e) => writer.write_event(&e)?,
_ => {}
}
}
Ok(beresp.with_body(writer.into_inner()))
}