parse_book_source/http_client/
mod.rs1use crate::{HttpConfig, Result};
2use anyhow::anyhow;
3use rate_limiter::TokenBucket;
4use reqwest::{
5 Body, Client, ClientBuilder,
6 header::{HeaderMap, HeaderName, HeaderValue},
7};
8use std::time::Duration;
9pub mod rate_limiter;
10
11#[derive(Debug, Clone)]
12pub struct HttpClient {
13 pub client: Client,
14 pub base_url: String,
15 pub rate_limiter: Option<TokenBucket>,
16}
17
18impl HttpClient {
19 pub fn new(base_url: &str, config: &HttpConfig) -> Result<Self> {
20 let mut client = ClientBuilder::new().cookie_store(true);
21
22 if let Some(header) = &config.header {
23 let mut headers = HeaderMap::new();
24
25 for (k, v) in header {
26 headers.insert(
27 HeaderName::try_from(k)
28 .map_err(|e| anyhow!("header name is not valid: {}", e))?,
29 HeaderValue::from_str(v)
30 .map_err(|e| anyhow!("header value is not valid: {}", e))?,
31 );
32 }
33 client = client.default_headers(headers);
34 }
35
36 if let Some(timeout) = config.timeout {
37 client = client.timeout(Duration::from_millis(timeout));
38 }
39
40 Ok(Self {
41 client: client.build()?,
42 base_url: base_url.to_string(),
43 rate_limiter: config.rate_limit.as_ref().map(|rate_limit| {
44 TokenBucket::new(
45 rate_limit.max_count as usize,
46 Duration::from_secs_f64(rate_limit.fill_duration),
47 )
48 }),
49 })
50 }
51
52 fn url_with_base(&self, url: &str) -> String {
53 if url.starts_with("http") {
54 url.to_string()
55 } else if url.starts_with('/') {
56 format!("{}{}", self.base_url, url)
57 } else {
58 format!("{}/{}", self.base_url, url)
59 }
60 }
61
62 pub async fn get(&self, url: &str) -> Result<reqwest::Response> {
63 if let Some(rate_limiter) = &self.rate_limiter {
64 rate_limiter.acquire().await;
65 }
66
67 let url = self.url_with_base(url);
68
69 Ok(self.client.get(url).send().await?)
70 }
71
72 pub async fn post<T: Into<Body>>(&self, url: &str, body: T) -> Result<reqwest::Response> {
73 if let Some(rate_limiter) = &self.rate_limiter {
74 rate_limiter.acquire().await;
75 }
76
77 let url = self.url_with_base(url);
78
79 Ok(self.client.post(url).body(body).send().await?)
80 }
81}