commit 9829ba73106ff3f427415bb39f79d74e2dc3cd80 Author: Leonora Tindall Date: Mon Sep 19 09:56:48 2022 -0500 Initial working setup. diff --git a/.cargo/config b/.cargo/config new file mode 100644 index 0000000..6b77899 --- /dev/null +++ b/.cargo/config @@ -0,0 +1,2 @@ +[build] +target = "wasm32-wasi" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9f6a089 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/target +**/*.rs.bk +/bin +/pkg diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..591d2b3 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,438 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anyhow" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bytes" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" + +[[package]] +name = "bytes" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cpufeatures" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed00c67cb5d0a7d64a44f6ad2668db7e7530311dd53ea79bcd4fb022c64911c8" +dependencies = [ + "libc", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "edgeblog" +version = "0.1.0" +dependencies = [ + "fastly", + "pulldown-cmark", +] + +[[package]] +name = "fastly" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b1d6d1476a85603b9fd5162f747570686f4637383d9f8798d5ef0f2e628193b" +dependencies = [ + "anyhow", + "bytes 0.5.6", + "fastly-macros", + "fastly-shared", + "fastly-sys", + "http", + "lazy_static", + "mime", + "serde", + "serde_json", + "serde_urlencoded", + "sha2", + "thiserror", + "time", + "url", +] + +[[package]] +name = "fastly-macros" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b495f156e4403917917522866ef0761164196c4e36369cc009c1e96d6d90012" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "fastly-shared" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "010266f409d00e3d4cf2e0f3e201265960b96fa342bbddf366a41beea5d10346" +dependencies = [ + "bitflags", + "http", + "thiserror", +] + +[[package]] +name = "fastly-sys" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd84c88b732ac6d5711363856a3d4b686fd2691ba753d565879779c6ba8d84b" +dependencies = [ + "fastly-shared", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getopts" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "http" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527e8c9ac747e28542699a951517aa9a6945af506cd1f2e1b53a576c17b6cc11" +dependencies = [ + "bytes 1.0.1", + "fnv", + "itoa", +] + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "itoa" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21" + +[[package]] +name = "matches" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "proc-macro2" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "pulldown-cmark" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d9cc634bc78768157b5cbfe988ffcd1dcba95cd2b2f03a88316c08c6d00ed63" +dependencies = [ + "bitflags", + "getopts", + "memchr", + "unicase", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "serde" +version = "1.0.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b362ae5752fd2137731f9fa25fd4d9058af34666ca1966fb969119cc35719f12" +dependencies = [ + "block-buffer", + "cfg-if", + "cpufeatures", + "digest", + "opaque-debug", +] + +[[package]] +name = "syn" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "thiserror" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa6f76457f59514c7eeb4e59d891395fab0b2fd1d40723ae737d64153392e9c6" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a36768c0fbf1bb15eca10defa29526bda730a2376c2ab4393ccfa16fb1a318d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e0a10c9a9fb3a5dce8c2239ed670f1a2569fcf42da035f5face1b19860d52b0" +dependencies = [ + "libc", + "serde", +] + +[[package]] +name = "tinyvec" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "typenum" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06" + +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeb8be209bb1c96b7c177c7420d26e04eccacb0eeae6b980e35fcb74678107e0" +dependencies = [ + "matches", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07fbfce1c8a97d547e8b5334978438d9d6ec8c20e38f56d4a4374d181493eaef" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "version_check" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..db9479f --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "edgeblog" +version = "0.1.0" +authors = [] +edition = "2018" +# Remove this line if you want to be able to publish this crate as open source on crates.io. +# Otherwise, `publish = false` prevents an accidental `cargo publish` from revealing private source. +publish = false + +[profile.release] +debug = 1 + +[dependencies] +fastly = "^0.8.0" +pulldown-cmark = "0.9" + diff --git a/README.md b/README.md new file mode 100644 index 0000000..ada44e3 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Markdown @Edge + +A Markdown renderer on the Compute @Edge platform from Fastly! diff --git a/fastly.toml b/fastly.toml new file mode 100644 index 0000000..5aee857 --- /dev/null +++ b/fastly.toml @@ -0,0 +1,16 @@ +# This file describes a Fastly Compute@Edge package. To learn more visit: +# https://developer.fastly.com/reference/fastly-toml/ + +authors = ["nora@nora.codes"] +description = "A Markdown blog, rendered at the edge." +language = "rust" +manifest_version = 2 +name = "edgeblog" +service_id = "3bjMd8dgb83McAT6FMQum8" + +[local_server.backends.upstream] +url = "http://nora.codes/" + +[local_server.backends.upstream_ssl] +url = "https://nora.codes/" + diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..5914a56 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "stable" +targets = [ "wasm32-wasi" ] diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..d60a231 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,95 @@ +//! 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(()) +}