feat: update container
This commit is contained in:
@@ -1,155 +1,20 @@
|
|||||||
extern crate core;
|
extern crate core;
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
|
||||||
use std::io::{ErrorKind, Read, Write};
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use reqwest::Method;
|
use serde_json::Value;
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use serde_json::{Map, Number, Value};
|
|
||||||
use wasmtime::{Config, Engine, Instance, Linker, Module, Store};
|
use wasmtime::{Config, Engine, Instance, Linker, Module, Store};
|
||||||
|
|
||||||
|
mod fn_common;
|
||||||
|
mod fn_fetch;
|
||||||
|
|
||||||
wit_bindgen_wasmtime::export!("../container.wit");
|
wit_bindgen_wasmtime::export!("../container.wit");
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
struct FetchResult {
|
|
||||||
error: Option<String>,
|
|
||||||
result: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FetchResult {
|
|
||||||
fn fail(message: impl Into<String>) -> Self {
|
|
||||||
FetchResult {
|
|
||||||
result: None,
|
|
||||||
error: Some(message.into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn success(result: String) -> Self {
|
|
||||||
FetchResult {
|
|
||||||
result: Some(result),
|
|
||||||
error: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_json(&self) -> String {
|
|
||||||
serde_json::to_string(&self).expect("JSResult to json error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<String> for FetchResult {
|
|
||||||
fn into(self) -> String {
|
|
||||||
self.to_json()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
struct FetchParams {
|
|
||||||
url: String,
|
|
||||||
options: Option<FetchOptions>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
struct FetchOptions {
|
|
||||||
method: Option<String>,
|
|
||||||
headers: Option<BTreeMap<String, String>>,
|
|
||||||
body: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct MyContainer;
|
pub struct MyContainer;
|
||||||
|
|
||||||
impl container::Container for MyContainer {
|
impl container::Container for MyContainer {
|
||||||
fn fetch(&mut self, params: &str) -> String {
|
fn fetch(&mut self, params: &str) -> String {
|
||||||
let fetch_params: FetchParams = match serde_json::from_str(params) {
|
fn_fetch::do_fetch(params).to_json()
|
||||||
Err(e) => return FetchResult::fail(format!("Fetch param parse error: {}, raw params: {}", e, params)).to_json(),
|
|
||||||
Ok(params) => params,
|
|
||||||
};
|
|
||||||
if fetch_params.url.is_empty() {
|
|
||||||
return FetchResult::fail("Param url cannot be empty").to_json();
|
|
||||||
}
|
|
||||||
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 FetchResult::fail(format!("Create client failed: {}", e)).to_json(),
|
|
||||||
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 FetchResult::fail(format!("Unsupported method: {}", m)).to_json(),
|
|
||||||
};
|
|
||||||
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 FetchResult::fail(format!("Send request failed: {}", e)).to_json(),
|
|
||||||
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 FetchResult::fail(format!("Fetch response failed: {}", e)).to_json(),
|
|
||||||
Ok(len) => if len == 0 {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
if let Err(e) = body_buff.write_all(&buff[0..len]) {
|
|
||||||
return FetchResult::fail(format!("Fetch response failed: {}", e)).to_json();
|
|
||||||
}
|
|
||||||
if body_buff.len() > 1024 * 1024 {
|
|
||||||
return FetchResult::fail(format!("Fetch response too large, {} more than 1MB", body_buff.len())).to_json();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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()));
|
|
||||||
|
|
||||||
|
|
||||||
FetchResult::success(format!("{}", Value::Object(result_map))).to_json()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,15 +30,15 @@ fn main() -> Result<()> {
|
|||||||
)?;
|
)?;
|
||||||
let a = exports.eval_javascript(&mut store, r##"
|
let a = exports.eval_javascript(&mut store, r##"
|
||||||
function hi(name) { return "hi: " + name; }
|
function hi(name) { return "hi: " + name; }
|
||||||
// let a = [];
|
let a = [];
|
||||||
// a.push(fetch('https://hatter.ink/util/print_request.action'));
|
a.push(fetch('https://hatter.ink/util/print_request.action'));
|
||||||
// for (let i = 0; i < 3; i++) { a.push(i); }
|
for (let i = 0; i < 3; i++) { a.push(i); }
|
||||||
// a.push({
|
a.push({
|
||||||
// id: 1, name: 'hatter'
|
id: 1, name: 'hatter'
|
||||||
// });
|
});
|
||||||
// a.push(hi('001'));
|
a.push(hi('001'));
|
||||||
// a.push(hi('002'));
|
a.push(hi('002'));
|
||||||
let a = fetch('https://hatter.ink/util/print_request.action');
|
// let a = fetch('https://hatter.ink/util/print_request.action');
|
||||||
JSON.stringify(a)
|
JSON.stringify(a)
|
||||||
// a
|
// a
|
||||||
"##);
|
"##);
|
||||||
|
|||||||
Reference in New Issue
Block a user