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}