96 lines
3.2 KiB
Rust
96 lines
3.2 KiB
Rust
//! Default Compute@Edge template program.
|
|
|
|
use fastly::http::{header, Method, StatusCode};
|
|
use fastly::{mime, Body, Error, Request, Response};
|
|
use pulldown_cmark::{html, Options, Parser};
|
|
use std::io::Read;
|
|
|
|
/// The configured backend name.
|
|
const BACKEND_NAME: &str = "upstream_ssl";
|
|
|
|
/// The URL of the header markdown content.
|
|
const HEADER_URL: &str = "http://nora.codes/edgeblog/_header.md";
|
|
/// The URL of the footer markdown content.
|
|
const FOOTER_URL: &str = "http://nora.codes/edgeblog/_footer.md";
|
|
|
|
fn main() -> Result<(), Error> {
|
|
let req = Request::from_client();
|
|
// Filter request methods...
|
|
match req.get_method() {
|
|
// Allow GET and HEAD requests.
|
|
&Method::GET | &Method::HEAD => (),
|
|
|
|
// Deny anything else.
|
|
_ => {
|
|
Response::from_status(StatusCode::METHOD_NOT_ALLOWED)
|
|
.with_header(header::ALLOW, "GET, HEAD")
|
|
.with_body_text_plain("This method is not allowed\n")
|
|
.send_to_client();
|
|
return Ok(());
|
|
}
|
|
};
|
|
|
|
// Rewrite the / path to index.md, and construct a Request
|
|
let upstream_req = match req.get_path() {
|
|
"/" => Request::get("http://nora.codes/edgeblog/index.md"),
|
|
path => Request::get(format!("http://nora.codes/edgeblog/{}", path)),
|
|
};
|
|
|
|
// Get the data for the header portion of the page
|
|
let mut header_body = match Request::get(HEADER_URL).send(BACKEND_NAME) {
|
|
Err(_e) => Body::new(),
|
|
Ok(mut r) => r.take_body(),
|
|
};
|
|
|
|
// Get the data for the footer portion of the page
|
|
let mut footer_body = match Request::get(FOOTER_URL).send(BACKEND_NAME) {
|
|
Err(_e) => Body::new(),
|
|
Ok(mut r) => r.take_body(),
|
|
};
|
|
|
|
// Write the header into the synthetic body
|
|
let mut md_assembled = String::new();
|
|
header_body.read_to_string(&mut md_assembled)?;
|
|
md_assembled.push_str("\n");
|
|
|
|
// Get the geoip info for the client, if possible.
|
|
let geotext: String = match req.get_client_ip_addr() {
|
|
Some(ip) => match fastly::geo::geo_lookup(ip) {
|
|
None => "an undisclosed location".into(),
|
|
Some(geo) => format!(
|
|
"AS{} '{}', in {}",
|
|
geo.as_number(),
|
|
geo.as_name(),
|
|
geo.city()
|
|
)
|
|
.into(),
|
|
},
|
|
None => "an undisclosed location".into(),
|
|
};
|
|
|
|
// Get the content of the page itself. If this fails, pass on the failure.
|
|
let mut beresp = upstream_req.send(BACKEND_NAME)?;
|
|
if beresp.get_status() == StatusCode::OK {
|
|
beresp.take_body().read_to_string(&mut md_assembled)?;
|
|
md_assembled = md_assembled.replace("{{geo}}", &geotext);
|
|
} else {
|
|
beresp.send_to_client();
|
|
return Ok(());
|
|
}
|
|
|
|
// Write the footer into the synthetic body
|
|
md_assembled.push_str("\n");
|
|
footer_body.read_to_string(&mut md_assembled)?;
|
|
|
|
let md_assembled = md_assembled.replace("{{geo}}", &geotext);
|
|
let parser = Parser::new_ext(&md_assembled, Options::empty());
|
|
|
|
let mut client_body = beresp
|
|
.clone_without_body()
|
|
.with_content_type(mime::TEXT_HTML_UTF_8)
|
|
.stream_to_client();
|
|
|
|
html::write_html(&mut client_body, parser)?;
|
|
Ok(())
|
|
}
|