forked from kailan/esi
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
261 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
[target.wasm32-wasi] | ||
rustflags = ["-C", "debuginfo=2"] | ||
runner = "viceroy run -C fastly.toml -- " | ||
|
||
[build] | ||
target = "wasm32-wasi" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
[package] | ||
name = "esi_example_variants" | ||
version.workspace = true | ||
authors.workspace = true | ||
license.workspace = true | ||
edition.workspace = true | ||
publish = false | ||
|
||
[dependencies] | ||
fastly = "^0.11" | ||
esi = { path = "../../esi" } | ||
log = "^0.4" | ||
env_logger = "^0.11" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# This file describes a Fastly Compute package. To learn more visit: | ||
# https://developer.fastly.com/reference/fastly-toml/ | ||
|
||
authors = ["[email protected]"] | ||
description = "" | ||
language = "rust" | ||
manifest_version = 2 | ||
name = "esi_example_variants" | ||
service_id = "" | ||
|
||
[local_server] | ||
|
||
[local_server.backends] | ||
|
||
[local_server.backends.mock-s3] | ||
url = "https://mock-s3.edgecompute.app" | ||
override_host = "mock-s3.edgecompute.app" | ||
|
||
[scripts] | ||
build = "cargo build --bin esi_example_variants --release --target wasm32-wasi --color always" | ||
|
||
[setup] | ||
|
||
[setup.backends] | ||
|
||
[setup.backends.mock-s3] | ||
address = "mock-s3.edgecompute.app" | ||
port = 443 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<title>My Broken Website</title> | ||
</head> | ||
<body> | ||
<header style="background: #f1f1f1; padding: 16px"> | ||
<h1>My Broken Website</h1> | ||
</header> | ||
<p>There is <em>some</em> valid content on this page, like the text you're reading right now.</p> | ||
<p>But watch this... I'm about to include an ESI fragment that does not exist:</p> | ||
<esi:include src="/_fragments/doesnotexist.html"/> | ||
<p>If you're seeing this text in the browser, then something is broken.</p> | ||
</body> | ||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
use std::{collections::HashMap, io::Write}; | ||
|
||
use esi::{parse_tags, Reader, Writer}; | ||
use fastly::{http::StatusCode, mime, Request, Response}; | ||
use log::{error, info}; | ||
|
||
// Take a list of fragment URLs and return a map that maps each URL to a variant for the specific request. | ||
fn get_variant_urls(urls: Vec<String>) -> HashMap<String, String> { | ||
let mut variant_urls = HashMap::new(); | ||
for url in urls { | ||
// For demonstration, add a query parameter to each request | ||
let variant_url = if url.contains('?') { | ||
format!("{}&variant=1", url) | ||
} else { | ||
format!("{}?variant=1", url) | ||
}; | ||
variant_urls.insert(url, variant_url); | ||
} | ||
variant_urls | ||
} | ||
|
||
fn main() { | ||
env_logger::builder() | ||
.filter(None, log::LevelFilter::Trace) | ||
.init(); | ||
|
||
let req = Request::from_client(); | ||
|
||
if req.get_path() != "/" { | ||
Response::from_status(StatusCode::NOT_FOUND).send_to_client(); | ||
return; | ||
} | ||
|
||
// Generate synthetic test response from "index.html" file. | ||
// You probably want replace this with a backend call, e.g. `req.clone_without_body().send("origin_0")` | ||
let mut beresp = | ||
Response::from_body(include_str!("index.html")).with_content_type(mime::TEXT_HTML); | ||
|
||
// If the response is HTML, we can parse it for ESI tags. | ||
if beresp | ||
.get_content_type() | ||
.is_some_and(|c| c.subtype() == mime::HTML) | ||
{ | ||
let processor = esi::Processor::new(Some(req), esi::Configuration::default()); | ||
|
||
// Create a response to send the headers to the client | ||
let resp = Response::from_status(StatusCode::OK).with_content_type(mime::TEXT_HTML); | ||
|
||
// Send the response headers to the client and open an output stream | ||
let output_writer = resp.stream_to_client(); | ||
|
||
// Set up an XML writer to write directly to the client output stream. | ||
let mut xml_writer = Writer::new(output_writer); | ||
|
||
// Parse the ESI document and store it in memory | ||
let mut events = Vec::new(); | ||
let mut beresp_reader = Reader::from_reader(beresp.take_body()); | ||
parse_tags("esi", &mut beresp_reader, &mut |event| { | ||
events.push(event); | ||
Ok(()) | ||
}) | ||
.expect("failed to parse ESI template"); | ||
|
||
// Extract the `src` URLs from ESI includes | ||
let urls = events | ||
.iter() | ||
.filter_map(|event| match event { | ||
esi::Event::ESI(esi::Tag::Include { src, .. }) => Some(src.clone()), | ||
_ => None, | ||
}) | ||
.collect::<Vec<_>>(); | ||
|
||
// Check the variant database to determine the URLs to fetch | ||
let variant_urls = get_variant_urls(urls); | ||
|
||
// Process the already-parsed ESI document, replacing the request URLs with the variant URLs | ||
let result = processor.process_parsed_document( | ||
events.into(), | ||
&mut xml_writer, | ||
Some(&move |req| { | ||
let original_url = req.get_url().to_string(); | ||
let variant_url = variant_urls.get(&original_url).unwrap_or(&original_url); | ||
info!( | ||
"Sending request - original URL: ({}) variant URL: ({})", | ||
req.get_path(), | ||
variant_url | ||
); | ||
Ok(esi::PendingFragmentContent::PendingRequest( | ||
req.with_url(variant_url) | ||
.with_ttl(120) | ||
.send_async("mock-s3")?, | ||
)) | ||
}), | ||
Some(&|req, resp| { | ||
info!( | ||
"Received response for {} {}", | ||
req.get_method(), | ||
req.get_path() | ||
); | ||
Ok(resp) | ||
}), | ||
); | ||
|
||
match result { | ||
Ok(()) => { | ||
xml_writer.into_inner().finish().unwrap(); | ||
} | ||
Err(err) => { | ||
error!("error processing ESI document: {}", err); | ||
let _ = xml_writer.get_mut().write(b"Internal server error"); | ||
xml_writer.into_inner().finish().unwrap_or_else(|_| { | ||
error!("error flushing error response to client"); | ||
}); | ||
} | ||
} | ||
} else { | ||
// Otherwise, we can just return the response. | ||
beresp.send_to_client(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters