Skip to main content

flyr/
fetch.rs

1use std::sync::Arc;
2use std::time::Duration;
3
4use wreq::Client;
5use wreq::cookie::Jar;
6use wreq_util::Emulation;
7
8use crate::error::{self, FlightError};
9
10fn cache_buster() -> String {
11    use std::time::{SystemTime, UNIX_EPOCH};
12    SystemTime::now()
13        .duration_since(UNIX_EPOCH)
14        .unwrap()
15        .as_secs()
16        .to_string()
17}
18
19const BASE_URL: &str = "https://www.google.com/travel/flights";
20
21#[derive(Clone)]
22pub struct FetchOptions {
23    pub proxy: Option<String>,
24    pub timeout: u64,
25}
26
27impl Default for FetchOptions {
28    fn default() -> Self {
29        Self {
30            proxy: None,
31            timeout: 30,
32        }
33    }
34}
35
36pub async fn fetch_html(
37    params: &[(String, String)],
38    options: &FetchOptions,
39) -> Result<String, FlightError> {
40    let jar = Arc::new(Jar::default());
41    let url: wreq::Uri = "https://www.google.com".parse().unwrap();
42    jar.add(
43        "SOCS=CAESEwgDEgk2MjA5NDM1NjAaAmVuIAEaBgiA_Le-Bg",
44        &url,
45    );
46    jar.add("CONSENT=PENDING+987", &url);
47
48    let mut builder = Client::builder()
49        .emulation(Emulation::Chrome137)
50        .cookie_provider(jar)
51        .timeout(Duration::from_secs(options.timeout));
52
53    if let Some(ref proxy) = options.proxy {
54        builder = builder.proxy(
55            wreq::Proxy::all(proxy).map_err(error::from_http_error)?,
56        );
57    }
58
59    let client = builder.build().map_err(error::from_http_error)?;
60
61    let mut params = params.to_vec();
62    params.push(("cx".to_string(), cache_buster()));
63
64    let response = client
65        .get(BASE_URL)
66        .query(&params)
67        .send()
68        .await
69        .map_err(error::from_http_error)?;
70
71    let status = response.status().as_u16();
72    match status {
73        200 => {}
74        429 => return Err(FlightError::RateLimited),
75        403 | 503 => return Err(FlightError::Blocked(status)),
76        _ if status >= 400 => return Err(FlightError::HttpStatus(status)),
77        _ => {}
78    }
79
80    response.text().await.map_err(error::from_http_error)
81}