Skip to main content

clawbox_server/
proxy_handler.rs

1//! Bridge between WASM host_call (sync) and ProxyService (async).
2//!
3//! Implements `HostCallHandler` for the proxy, allowing WASM tools
4//! to make HTTP requests through the proxy pipeline.
5
6use std::collections::HashMap;
7
8use clawbox_proxy::ProxyService;
9use clawbox_types::HostCallHandler;
10use tokio::runtime::Handle;
11
12/// Handler that bridges WASM host calls to the async ProxyService.
13#[non_exhaustive]
14pub struct ProxyHandler {
15    proxy: ProxyService,
16    handle: Handle,
17}
18
19impl ProxyHandler {
20    pub fn new(proxy: ProxyService, handle: Handle) -> Self {
21        Self { proxy, handle }
22    }
23}
24
25impl HostCallHandler for ProxyHandler {
26    /// Dispatch a host call to the proxy service.
27    ///
28    /// # Warning
29    /// This method uses `block_on` internally and **must** be called from
30    /// `spawn_blocking`, never directly from an async context. Calling it
31    /// from an async runtime thread will panic.
32    fn handle(
33        &self,
34        method: &str,
35        params: &serde_json::Value,
36    ) -> Result<serde_json::Value, String> {
37        match method {
38            "http_request" => {
39                let url = params
40                    .get("url")
41                    .and_then(|v| v.as_str())
42                    .ok_or("missing 'url' parameter")?
43                    .to_string();
44
45                let method_str = params
46                    .get("method")
47                    .and_then(|v| v.as_str())
48                    .unwrap_or("GET")
49                    .to_string();
50
51                let headers: HashMap<String, String> = params
52                    .get("headers")
53                    .and_then(|v| serde_json::from_value(v.clone()).ok())
54                    .unwrap_or_default();
55
56                let body = params
57                    .get("body")
58                    .and_then(|v| v.as_str())
59                    .map(|s| s.to_string());
60
61                // Bridge async ProxyService into sync HostCallHandler
62                let result = tokio::task::block_in_place(|| {
63                    self.handle.block_on(self.proxy.forward_request(
64                        &url,
65                        &method_str,
66                        headers,
67                        body,
68                    ))
69                });
70
71                match result {
72                    Ok(response) => Ok(serde_json::json!({
73                        "status": response.status,
74                        "headers": response.headers,
75                        "body": response.body
76                    })),
77                    Err(e) => Err(e.to_string()),
78                }
79            }
80            other => Err(format!("unknown host call method: {other}")),
81        }
82    }
83}