From a116b1c2e67d355ce9a2fbad1b1935927adad497 Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Sat, 23 Jul 2022 00:21:43 +0800 Subject: [PATCH] feat: update container --- .../container/src/fn_common.rs | 27 +++++ .../container/src/fn_fetch.rs | 112 ++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 __wasm/wit-bindgen-sample/container/src/fn_common.rs create mode 100644 __wasm/wit-bindgen-sample/container/src/fn_fetch.rs diff --git a/__wasm/wit-bindgen-sample/container/src/fn_common.rs b/__wasm/wit-bindgen-sample/container/src/fn_common.rs new file mode 100644 index 0000000..9b3d50b --- /dev/null +++ b/__wasm/wit-bindgen-sample/container/src/fn_common.rs @@ -0,0 +1,27 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct FnResult { + error: Option, + result: Option, +} + +impl FnResult { + pub fn fail(message: impl Into) -> Self { + FnResult { + result: None, + error: Some(message.into()), + } + } + + pub fn success(result: String) -> Self { + FnResult { + result: Some(result), + error: None, + } + } + + pub fn to_json(&self) -> String { + serde_json::to_string(&self).expect("JSResult to json error") + } +} diff --git a/__wasm/wit-bindgen-sample/container/src/fn_fetch.rs b/__wasm/wit-bindgen-sample/container/src/fn_fetch.rs new file mode 100644 index 0000000..f790fdc --- /dev/null +++ b/__wasm/wit-bindgen-sample/container/src/fn_fetch.rs @@ -0,0 +1,112 @@ +use std::collections::BTreeMap; +use std::io::{ErrorKind, Read, Write}; +use std::time::Duration; + +use reqwest::Method; +use serde::{Deserialize, Serialize}; +use serde_json::{Map, Number, Value}; + +use crate::fn_common::FnResult; + +#[derive(Clone, Debug, Serialize, Deserialize)] +struct FetchParams { + url: String, + options: Option, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +struct FetchOptions { + method: Option, + headers: Option>, + body: Option, +} + +pub fn do_fetch(params: &str) -> FnResult { + let fetch_params: FetchParams = match serde_json::from_str(params) { + Err(e) => return FnResult::fail(format!("Fetch param parse error: {}, raw params: {}", e, params)), + Ok(params) => params, + }; + if fetch_params.url.is_empty() { + return FnResult::fail("Param url cannot be empty"); + } + let client = match reqwest::blocking::Client::builder() + .timeout(Duration::from_secs(8)) + .connect_timeout(Duration::from_secs(3)) + // .proxy(reqwest::Proxy::all("http://127.0.0.1:1086").expect("to proxy failed")) + .build() { + Err(e) => return FnResult::fail(format!("Create client failed: {}", e)), + Ok(client) => client, + }; + let method = fetch_params.options.as_ref().map(|options| options.method.as_ref().map(|m| m.to_uppercase())).flatten(); + let method_str = method.as_ref().map(|m| m.as_str()).unwrap_or_else(|| "GET"); + let request_method = match method_str { + "GET" => Method::GET, + "POST" => Method::POST, + m => return FnResult::fail(format!("Unsupported method: {}", m)), + }; + let mut request_builder = client.request(request_method.clone(), &fetch_params.url); + if let Some(options) = &fetch_params.options { + let mut has_user_agent = false; + if let Some(headers) = &options.headers { + for (k, v) in headers { + if k.to_lowercase() == "user-agent" { + has_user_agent = true; + } + request_builder = request_builder.header(k.to_string(), v.to_string()); + } + } + if !has_user_agent { + request_builder = request_builder.header("User-Agent", "JavaScriptSandboxContainer/0.1"); + } + + if let Some(body) = &options.body { + if Method::POST == request_method { + let body = reqwest::blocking::Body::from(body.to_string()); + request_builder = request_builder.body(body); + } + } + } + + let mut fetch_response = match request_builder.send() { + Err(e) => return FnResult::fail(format!("Send request failed: {}", e)), + Ok(fetch_response) => fetch_response, + }; + + let status = fetch_response.status().as_u16(); + let headers = fetch_response.headers().clone(); + + let mut body_buff = Vec::new(); + let mut buff = [0_u8; 1024]; + loop { + match fetch_response.read(&mut buff) { + Err(e) if e.kind() == ErrorKind::Interrupted => continue, + Err(e) => return FnResult::fail(format!("Fetch response failed: {}", e)), + Ok(len) => if len == 0 { + break; + } else { + if let Err(e) = body_buff.write_all(&buff[0..len]) { + return FnResult::fail(format!("Fetch response failed: {}", e)); + } + if body_buff.len() > 1024 * 1024 { + return FnResult::fail(format!("Fetch response too large, {} more than 1MB", body_buff.len())); + } + } + } + } + + let mut result_map = Map::new(); + result_map.insert("status".to_string(), Value::Number(Number::from(status))); + let mut header_map = Map::new(); + for (k, v) in headers { + let header_key = k.map(|n| n.as_str().to_string()).unwrap_or_else(|| "".to_string()); + let header_value = match v.to_str() { + Err(_) => continue, + Ok(v) => v.to_string(), + }; + header_map.insert(header_key, Value::String(header_value)); + } + result_map.insert("headers".to_string(), Value::Object(header_map)); + result_map.insert("body".to_string(), Value::String(String::from_utf8_lossy(&body_buff).to_string())); + + FnResult::success(format!("{}", Value::Object(result_map))) +}