forked from wasmerio/wasmer
-
Notifications
You must be signed in to change notification settings - Fork 0
/
http_dynamic_size.rs
130 lines (114 loc) · 3.79 KB
/
http_dynamic_size.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
use anyhow::Result;
use wasmer::{
imports, wat2wasm, AsStoreRef, Function, FunctionEnv, FunctionEnvMut, Instance, Memory,
MemoryView, Module, Store, WasmPtr,
};
// 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(())
}