From 5b91b61af91a051216497f54fd513b163d2d0fd3 Mon Sep 17 00:00:00 2001 From: Knarkzel Date: Sun, 22 Jan 2023 13:01:23 +0100 Subject: [PATCH] Add http example --- Cargo.lock | 17 +++++ Cargo.toml | 6 ++ examples/http_dynamic_size.rs | 122 ++++++++++++++++++++++++++++++++++ 3 files changed, 145 insertions(+) create mode 100644 examples/http_dynamic_size.rs diff --git a/Cargo.lock b/Cargo.lock index fd01f093b65..51cdf093ca5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5230,6 +5230,22 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "ureq" +version = "2.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "338b31dd1314f68f3aabf3ed57ab922df95ffcd902476ca7ba3c4ce7b908c46d" +dependencies = [ + "base64", + "flate2", + "log", + "once_cell", + "rustls 0.20.7", + "url", + "webpki 0.22.0", + "webpki-roots 0.22.6", +] + [[package]] name = "url" version = "2.5.0" @@ -6596,6 +6612,7 @@ dependencies = [ "tokio", "tracing", "tracing-subscriber", + "ureq", "wasi-test-generator", "wasmer", "wasmer-cache", diff --git a/Cargo.toml b/Cargo.toml index 3019bed58a7..000d8016bb5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -109,6 +109,7 @@ lazy_static = "1.4" serial_test = "0.5" compiler-test-derive = { path = "tests/lib/compiler-test-derive" } tempfile = "3.6.0" +ureq = "2.6" # For logging tests using the `RUST_LOG=debug` when testing test-log = { version = "0.2", default-features = false, features = ["trace"] } tracing = { version = "0.1", default-features = false, features = ["log"] } @@ -314,3 +315,8 @@ required-features = ["backend"] name = "features" path = "examples/features.rs" required-features = ["cranelift"] + +[[example]] +name = "http-dynamic-size" +path = "examples/http_dynamic_size.rs" +required-features = ["cranelift"] diff --git a/examples/http_dynamic_size.rs b/examples/http_dynamic_size.rs new file mode 100644 index 00000000000..fda8ec66f95 --- /dev/null +++ b/examples/http_dynamic_size.rs @@ -0,0 +1,122 @@ +use anyhow::Result; +use wasmer::{wat2wasm, imports, Instance, Module, Store, Memory, AsStoreRef, MemoryView, FunctionEnvMut, WasmPtr, FunctionEnv, Function}; + +// Utils +pub fn read_string(view: &MemoryView, start: u32, len: u32) -> Result { + Ok(WasmPtr::::new(start).read_utf8_string(view, len)?) +} + +// Environment +pub struct ExampleEnv { + memory: Option, +} + +impl ExampleEnv { + fn set_memory(&mut self, memory: Memory) { + self.memory = Some(memory); + } + + fn get_memory(&self) -> &Memory { + self.memory.as_ref().unwrap() + } + + fn view<'a>(&'a self, store: &'a impl AsStoreRef) -> MemoryView<'a> { + self.get_memory().view(store) + } +} + +fn http_get(mut ctx: FunctionEnvMut, url: u32, url_len: u32) -> u32 { + // Setup environment + let (response, memory_size) = { + // Read url from memory + let view = ctx.data().view(&ctx); + let memory_size = view.data_size() as usize; + let address = read_string(&view, url, url_len).unwrap(); + + // Get request + let response = ureq::get(&address).call().unwrap(); + let capacity = match response.header("Content-Length").map(|it| it.parse::()) { + Some(Ok(len)) => len, + _ => 1024, + }; + let mut buffer = Vec::with_capacity(capacity); + let mut reader = response.into_reader(); + reader.read_to_end(&mut buffer).unwrap(); + (buffer, memory_size) + }; + + // If the response is too big, grow memory + if 1114112 + response.len() > memory_size { + let delta = (1114112 + response.len() - memory_size) / wasmer::WASM_PAGE_SIZE + 1; + let memory = ctx.data().get_memory().clone(); + memory.grow(&mut ctx, delta as u32).unwrap(); + } + + // Write response as string [ptr, cap, len] to wasm memory and return pointer + let view = ctx.data().view(&ctx); + view.write(1114112, &u32::to_le_bytes(1114124)).unwrap(); + view.write(1114116, &u32::to_le_bytes(response.len() as u32)).unwrap(); + view.write(1114120, &u32::to_le_bytes(response.len() as u32)).unwrap(); + view.write(1114124, &response).unwrap(); + 1114112 +} + +fn main() -> Result<()> { + let wasm_bytes = wat2wasm( + br#" +(module + (type (;0;) (func (param i32 i32) (result i32))) + (type (;1;) (func (result i32))) + (type (;2;) (func)) + (import "env" "http_get" (func (;0;) (type 0))) + (func (;1;) (type 1) (result i32) + i32.const 1048576 + i32.const 45 + call 0 + i32.const 8 + i32.add + i32.load) + (func (;2;) (type 2)) + (func (;3;) (type 2) + call 2 + call 2) + (func (;4;) (type 1) (result i32) + call 1 + call 3) + (table (;0;) 1 1 funcref) + (memory (;0;) 17) + (global (;0;) (mut i32) (i32.const 1048576)) + (export "memory" (memory 0)) + (export "fetch" (func 4)) + (data (;0;) (i32.const 1048576) "https://postman-echo.com/bytes/5/mb?type=json")) +"#, + )?; + + // Load module + let mut store = Store::default(); + let module = Module::new(&store, wasm_bytes)?; + + // Add host functions + let function_env = FunctionEnv::new(&mut store, ExampleEnv { memory: None }); + let import_object = imports! { + // We use the default namespace "env". + "env" => { + // And call our function "http_get". + "http_get" => Function::new_typed_with_env(&mut store, &function_env, http_get), + } + }; + + // Create instance + let instance = Instance::new(&mut store, &module, &import_object)?; + let memory = instance.exports.get_memory("memory")?; + + // Give reference to memory + function_env.as_mut(&mut store).set_memory(memory.clone()); + + // Call function + let fetch = instance.exports.get_function("fetch")?; + let result = fetch.call(&mut store, &[])?; + println!("Response size: {result:?}"); + + Ok(()) +}