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
10const BASE_URL: &str = "https://www.google.com/travel/flights";
11
12pub struct FetchOptions {
13    pub proxy: Option<String>,
14    pub timeout: u64,
15}
16
17impl Default for FetchOptions {
18    fn default() -> Self {
19        Self {
20            proxy: None,
21            timeout: 30,
22        }
23    }
24}
25
26pub async fn fetch_html(
27    params: &[(String, String)],
28    options: &FetchOptions,
29) -> Result<String, FlightError> {
30    let jar = Arc::new(Jar::default());
31    let url: wreq::Uri = "https://www.google.com".parse().unwrap();
32    jar.add(
33        "SOCS=CAESEwgDEgk2MjA5NDM1NjAaAmVuIAEaBgiA_Le-Bg",
34        &url,
35    );
36    jar.add("CONSENT=PENDING+987", &url);
37
38    let mut builder = Client::builder()
39        .emulation(Emulation::Chrome137)
40        .cookie_provider(jar)
41        .timeout(Duration::from_secs(options.timeout));
42
43    if let Some(ref proxy) = options.proxy {
44        builder = builder.proxy(
45            wreq::Proxy::all(proxy).map_err(error::from_http_error)?,
46        );
47    }
48
49    let client = builder.build().map_err(error::from_http_error)?;
50
51    let response = client
52        .get(BASE_URL)
53        .query(params)
54        .send()
55        .await
56        .map_err(error::from_http_error)?;
57
58    let status = response.status().as_u16();
59    match status {
60        200 => {}
61        429 => return Err(FlightError::RateLimited),
62        403 | 503 => return Err(FlightError::Blocked(status)),
63        _ if status >= 400 => return Err(FlightError::HttpStatus(status)),
64        _ => {}
65    }
66
67    response.text().await.map_err(error::from_http_error)
68}