#![doc = include_str!("README.md")]
use anyhow::anyhow;
use response::Response;
use rquickjs::loader::Bundle;
use rquickjs::prelude::*;
use rquickjs::{
AsyncContext, AsyncRuntime, Class, Ctx, Exception, Module, Result, async_with, embed,
};
use std::collections::HashMap;
use std::str::FromStr;
use std::sync::Arc;
use std::time::Duration;
use super::check_exception;
mod response;
static BUNDLE: Bundle = embed! {
"headers.js": "src/javascript/fetch/headers.js",
"fetch.js": "src/javascript/fetch/fetch.js",
};
pub struct SendHttpRequest;
async fn send_http_request(
client: Arc<reqwest::Client>,
ctx: Ctx<'_>,
method: String,
url: String,
headers: Option<HashMap<String, String>>,
body: Option<String>,
timeout_ms: Option<u64>,
) -> Result<Class<'_, Response>> {
let method = reqwest::Method::from_str(&method)
.map_err(|e| Exception::throw_syntax(&ctx, &e.to_string()))?;
let mut request = client.request(method, url);
if let Some(headers) = headers {
for (key, value) in headers {
request = request.header(key, value);
}
}
if let Some(body) = body {
request = request.body(body);
}
if let Some(timeout_ms) = timeout_ms {
request = request.timeout(Duration::from_millis(timeout_ms));
}
let res = request.send().await.map_err(|e| {
let message = format!("{:#}", anyhow!(e)); Exception::throw_message(&ctx, &message)
})?;
let response = Response::new(res);
Class::instance(ctx, response)
}
impl<'js> rquickjs::IntoJs<'js> for SendHttpRequest {
fn into_js(self, ctx: &Ctx<'js>) -> Result<rquickjs::Value<'js>> {
let client = Arc::new(reqwest::Client::new());
rquickjs::Function::new(
ctx.clone(),
Async(
move |ctx: Ctx<'js>,
method: String,
url: String,
headers: Option<HashMap<String, String>>,
body: Option<String>,
timeout_ms: Option<u64>| {
send_http_request(client.clone(), ctx, method, url, headers, body, timeout_ms)
},
),
)?
.into_js(ctx)
}
}
pub async fn enable_fetch(rt: &AsyncRuntime, ctx: &AsyncContext) -> anyhow::Result<()> {
rt.set_loader(BUNDLE, BUNDLE).await;
async_with!(ctx => |ctx| {
ctx.globals()
.set("sendHttpRequest", SendHttpRequest)
.map_err(|e| check_exception(e, &ctx))?;
Module::evaluate(
ctx.clone(),
"enable_fetch",
r"
import { fetch, Headers, Request } from 'fetch.js';
globalThis.fetch = fetch;
globalThis.Headers = Headers;
globalThis.Request = Request;",
).map_err(|e| check_exception(e, &ctx))?;
Ok(())
})
.await
}