extern crate serde;
extern crate serde_derive;
extern crate serde_json;
extern crate wapc_guest as guest;
#[macro_use(defer)]
extern crate scopeguard;
pub mod bccontext;
pub mod bccontext_core;
pub mod bccontext_error;
pub mod bccontext_ext;
pub mod bccontext_search;
pub mod bccontext_struct;
pub use self::bccontext::*;
pub use self::bccontext_core::*;
pub use self::bccontext_error::*;
pub use self::bccontext_ext::*;
pub use self::bccontext_search::*;
pub use self::bccontext_struct::*;
use std::str;
use guest::prelude::*;
use std::collections::HashMap;
use lazy_static::lazy_static;
use std::sync::Mutex;
#[derive(Clone)]
struct HandlerData<'a> {
pub hf: HandlerFunction<'a>,
pub req: Option<BitcodeContext>,
}
lazy_static! {
static ref CALLMAP: Mutex<HashMap<String, HandlerData<'static>>> = Mutex::new(HashMap::new());
}
#[macro_export]
macro_rules! register_handlers {
() => {};
($handler_name:literal, $handler_func:ident $(,$more_name:literal, $more_func:ident )*) => {
register_handler($handler_name, $handler_func);
register_handlers!($( $more_name, $more_func ),* );
}
}
#[macro_export]
macro_rules! implement_bitcode_module {
($handler_name:literal, $handler_func:ident $(, $more_lit:literal, $more:ident)*) => {
extern crate wapc_guest as guest;
use guest::{register_function, CallResult, console_log};
use std::panic;
use std::io;
use elvwasm::register_handlers;
fn hook_impl(info: &std::panic::PanicInfo) {
let _ = console_log(&format!("Panic is WASM!! {}", info));
}
#[no_mangle]
pub extern "C" fn wapc_init() {
register_handlers!($handler_name, $handler_func $(, $more_lit, $more)*);
register_function("_JPC", jpc);
panic::set_hook(Box::new(hook_impl));
}
}
}
#[cfg(test)]
mod tests {
macro_rules! output_raw_pointers {
($raw_ptr:ident, $raw_len:ident) => {
unsafe {
std::str::from_utf8(std::slice::from_raw_parts($raw_ptr, $raw_len))
.unwrap_or("unable to convert")
}
};
}
#[no_mangle]
pub extern "C" fn __console_log(ptr: *const u8, len: usize) {
let out_str = output_raw_pointers!(ptr, len);
println!("console output : {}", out_str);
}
#[no_mangle]
pub extern "C" fn __host_call(
bd_ptr: *const u8,
bd_len: usize,
ns_ptr: *const u8,
ns_len: usize,
op_ptr: *const u8,
op_len: usize,
ptr: *const u8,
len: usize,
) -> usize {
let out_bd = output_raw_pointers!(bd_ptr, bd_len);
let out_ns = output_raw_pointers!(ns_ptr, ns_len);
let out_op = output_raw_pointers!(op_ptr, op_len);
let out_ptr = output_raw_pointers!(ptr, len);
println!(
"host call bd = {} ns = {} op = {}, ptr={}",
out_bd, out_ns, out_op, out_ptr
);
0
}
#[no_mangle]
pub extern "C" fn __host_response(ptr: *const u8) {
println!("host __host_response ptr = {:?}", ptr);
}
#[no_mangle]
pub extern "C" fn __host_response_len() -> usize {
println!("host __host_response_len");
0
}
#[no_mangle]
pub extern "C" fn __host_error_len() -> usize {
println!("host __host_error_len");
0
}
#[no_mangle]
pub extern "C" fn __host_error(ptr: *const u8) {
println!("host __host_error ptr = {:?}", ptr);
}
#[no_mangle]
pub extern "C" fn __guest_response(ptr: *const u8, len: usize) {
let out_resp = output_raw_pointers!(ptr, len);
println!("host __guest_response ptr = {}", out_resp);
}
#[no_mangle]
pub extern "C" fn __guest_error(ptr: *const u8, len: usize) {
let out_error = output_raw_pointers!(ptr, len);
println!("host __guest_error ptr = {}", out_error);
}
#[no_mangle]
pub extern "C" fn __guest_request(op_ptr: *const u8, ptr: *const u8) {
println!("host __guest_request op_ptr = {:?} ptr = {:?}", op_ptr, ptr);
}
pub use self::bccontext::*;
pub use self::bccontext_struct::QList;
use super::*;
use serde_json::*;
fn handler_for_test(bcc: &mut BitcodeContext) -> CallResult {
bcc.make_success("DONE")
}
#[test]
fn test_basic_http() {
register_handler("testing", handler_for_test);
let test_json = json!({
"id" : "dummydummy",
"jpc" : "1.0",
"method" : "content",
"params" : {
"http" : {
"path" : "/testing",
"verb" : "GET",
},
},
"qinfo" : {
"qlib_id" : "idlib1234",
"type" : "some_type",
},
});
match serde_json::to_vec(&test_json) {
Ok(x) => {
let res = jpc(&x);
match res {
Ok(_) => {}
Err(err) => {
panic!("failed test_http err = {:?}", err);
}
}
}
Err(err) => {
panic!("failed test_http err = {:?}", err);
}
};
}
#[test]
fn test_basic_http_failure() {
register_handler("test_handler", handler_for_test);
let test_json = json!({
"id" : "dummydummy",
"jpc" : "1.0",
"method" : "GET",
"params" : {
"http" : {
},
},
});
match serde_json::to_vec(&test_json) {
Ok(x) => {
let res = jpc(&x);
match res {
Ok(k) => {
let mut res_json: serde_json::Map<String, serde_json::Value> =
serde_json::from_slice(&k).unwrap();
let mut err_json: serde_json::Map<String, serde_json::Value> =
serde_json::from_value(res_json["error"].take()).unwrap();
println!("{:?}", err_json);
assert_eq!(err_json["op"], 2);
let err_json_data: serde_json::Map<String, serde_json::Value> =
serde_json::from_value(err_json["data"].take()).unwrap();
assert_eq!(err_json_data["op"], 2);
}
Err(err) => {
panic!("failed test_http err = {:?}", err);
}
}
}
Err(err) => {
panic!("failed test_http err = {:?}", err);
}
};
}
}
type HandlerFunction<'a> = fn(bcc: &'a mut BitcodeContext) -> CallResult;
#[no_mangle]
pub fn register_handler(name: &str, h: HandlerFunction<'static>) {
let hd = HandlerData { hf: h, req: None };
match CALLMAP.lock().as_mut() {
Ok(x) => {
x.insert(name.to_string(), hd);
}
Err(e) => console_log(&format!("MutexGuard unable to aquire lock, error = {e}")),
};
}
#[cfg(test)]
pub fn console_log(s: &str) {
println!("{}", s)
}
const ID_NOT_CALCULATED_YET: &str = "id not yet calculated";
fn do_bitcode(json_params: Request) -> CallResult {
console_log("Parameters parsed");
let split_path: Vec<&str> = json_params.params.http.path.as_str().split('/').collect();
console_log(&format!("splitpath={split_path:?}"));
let mut v_leaks: Vec<Box<BitcodeContext>> = Vec::<Box<BitcodeContext>>::new();
let cm = match CALLMAP.lock() {
Ok(c) => c,
Err(e) => {
return make_json_error(
ErrorKinds::BadHttpParams(format!("unable to gain access to callmap: error = {e}")),
ID_NOT_CALCULATED_YET,
)
}
};
let mut bind = cm.get(split_path[1]).into_iter();
let cm_handler = match bind.find(|mut _x| true).as_mut() {
Some(b) => b.to_owned(),
None => {
return Err(Box::new(ErrorKinds::Invalid(format!(
"handler not found {}",
split_path[1]
))))
}
};
match cm_handler.req {
Some(f) => {
let id = f.request.id.to_string();
let bcc = Box::new(f);
let l = Box::leak(bcc);
unsafe {
v_leaks.push(Box::from_raw(l));
}
match (cm_handler.hf)(l) {
Ok(o) => Ok(o),
Err(e) => make_json_error(ErrorKinds::Other(e.to_string()), &id),
}
}
None => {
let bcc = BitcodeContext {
request: json_params.clone(),
};
let id = bcc.request.id.clone();
let l = Box::leak(Box::new(bcc));
unsafe {
v_leaks.push(Box::from_raw(l));
}
match (cm_handler.hf)(l) {
Ok(o) => Ok(o),
Err(e) => make_json_error(ErrorKinds::Other(e.to_string()), &id),
}
}
}
}
#[no_mangle]
pub fn jpc(_msg: &[u8]) -> CallResult {
console_log("In jpc");
let input_string = str::from_utf8(_msg)?;
console_log(&format!("parameters = {input_string}"));
let json_params: Request = match serde_json::from_str(input_string) {
Ok(m) => m,
Err(err) => {
return make_json_error(
ErrorKinds::Invalid(format!("parse failed for http error = {err}")),
ID_NOT_CALCULATED_YET,
);
}
};
console_log("Request parsed");
do_bitcode(json_params)
}