Skip to main content

whatsapp_rust_ureq_http_client/
lib.rs

1use anyhow::Result;
2use async_trait::async_trait;
3use wacore::net::{HttpClient, HttpRequest, HttpResponse, StreamingHttpResponse};
4
5/// HTTP client implementation using `ureq` for synchronous HTTP requests.
6/// Since `ureq` is blocking, all requests are wrapped in `tokio::task::spawn_blocking`.
7#[derive(Debug, Clone)]
8pub struct UreqHttpClient;
9
10impl UreqHttpClient {
11    pub fn new() -> Self {
12        Self
13    }
14}
15
16impl Default for UreqHttpClient {
17    fn default() -> Self {
18        Self::new()
19    }
20}
21
22#[async_trait]
23impl HttpClient for UreqHttpClient {
24    async fn execute(&self, request: HttpRequest) -> Result<HttpResponse> {
25        // Since ureq is blocking, we must use spawn_blocking
26        tokio::task::spawn_blocking(move || {
27            let response = match request.method.as_str() {
28                "GET" => {
29                    let mut req = ureq::get(&request.url);
30                    for (key, value) in &request.headers {
31                        req = req.header(key, value);
32                    }
33                    req.call()?
34                }
35                "POST" => {
36                    let mut req = ureq::post(&request.url);
37                    for (key, value) in &request.headers {
38                        req = req.header(key, value);
39                    }
40                    if let Some(body) = request.body {
41                        req.send(&body[..])?
42                    } else {
43                        req.send(&[])?
44                    }
45                }
46                method => {
47                    return Err(anyhow::anyhow!("Unsupported HTTP method: {}", method));
48                }
49            };
50
51            let status_code = response.status().as_u16();
52
53            // Read the response body
54            let mut body = response.into_body();
55            let body_bytes = body.read_to_vec()?;
56
57            Ok(HttpResponse {
58                status_code,
59                body: body_bytes,
60            })
61        })
62        .await?
63    }
64
65    fn execute_streaming(&self, request: HttpRequest) -> Result<StreamingHttpResponse> {
66        // Note: no spawn_blocking here — this is called FROM within spawn_blocking
67        // by the streaming download code. The entire HTTP fetch + decrypt happens
68        // in one blocking thread.
69        let response = match request.method.as_str() {
70            "GET" => {
71                let mut req = ureq::get(&request.url);
72                for (key, value) in &request.headers {
73                    req = req.header(key, value);
74                }
75                req.call()?
76            }
77            method => {
78                return Err(anyhow::anyhow!(
79                    "Streaming only supports GET, got: {}",
80                    method
81                ));
82            }
83        };
84
85        let status_code = response.status().as_u16();
86        let reader = response.into_body().into_reader();
87
88        Ok(StreamingHttpResponse {
89            status_code,
90            body: Box::new(reader),
91        })
92    }
93}