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