diff --git a/Cargo.lock b/Cargo.lock index 1dd686ab..fab93b17 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2232,6 +2232,7 @@ name = "wasm-workers-rs" version = "0.1.0" dependencies = [ "anyhow", + "base64", "handler", "http", "serde", @@ -2245,6 +2246,7 @@ dependencies = [ "actix-files", "actix-web", "anyhow", + "base64", "clap 4.0.26", "env_logger", "glob", diff --git a/Cargo.toml b/Cargo.toml index 87a4bdd5..a2fee790 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.85" glob = "0.3.0" toml = "0.5.9" +base64 = "0.13.1" clap = { version = "4.0.10", features = ["derive"] } [workspace] @@ -37,6 +38,7 @@ members = [ ] # Exclude examples exclude = [ + "examples/pdf-create", "examples/rust-basic", "examples/rust-kv" ] diff --git a/docs/docs/tutorials/rust-workers.md b/docs/docs/tutorials/rust-workers.md index fb402cdb..d9756719 100644 --- a/docs/docs/tutorials/rust-workers.md +++ b/docs/docs/tutorials/rust-workers.md @@ -9,7 +9,7 @@ Then, they are loaded by Wasm Workers Server and start processing requests. ## Your first worker -Every worker receives a [Request](https://docs.rs/http/0.2.8/http/request/struct.Request.html) struct and returns a [Response](https://docs.rs/http/0.2.8/http/response/struct.Response.html). These structs come from the widely known [`http` crate](https://docs.rs/http/). Then, we create a handler macro to connect your worker with `wws`. +Every worker receives a [Request](https://docs.rs/http/0.2.8/http/request/struct.Request.html) struct and returns a [Response](https://docs.rs/http/0.2.8/http/response/struct.Response.html). These structs come from the widely known [`http` crate](https://docs.rs/http/) and the `Content` struct is defined in our rust kit. It allows you returning different types. Finally, the `handler` macro connects your worker with `wws`. In this example, the worker will get a request and print all the related information. @@ -39,10 +39,11 @@ In this example, the worker will get a request and print all the related informa use wasm_workers_rs::{ handler, http::{self, Request, Response}, + Content, }; #[handler] - fn reply(req: Request) -> Result> { + fn reply(req: Request) -> Result> { Ok(http::Response::builder() .status(200) .header("x-generated-by", "wasm-workers-server") @@ -145,10 +146,11 @@ To add a KV store to your worker, follow these steps: use wasm_workers_rs::{ handler, http::{self, Request, Response}, + Content, }; #[handler(cache)] - fn handler(_req: Request, cache: &mut Cache) -> Result> { + fn handler(_req: Request, cache: &mut Cache) -> Result> { Ok(http::Response::builder() .status(200) .header("x-generated-by", "wasm-workers-server") @@ -161,13 +163,13 @@ To add a KV store to your worker, follow these steps: ```rust title="src/main.rs" use anyhow::Result; use wasm_workers_rs::{ - cache::Cache, handler, http::{self, Request, Response}, + Cache, Content, }; #[handler(cache)] - fn handler(_req: Request, cache: &mut Cache) -> Result> { + fn handler(_req: Request, cache: &mut Cache) -> Result> { // Applied changes here to use the Response method. This requires changes // on signature and how it returns the data. let count = cache.get("counter"); @@ -249,10 +251,11 @@ use std::env; use wasm_workers_rs::{ handler, http::{self, Request, Response}, + Content, }; #[handler] -fn handler(req: Request) -> Result> { +fn handler(req: Request) -> Result> { // Read the environment variable using the std::env::var method let message = env::var("MESSAGE").unwrap_or_else(|_| String::from("Missing message")); diff --git a/examples/pdf-create/Cargo.lock b/examples/pdf-create/Cargo.lock new file mode 100644 index 00000000..f36c1ef9 --- /dev/null +++ b/examples/pdf-create/Cargo.lock @@ -0,0 +1,618 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "anyhow" +version = "1.0.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" + +[[package]] +name = "base-x" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "bumpalo" +version = "3.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" + +[[package]] +name = "bytes" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const_fn" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935" + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "discard" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" + +[[package]] +name = "dtoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" + +[[package]] +name = "encoding" +version = "0.2.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec" +dependencies = [ + "encoding-index-japanese", + "encoding-index-korean", + "encoding-index-simpchinese", + "encoding-index-singlebyte", + "encoding-index-tradchinese", +] + +[[package]] +name = "encoding-index-japanese" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04e8b2ff42e9a05335dbf8b5c6f7567e5591d0d916ccef4e0b1710d32a0d0c91" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding-index-korean" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dc33fb8e6bcba213fe2f14275f0963fd16f0a02c878e3095ecfdf5bee529d81" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding-index-simpchinese" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87a7194909b9118fc707194baa434a4e3b0fb6a5a757c73c3adb07aa25031f7" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding-index-singlebyte" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3351d5acffb224af9ca265f435b859c7c01537c0849754d3db3fdf2bfe2ae84a" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding-index-tradchinese" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd0e20d5688ce3cab59eb3ef3a2083a5c77bf496cb798dc6fcdb75f323890c18" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding_index_tests" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" + +[[package]] +name = "flate2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "handler" +version = "0.1.0" +dependencies = [ + "anyhow", + "http", + "quote", + "serde", + "serde_json", + "syn", + "wasi", +] + +[[package]] +name = "http" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +dependencies = [ + "bytes", + "fnv", + "itoa 1.0.3", +] + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "itoa" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" + +[[package]] +name = "js-sys" +version = "0.3.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" +dependencies = [ + "wasm-bindgen", +] + +[[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.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89" + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "lopdf" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8146695b97752d9c66da0092c6364f8f3ca683f5ea34341db21e5550c3b8c4f4" +dependencies = [ + "dtoa", + "encoding", + "flate2", + "itoa 0.4.8", + "lazy_static", + "linked-hash-map", + "log", + "pom", + "time", + "weezl", +] + +[[package]] +name = "miniz_oxide" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34" +dependencies = [ + "adler", +] + +[[package]] +name = "once_cell" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" + +[[package]] +name = "owned_ttf_parser" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60ac8dda2e5cc09bf6480e3b3feff9783db251710c922ae9369a429c51efdeb0" +dependencies = [ + "ttf-parser", +] + +[[package]] +name = "pdf-create" +version = "0.1.0" +dependencies = [ + "anyhow", + "printpdf", + "wasm-workers-rs", +] + +[[package]] +name = "pom" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e2192780e9f8e282049ff9bffcaa28171e1cb0844f49ed5374e518ae6024ec" + +[[package]] +name = "printpdf" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b61f0c6672a5507f0557c50c2263abc54fecc2a4c0ca56499be1396679a686c" +dependencies = [ + "js-sys", + "lopdf", + "owned_ttf_parser", + "time", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro2" +version = "1.0.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "serde" +version = "1.0.144" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.144" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.85" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" +dependencies = [ + "itoa 1.0.3", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" +dependencies = [ + "sha1_smol", +] + +[[package]] +name = "sha1_smol" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" + +[[package]] +name = "standback" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff" +dependencies = [ + "version_check", +] + +[[package]] +name = "stdweb" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" +dependencies = [ + "discard", + "rustc_version", + "stdweb-derive", + "stdweb-internal-macros", + "stdweb-internal-runtime", + "wasm-bindgen", +] + +[[package]] +name = "stdweb-derive" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "serde_derive", + "syn", +] + +[[package]] +name = "stdweb-internal-macros" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" +dependencies = [ + "base-x", + "proc-macro2", + "quote", + "serde", + "serde_derive", + "serde_json", + "sha1", + "syn", +] + +[[package]] +name = "stdweb-internal-runtime" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" + +[[package]] +name = "syn" +version = "1.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "time" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4752a97f8eebd6854ff91f1c1824cd6160626ac4bd44287f7f4ea2035a02a242" +dependencies = [ + "const_fn", + "libc", + "standback", + "stdweb", + "time-macros", + "version_check", + "winapi", +] + +[[package]] +name = "time-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" +dependencies = [ + "proc-macro-hack", + "time-macros-impl", +] + +[[package]] +name = "time-macros-impl" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "standback", + "syn", +] + +[[package]] +name = "ttf-parser" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ae2f58a822f08abdaf668897e96a5656fe72f5a9ce66422423e8849384872e6" + +[[package]] +name = "unicode-ident" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" + +[[package]] +name = "wasm-workers-rs" +version = "0.1.0" +dependencies = [ + "anyhow", + "base64", + "handler", + "http", + "serde", + "serde_json", +] + +[[package]] +name = "weezl" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/examples/pdf-create/Cargo.toml b/examples/pdf-create/Cargo.toml new file mode 100644 index 00000000..cf9217ca --- /dev/null +++ b/examples/pdf-create/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "pdf-create" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1.0.63" +wasm-workers-rs = { path = "../../kits/rust" } +printpdf = "0.5.3" \ No newline at end of file diff --git a/examples/pdf-create/src/main.rs b/examples/pdf-create/src/main.rs new file mode 100644 index 00000000..bc45807c --- /dev/null +++ b/examples/pdf-create/src/main.rs @@ -0,0 +1,38 @@ +use anyhow::Result; +use printpdf::*; +use std::io::BufWriter; +use wasm_workers_rs::{ + handler, + http::{self, Request, Response}, + Content, +}; + +#[handler] +fn handler(req: Request) -> Result> { + let mut buf = BufWriter::new(Vec::new()); + + let (doc, page1, layer1) = PdfDocument::new("My Quote", Mm(247.0), Mm(210.0), "Layer 1"); + let current_layer = doc.get_page(page1).get_layer(layer1); + let font = doc.add_builtin_font(BuiltinFont::TimesRoman).unwrap(); + + current_layer.use_text("Your Quote", 24.0, Mm(20.0), Mm(190.0), &font); + current_layer.use_text(req.body(), 18.0, Mm(20.0), Mm(170.0), &font); + current_layer.use_text( + "Created by a Wasm module in Wasm Workers Server", + 12.0, + Mm(20.0), + Mm(20.0), + &font, + ); + + doc.save(&mut buf)?; + + let bytes = buf.into_inner()?; + + Ok(http::Response::builder() + .status(200) + .header("Content-Disposition", "attachment; filename=\"quote.pdf\"") + .header("Content-Type", "application/pdf") + .header("x-generated-by", "wasm-workers-server") + .body(bytes.into())?) +} diff --git a/examples/rust-basic/Cargo.lock b/examples/rust-basic/Cargo.lock index c0cce36f..a753bbdb 100644 --- a/examples/rust-basic/Cargo.lock +++ b/examples/rust-basic/Cargo.lock @@ -8,6 +8,12 @@ version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "bytes" version = "1.2.1" @@ -141,6 +147,7 @@ name = "wasm-workers-rs" version = "0.1.0" dependencies = [ "anyhow", + "base64", "handler", "http", "serde", diff --git a/examples/rust-basic/src/main.rs b/examples/rust-basic/src/main.rs index eccad65d..7f2d64ad 100644 --- a/examples/rust-basic/src/main.rs +++ b/examples/rust-basic/src/main.rs @@ -3,10 +3,11 @@ use std::env; use wasm_workers_rs::{ handler, http::{self, HeaderValue, Request, Response}, + Content, }; #[handler] -fn handler(req: Request) -> Result> { +fn handler(req: Request) -> Result> { let message = env::var("MESSAGE").unwrap_or_else(|_| String::from("Missing title")); // Applied changes here to use the Response method. This requires changes diff --git a/examples/rust-kv/src/main.rs b/examples/rust-kv/src/main.rs index fa8088be..1d82f5fd 100644 --- a/examples/rust-kv/src/main.rs +++ b/examples/rust-kv/src/main.rs @@ -1,12 +1,12 @@ use anyhow::Result; use wasm_workers_rs::{ - cache::Cache, handler, http::{self, Request, Response}, + Cache, Content, }; #[handler(cache)] -fn handler(_req: Request, cache: &mut Cache) -> Result> { +fn handler(_req: Request, cache: &mut Cache) -> Result> { // Applied changes here to use the Response method. This requires changes // on signature and how it returns the data. let count = cache.get("counter"); diff --git a/kits/javascript/src/glue.js b/kits/javascript/src/glue.js index 0712f6f3..a5b98901 100644 --- a/kits/javascript/src/glue.js +++ b/kits/javascript/src/glue.js @@ -128,7 +128,7 @@ const requestToHandler = input => { handlerFunction(event); return { - body: event.response.body, + data: event.response.body, headers: event.response.headers.headers, status: event.response.status, kv: Cache.state diff --git a/kits/javascript/wasm-workers-quick-js-engine.wasm b/kits/javascript/wasm-workers-quick-js-engine.wasm index d62b9b8e..4b8fcb78 100755 Binary files a/kits/javascript/wasm-workers-quick-js-engine.wasm and b/kits/javascript/wasm-workers-quick-js-engine.wasm differ diff --git a/kits/rust/Cargo.toml b/kits/rust/Cargo.toml index 44580223..099e2a67 100644 --- a/kits/rust/Cargo.toml +++ b/kits/rust/Cargo.toml @@ -12,3 +12,4 @@ http = "0.2.8" handler = { path = "./handler" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.85" +base64 = "0.13.1" \ No newline at end of file diff --git a/kits/rust/handler/src/expand.rs b/kits/rust/handler/src/expand.rs index 48f18ac9..e20429b4 100644 --- a/kits/rust/handler/src/expand.rs +++ b/kits/rust/handler/src/expand.rs @@ -11,17 +11,16 @@ pub fn expand_macro(attr: TokenStream, item: TokenStream) -> TokenStream { let handler_fn = parse_macro_input!(item as syn::ItemFn); let handler_fn_name = &handler_fn.sig.ident; let args = parse_macro_input!(attr as Args); - let func_call; - if args.has_cache() { - func_call = quote! { + let func_call = if args.has_cache() { + quote! { #handler_fn_name(input.to_http_request(), &mut cache) } } else { - func_call = quote! { + quote! { #handler_fn_name(input.to_http_request()) } - } + }; let main_fn = quote! { use wasm_workers_rs::io::{Input, Output}; @@ -33,7 +32,8 @@ pub fn expand_macro(attr: TokenStream, item: TokenStream) -> TokenStream { "There was an error running the handler", 500, None, - None + None, + false ).to_json().unwrap(); if let Ok(input) = input { diff --git a/kits/rust/src/content.rs b/kits/rust/src/content.rs new file mode 100644 index 00000000..e17e29d0 --- /dev/null +++ b/kits/rust/src/content.rs @@ -0,0 +1,28 @@ +use base64::encode; + +/// Identifies the content of a response. In other words, the body. +/// We need this intermediate entity in Rust to be able to expose +/// an array of bytes as response. +/// +/// Note that Wasm Workers Server interacts with modules via +/// serialized UTF-8 JSONs. An array of bytes response may include +/// bytes that cannot be represented as UTF-8. To avoid this +/// limitation, Content is able to encode them as base64. Then, +/// wws will ensure to decode them before sending the bytes to the +/// client. +pub enum Content { + Text(String), + Base64(String), +} + +impl From> for Content { + fn from(s: Vec) -> Content { + Content::Base64(encode(s)) + } +} + +impl From for Content { + fn from(s: String) -> Content { + Content::Text(s) + } +} diff --git a/kits/rust/src/io.rs b/kits/rust/src/io.rs index 0e45f660..24e80aba 100644 --- a/kits/rust/src/io.rs +++ b/kits/rust/src/io.rs @@ -1,6 +1,7 @@ // Copyright 2022 VMware, Inc. // SPDX-License-Identifier: Apache-2.0 +use crate::Content; use anyhow::Result; use http::Response; use serde::{Deserialize, Serialize}; @@ -22,7 +23,7 @@ pub struct Input { impl Input { /// Build the object from a JSON input pub fn new(reader: Stdin) -> Result { - serde_json::from_reader::(reader).map_err(|e| anyhow::Error::new(e)) + serde_json::from_reader::(reader).map_err(anyhow::Error::new) } /// Convers the current object to a valid http::Request @@ -49,30 +50,33 @@ impl Input { /// back to the main project #[derive(Serialize, Deserialize)] pub struct Output { - body: String, + data: String, headers: HashMap, status: u16, kv: HashMap, + base64: bool, } impl Output { /// Build the struct from Scratch pub fn new( - body: &str, + data: &str, status: u16, headers: Option>, kv: Option>, + base64: bool, ) -> Self { Self { - body: body.to_string(), - status: status, - headers: headers.unwrap_or_else(|| HashMap::new()), - kv: kv.unwrap_or_else(|| HashMap::new()), + data: data.to_string(), + status, + headers: headers.unwrap_or_default(), + kv: kv.unwrap_or_default(), + base64, } } /// Build the struct from a http::Response object - pub fn from_response(response: Response, cache: HashMap) -> Self { + pub fn from_response(response: Response, cache: HashMap) -> Self { let mut headers = HashMap::new(); for (key, value) in response.headers().iter() { @@ -85,17 +89,26 @@ impl Output { // Note: added status here because `into_body` takes ownership of the // response let status = response.status().as_u16(); + let content = response.into_body(); + let body; + let base64; - Self::new( - response.into_body().as_str(), - status, - Some(headers), - Some(cache.clone()), - ) + match content { + Content::Base64(data) => { + body = data; + base64 = true; + } + Content::Text(data) => { + body = data; + base64 = false; + } + } + + Self::new(&body, status, Some(headers), Some(cache), base64) } /// Convert it to JSON pub fn to_json(&self) -> Result { - serde_json::to_string(&self).map_err(|e| anyhow::Error::new(e)) + serde_json::to_string(&self).map_err(anyhow::Error::new) } } diff --git a/kits/rust/src/lib.rs b/kits/rust/src/lib.rs index 777d5a5e..f18425cd 100644 --- a/kits/rust/src/lib.rs +++ b/kits/rust/src/lib.rs @@ -1,7 +1,10 @@ // Copyright 2022 VMware, Inc. // SPDX-License-Identifier: Apache-2.0 -pub mod cache; +mod cache; +mod content; +pub use cache::Cache; +pub use content::Content; pub mod io; pub use handler::handler; diff --git a/src/data/kv.rs b/src/data/kv.rs index fb56c4b1..d2956fd0 100644 --- a/src/data/kv.rs +++ b/src/data/kv.rs @@ -36,9 +36,9 @@ impl KV { /// Replaces the content of an existing store. If the store doesn't exist, /// this method won't apply any change - pub fn replace_store(&mut self, namespace: &str, state: HashMap) { + pub fn replace_store(&mut self, namespace: &str, state: &HashMap) { if let Some(store) = self.find_mut_store(namespace) { - store.replace(state); + store.replace(state.clone()); } } diff --git a/src/main.rs b/src/main.rs index 1cde29d5..635cd3c9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -127,11 +127,13 @@ async fn wasm_handler(req: HttpRequest, body: Bytes) -> HttpResponse { let handler_result = route .runner .run(&runner::build_wasm_input(&req, body_str, store), vars) - .unwrap_or(WasmOutput { - body: String::from("

There was an error running this function

"), - headers: HashMap::from([("content-type".to_string(), "text/html".to_string())]), - status: StatusCode::SERVICE_UNAVAILABLE.as_u16(), - kv: HashMap::new(), + .unwrap_or_else(|_| { + WasmOutput::new( + "

There was an error running this function

", + HashMap::from([("content-type".to_string(), "text/html".to_string())]), + StatusCode::SERVICE_UNAVAILABLE.as_u16(), + HashMap::new(), + ) }); let mut builder = HttpResponse::build( @@ -152,10 +154,15 @@ async fn wasm_handler(req: HttpRequest, body: Bytes) -> HttpResponse { .write() .unwrap() .kv - .replace_store(&kv_namespace.unwrap(), handler_result.kv) + .replace_store(&kv_namespace.unwrap(), &handler_result.kv) } - result = builder.body(String::from(&handler_result.body)) + result = match handler_result.body() { + Ok(res) => builder.body(res), + Err(_) => { + HttpResponse::ServiceUnavailable().body("There was an error running the worker") + } + } } } diff --git a/src/runner.rs b/src/runner.rs index 59b0a57c..2ecad334 100644 --- a/src/runner.rs +++ b/src/runner.rs @@ -3,6 +3,7 @@ use actix_web::{http::header::HeaderMap, HttpRequest}; use anyhow::Result; +use base64::decode; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::fs; @@ -47,16 +48,51 @@ impl WasmInput { /// JSON output from a wasm module. This information is passed via STDOUT / WASI /// from the module. -#[derive(Serialize, Deserialize, Debug)] +#[derive(Deserialize, Debug)] pub struct WasmOutput { - /// Response body - pub body: String, /// Response headers pub headers: HashMap, /// Response HTTP status pub status: u16, /// New state of the K/V store if available pub kv: HashMap, + /// Response body data + data: String, + /// Internal value to indicate if the body is base64 encoded + #[serde(default = "default_base64_encoding")] + base64: bool, +} + +fn default_base64_encoding() -> bool { + false +} + +impl WasmOutput { + /// Initializes a new WasmOutput object + pub fn new( + body: &str, + headers: HashMap, + status: u16, + kv: HashMap, + ) -> Self { + Self { + data: String::from(body), + base64: false, + headers, + status, + kv, + } + } + + /// Return the content body as bytes. It will automatically + /// decode the data if the base64 flag is enabled. + pub fn body(&self) -> Result> { + if self.base64 { + Ok(decode(&self.data)?) + } else { + Ok(self.data.as_bytes().into()) + } + } } /// Builds the JSON string to pass to the Wasm module using WASI STDIO strategy.