use crate::{Request, Response};
#[cfg(feature = "native-async")]
use async_channel::{Receiver, Sender};
pub fn fetch_blocking(request: &Request) -> crate::Result<Response> {
let mut req = ureq::request(&request.method, &request.url);
if let Some(timeout) = request.timeout {
req = req.timeout(timeout);
}
for (k, v) in &request.headers {
req = req.set(k, v);
}
let resp = if request.body.is_empty() {
req.call()
} else {
req.send_bytes(&request.body)
};
let (ok, resp) = match resp {
Ok(resp) => (true, resp),
Err(ureq::Error::Status(_, resp)) => (false, resp), Err(ureq::Error::Transport(err)) => return Err(err.to_string()),
};
let url = resp.get_url().to_owned();
let status = resp.status();
let status_text = resp.status_text().to_owned();
let mut headers = crate::Headers::default();
for key in &resp.headers_names() {
if let Some(value) = resp.header(key) {
headers.insert(key, value.to_owned());
}
}
headers.sort();
let mut reader = resp.into_reader();
let mut bytes = vec![];
use std::io::Read as _;
if let Err(err) = reader.read_to_end(&mut bytes) {
if request.method == "HEAD" && err.kind() == std::io::ErrorKind::UnexpectedEof {
} else {
return Err(format!("Failed to read response body: {err}"));
}
}
let response = Response {
url,
ok,
status,
status_text,
headers,
bytes,
};
Ok(response)
}
pub(crate) fn fetch(request: Request, on_done: Box<dyn FnOnce(crate::Result<Response>) + Send>) {
std::thread::Builder::new()
.name("ehttp".to_owned())
.spawn(move || on_done(fetch_blocking(&request)))
.expect("Failed to spawn ehttp thread");
}
#[cfg(feature = "native-async")]
pub(crate) async fn fetch_async(request: Request) -> crate::Result<Response> {
let (tx, rx): (
Sender<crate::Result<Response>>,
Receiver<crate::Result<Response>>,
) = async_channel::bounded(1);
fetch(
request,
Box::new(move |received| tx.send_blocking(received).unwrap()),
);
rx.recv().await.map_err(|err| err.to_string())?
}