halldyll_core/fetch/
client.rs

1//! HTTP Client - Configuration and connection pooling
2
3use reqwest::{Client, ClientBuilder};
4use std::sync::Arc;
5use std::time::Duration;
6
7use crate::types::{Config, Error};
8use crate::types::error::Result;
9
10/// Thread-safe shareable HTTP client
11#[derive(Clone)]
12pub struct HttpClient {
13    inner: Arc<Client>,
14    config: Arc<Config>,
15}
16
17impl HttpClient {
18    /// Creates a new HTTP client with the given configuration
19    pub fn new(config: Config) -> Result<Self> {
20        let client = Self::build_client(&config)?;
21        Ok(Self {
22            inner: Arc::new(client),
23            config: Arc::new(config),
24        })
25    }
26
27    /// Builds the reqwest client
28    fn build_client(config: &Config) -> Result<Client> {
29        let mut builder = ClientBuilder::new()
30            .user_agent(&config.fetch.user_agent)
31            .timeout(Duration::from_millis(config.fetch.total_timeout_ms))
32            .connect_timeout(Duration::from_millis(config.fetch.connect_timeout_ms))
33            .pool_max_idle_per_host(10)
34            .pool_idle_timeout(Duration::from_secs(90))
35            .tcp_nodelay(true)
36            .tcp_keepalive(Duration::from_secs(60));
37
38        // Redirects
39        if config.fetch.follow_redirects {
40            builder = builder.redirect(reqwest::redirect::Policy::limited(
41                config.fetch.max_redirects as usize,
42            ));
43        } else {
44            builder = builder.redirect(reqwest::redirect::Policy::none());
45        }
46
47        // Decompression
48        if config.fetch.enable_decompression {
49            builder = builder
50                .gzip(true)
51                .brotli(true)
52                .deflate(true);
53        } else {
54            builder = builder
55                .gzip(false)
56                .brotli(false)
57                .deflate(false);
58        }
59
60        builder.build().map_err(Error::Network)
61    }
62
63    /// Access to the inner client
64    pub fn client(&self) -> &Client {
65        &self.inner
66    }
67
68    /// Access to the configuration
69    pub fn config(&self) -> &Config {
70        &self.config
71    }
72
73    /// Configured User-Agent
74    pub fn user_agent(&self) -> &str {
75        &self.config.fetch.user_agent
76    }
77}