Skip to content

Commit

Permalink
Add http example
Browse files Browse the repository at this point in the history
  • Loading branch information
svelterust committed Jan 22, 2023
1 parent bf7031a commit 5e16f6d
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 16 deletions.
17 changes: 17 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 9 additions & 16 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ lazy_static = "1.4"
serial_test = "0.5"
compiler-test-derive = { path = "tests/lib/compiler-test-derive" }
tempfile = "3.1"
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"] }
Expand All @@ -87,26 +88,15 @@ tracing-subscriber = { version = "0.3", default-features = false, features = [
[features]
# Don't add the compiler features in default, please add them on the Makefile
# since we might want to autoconfigure them depending on the availability on the host.
default = [
"wat",
"wast",
"cache",
"wasi",
"engine",
"emscripten",
"middlewares",
]
default = ["wat", "wast", "cache", "wasi", "engine", "emscripten", "middlewares"]
engine = ["universal"]
universal = []
cache = ["wasmer-cache"]
wast = ["wasmer-wast"]
wasi = ["wasmer-wasi"]
emscripten = ["wasmer-emscripten"]
wat = ["wasmer/wat"]
compiler = [
"wasmer/compiler",
"wasmer-compiler/translator",
]
compiler = ["wasmer/compiler", "wasmer-compiler/translator"]
singlepass = ["wasmer-compiler-singlepass", "compiler"]
cranelift = ["wasmer-compiler-cranelift", "compiler"]
llvm = ["wasmer-compiler-llvm", "compiler"]
Expand All @@ -121,9 +111,7 @@ test-singlepass = ["singlepass"]
test-cranelift = ["cranelift"]
test-llvm = ["llvm"]

test-universal = [
"test-generator/test-universal",
]
test-universal = ["test-generator/test-universal"]

# Specifies that we're running in coverage testing mode. This disables tests
# that raise signals because that interferes with tarpaulin.
Expand Down Expand Up @@ -260,3 +248,8 @@ required-features = ["cranelift"]
name = "features"
path = "examples/features.rs"
required-features = ["cranelift"]

[[example]]
name = "http-dynamic-size"
path = "examples/http_dynamic_size.rs"
required-features = ["cranelift"]
122 changes: 122 additions & 0 deletions examples/http_dynamic_size.rs
Original file line number Diff line number Diff line change
@@ -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<String> {
Ok(WasmPtr::<u8>::new(start).read_utf8_string(view, len)?)
}

// Environment
pub struct ExampleEnv {
memory: Option<Memory>,
}

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<ExampleEnv>, 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::<usize>()) {
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(())
}

0 comments on commit 5e16f6d

Please sign in to comment.