pub mod socket;
use crate::client::{Client, Http, Method, Request, Response, TransportError};
use std::sync::Arc;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
pub use socket::Feed;
pub const TIMEOUT: Duration = Duration::from_secs(30);
#[derive(Debug, Clone)]
pub struct Native {
client: reqwest::Client,
}
impl Native {
#[must_use]
pub fn new() -> Self {
Native::with_timeout(TIMEOUT)
}
#[must_use]
pub fn with_timeout(timeout: Duration) -> Self {
let client = reqwest::Client::builder().timeout(timeout).build().unwrap_or_else(|_| reqwest::Client::new());
Native { client }
}
#[must_use]
pub const fn with_client(client: reqwest::Client) -> Self {
Native { client }
}
}
impl Default for Native {
fn default() -> Self {
Native::new()
}
}
impl Http for Native {
async fn send(&self, request: Request) -> Result<Response, TransportError> {
request.refuse_if_offline()?;
let unreachable = |error: reqwest::Error| TransportError::Unreachable(error.without_url().to_string());
let mut outgoing = self.client.request(method(request.method), &request.url);
for (name, value) in &request.headers {
outgoing = outgoing.header(name, value);
}
if !request.body.is_empty() {
outgoing = outgoing.body(request.body);
}
let answer = outgoing.send().await.map_err(unreachable)?;
let status = answer.status().as_u16();
let headers = answer
.headers()
.iter()
.map(|(name, value)| (name.as_str().to_owned(), String::from_utf8_lossy(value.as_bytes()).into_owned()))
.collect();
let body = answer.bytes().await.map_err(unreachable)?.to_vec();
Ok(Response { status, headers, body })
}
}
fn method(method: Method) -> reqwest::Method {
match method {
Method::Get => reqwest::Method::GET,
Method::Post => reqwest::Method::POST,
}
}
pub const KEEP_WARM: Duration = Duration::from_secs(30);
pub fn keep_warm<S: Send + Sync + 'static>(
client: &Arc<Client<Native, S>>,
every: Duration,
) -> tokio::task::JoinHandle<()> {
let held = Arc::downgrade(client);
tokio::spawn(async move {
let mut ticks = tokio::time::interval(every);
loop {
ticks.tick().await;
let Some(client) = held.upgrade() else {
return;
};
client.warm().await;
}
})
}
#[must_use]
pub fn clock() -> u64 {
SystemTime::now().duration_since(UNIX_EPOCH).map_or(0, |since| u64::try_from(since.as_millis()).unwrap_or(u64::MAX))
}
#[cfg(test)]
#[path = "tests/http.rs"]
mod tests;