//! 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(()) }