an otoroshi plugin to respond to an http call from a WASM script. very rought, ugly, underperformant and unstable ;)
sbt package
in the following commands, download the right wasmer runtime and use the right jar according to your operating system
wget https://github.com/MAIF/otoroshi/releases/download/v1.5.0-alpha.14/otoroshi.jar
wget https://github.com/wasmerio/wasmer-java/releases/download/0.3.0/wasmer-jni-amd64-darwin-0.3.0.jar
# or wget https://github.com/wasmerio/wasmer-java/releases/download/0.3.0/wasmer-jni-amd64-linux-0.3.0.jar
# or wget https://github.com/wasmerio/wasmer-java/releases/download/0.3.0/wasmer-jni-amd64-windows-0.3.0.jar
java -cp "./wasmer-jni-amd64-darwin-0.3.0.jar:./target/scala-2.12/otorshi-wasmer-plugin_2.12-1.0.0-dev.jar:./otoroshi.jar" -Dapp.adminLogin=admin -Dapp.adminPassword=password play.core.server.ProdServerStart
then log into otoroshi (admin/password), creates a new service exposed on http://wasm.oto.tools:8080/
and add the plugin in the transformer section and configure it like
{
"WasmerResponse": {
"pages": 1,
"wasm": "https://github.com/mathieuancelin/otoroshi-wasmer-plugin/raw/master/hello.wasm"
}
}
or just run the following command to create the service without being logged in
curl -X POST -H 'Content-Type: application/json' \
http://otoroshi-api.oto.tools:8080/api/services/_template \
-u admin-api-apikey-id:admin-api-apikey-secret \
-d '{
"id": "wasm-test",
"name": "wasm-test",
"env": "prod",
"domain": "oto.tools",
"subdomain": "wasm",
"targets": [{ "host": "lolcatho.st:8080", "scheme": "http" }],
"root": "/",
"publicPatterns": [
"/.*"
],
"enforceSecureCommunication": false,
"sendStateChallenge": false,
"overrideHost": false,
"transformerRefs": ["cp:otoroshi_plugins.fr.maif.otoroshi.plugins.wasmer.WasmerResponse"],
"transformerConfig": {
"WasmerResponse": {
"pages": 1,
"_wasm":"file://./hello.wasm",
"wasm": "https://github.com/mathieuancelin/otoroshi-wasmer-plugin/raw/master/hello.wasm"
}
}
}'
curl http://wasm.oto.tools:8080/ --include
HTTP/1.1 200 OK
Date: Fri, 28 May 2021 12:53:55 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 37
<h1>Hello from lolcatho.st:8080!</h1>
[package]
name = "oto-plugin-wasm"
version = "0.1.0"
edition = "2018"
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2"
json = "0.12.4"
use std::ffi::{CStr, CString};
use std::mem;
use std::os::raw::{c_char, c_void};
use std::str;
extern crate json;
extern crate base64;
#[no_mangle]
pub extern fn allocate(size: usize) -> *mut c_void {
let mut buffer = Vec::with_capacity(size);
let pointer = buffer.as_mut_ptr();
mem::forget(buffer);
pointer as *mut c_void
}
#[no_mangle]
pub extern fn deallocate(pointer: *mut c_void, capacity: usize) {
unsafe {
let _ = Vec::from_raw_parts(pointer, 0, capacity);
}
}
#[no_mangle]
pub extern fn handle_http_request(pointer: *mut u8, length: u32) -> *const u8 {
let (c_string, _bytes) = unsafe {
let bytes: &mut [u8] = core::slice::from_raw_parts_mut(pointer, length as usize);
match str::from_utf8_mut(bytes) {
Err(why) => {
let why_str = why.to_string();
let formatted = format!(
r#"{{ "err": "err_from_ptr", "err_desc": "{}" }}"#,
why_str,
);
(formatted, bytes)
},
Ok(res) => (String::from(res), bytes),
}
};
match json::parse(c_string.as_str()) {
Err(why) => {
let why_str = why.to_string();
let formatted = format!(
r#"{{ "err": "err_json_parse", "err_desc": "{}" }}"#,
why_str,
);
formatted.as_bytes().as_ptr()
},
Ok(ctx) => {
match ctx["err"].as_str() {
None => {
let method = ctx["method"].as_str().unwrap_or("--");
let path = ctx["path"].as_str().unwrap_or("--");
let host = ctx["headers"]["Host"].as_str().unwrap_or("--");
let response = match (method, path) {
("GET", "/") => {
json::object!{
"status": 200,
"body": format!("<h1>Hello from {}!</h1>", host),
"headers": {
"Content-Type": "text/html; charset=utf-8",
}
}
},
_ => {
json::object!{
"status": 404,
"body": {
"err": "resource not found",
},
"headers": {
"Content-Type": "application/json",
}
}
},
};
response.to_string().as_bytes().as_ptr()
},
Some(err) => {
let err_desc = ctx["err_desc"].as_str().unwrap_or("--");
let response = json::object!{
"status": 500,
"headers": {},
"body": {
"err": err,
"err_desc": err_desc
}
};
response.to_string().as_bytes().as_ptr()
}
}
}
}
}
build with
cargo install wasm-pack
wasm-pack build --target web