- main
- manifest
- deps
- main
- Install
- Run
use fastly::http::{header, Url};
use fastly::{ConfigStore, Error, Request, Response};
use serde::Deserialize;
const LOOKUP_LOOP_LIMIT: u8 = 6;
#[derive(Debug, Deserialize)]
struct RedirectParams {
status: u16,
keep_query: bool,
path: String,
}
#[fastly::main]
fn main(req: Request) -> Result<Response, Error> {
let url = req.get_url();
if let Some(p) = lookup_redirects(url) {
let params = serde_json::from_str::<RedirectParams>(&p).unwrap();
let mut location_value = format!(
"{}://{}{}",
url.scheme(),
url.host_str().expect("Host header is present"),
params.path,
);
if params.keep_query && url.query().is_some() {
location_value.push('?');
location_value.push_str(url.query().expect("Query string is present"));
}
return Ok(
Response::from_status(params.status)
.with_header(header::LOCATION, location_value)
);
}
Ok(req.send("origin_0")?)
}
fn lookup_redirects(url: &Url) -> Option<String> {
let redirects = ConfigStore::open("redirects");
let mut key = String::new();
key.push_str(url.host_str()?);
key.push_str(url.path());
if let Some(params) = redirects.get(key.as_str()) {
return Some(params);
}
key.clear();
key.push_str(url.path());
if let Some(params) = redirects.get(key.as_str()) {
return Some(params);
}
key.clear();
key.push_str(url.host_str()?);
key.push_str(url.path().trim_end_matches('/'));
for _ in 0..2 {
let mut wildcard_lookup_attempt = 0;
while key.contains('/') && wildcard_lookup_attempt < LOOKUP_LOOP_LIMIT {
key.push_str("/*");
if let Some(params) = redirects.get(&key) {
return Some(params);
}
key.truncate(key.len() - 2);
if let Some(n) = key.rfind('/') {
key.truncate(n);
}
wildcard_lookup_attempt += 1;
}
key.clear();
key.push_str(url.path().trim_end_matches('/'));
}
println!("No redirection entry found");
None
}