Crate esi

source ·
Expand description

ESI for Fastly

This crate provides a streaming Edge Side Includes parser and executor designed for Fastly Compute@Edge.

The implementation is a subset of the ESI Language Specification 1.0 supporting the following tags:

  • <esi:include> (+ alt, onerror="continue")
  • <esi:comment>
  • <esi:remove>

Other tags will be ignored and served to the client as-is.

Example Usage

use fastly::{http::StatusCode, mime, Error, Request, Response};

fn main() {
    if let Err(err) = handle_request(Request::from_client()) {
        println!("returning error response");

        Response::from_status(StatusCode::INTERNAL_SERVER_ERROR)
            .with_body(err.to_string())
            .send_to_client();
    }
}

fn handle_request(req: Request) -> Result<(), Error> {
    // Fetch ESI document from backend.
    let mut beresp = req.clone_without_body().send("origin_0")?;

    // If the response is HTML, we can parse it for ESI tags.
    if beresp
        .get_content_type()
        .map(|c| c.subtype() == mime::HTML)
        .unwrap_or(false)
    {
        let processor = esi::Processor::new(
            // The original client request.
            Some(req),
            // Use the default ESI configuration.
            esi::Configuration::default()
        );

        processor.process_response(
            // The ESI source document. Note that the body will be consumed.
            &mut beresp,
            // Optionally provide a template for the client response.
            Some(Response::from_status(StatusCode::OK).with_content_type(mime::TEXT_HTML)),
            // Provide logic for sending fragment requests, otherwise the hostname
            // of the request URL will be used as the backend name.
            Some(&|req| {
                println!("Sending request {} {}", req.get_method(), req.get_path());
                Ok(Some(req.with_ttl(120).send_async("mock-s3")?))
            }),
            // Optionally provide a method to process fragment responses before they
            // are streamed to the client.
            Some(&|req, resp| {
                println!(
                    "Received response for {} {}",
                    req.get_method(),
                    req.get_path()
                );
                Ok(resp)
            }),
        )?;
    } else {
        // Otherwise, we can just return the response.
        beresp.send_to_client();
    }

    Ok(())
}

See example applications in the examples subdirectory or read the hosted documentation at docs.rs/esi. Due to the fact that this processor streams fragments to the client as soon as they are available, it is not possible to return a relevant status code for later errors once we have started streaming the response to the client. For this reason, it is recommended that you refer to the esi_example_advanced_error_handling application, which allows you to handle errors gracefully by maintaining ownership of the output stream.

License

The source and documentation for this project are released under the MIT License.

Structs

  • This struct is used to configure optional behaviour within the ESI processor.
  • An instance of the ESI processor with a given configuration.

Enums

  • A section of the pending response, either raw XML data or a pending fragment request.
  • Representation of either XML data or a parsed ESI tag.
  • Describes an error encountered during ESI parsing or execution.
  • Representation of an ESI tag from a source response.

Functions

  • Parses the ESI document from the given reader and calls the callback closure upon each successfully parsed ESI tag.

Type Definitions