Skip to content

Commit ef5d996

Browse files
Supporting cookies (#82)
1 parent da69bdb commit ef5d996

File tree

3 files changed

+167
-10
lines changed

3 files changed

+167
-10
lines changed

Cargo.lock

Lines changed: 76 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ clap = { version = "4.4.13", features = ["derive"] }
1010
dirs = "5.0.1"
1111
reqwest = { version = "0.12.23", default-features = false, features = [
1212
"blocking",
13+
"cookies",
1314
"json",
1415
"multipart",
1516
"native-tls",
@@ -33,6 +34,7 @@ chrono = "0.4"
3334
tokio = { version = "1.0", features = ["full"] }
3435
hyper = { version = "1.0", features = ["full"] }
3536
hyper-util = { version = "0.1", features = ["full"] }
37+
http = "1"
3638
http-body-util = "0.1"
3739
url = "2.5"
3840
open = "5.0"

src/utils/api.rs

Lines changed: 89 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,100 @@ fn get_source() -> String {
1919
std::env::var("CORGEA_SOURCE").unwrap_or_else(|_| "cli".to_string())
2020
}
2121

22-
pub fn http_client() -> reqwest::blocking::Client {
23-
let mut builder =
24-
reqwest::blocking::Client::builder().timeout(std::time::Duration::from_secs(5 * 30));
22+
static COOKIE_JAR: std::sync::LazyLock<std::sync::Arc<reqwest::cookie::Jar>> =
23+
std::sync::LazyLock::new(|| std::sync::Arc::new(reqwest::cookie::Jar::default()));
2524

26-
if let Ok(https_proxy) = std::env::var("https_proxy") {
27-
debug(&format!("https_proxy detected: {}", https_proxy));
25+
static SHARED_CLIENT: std::sync::LazyLock<reqwest::blocking::Client> =
26+
std::sync::LazyLock::new(|| {
27+
let mut builder = reqwest::blocking::Client::builder()
28+
.timeout(std::time::Duration::from_secs(5 * 30))
29+
.cookie_provider(COOKIE_JAR.clone());
2830

29-
if std::env::var("CORGEA_ACCEPT_CERT").is_ok() {
30-
debug(&format!("Skipping CA cert validation"));
31-
builder = builder.danger_accept_invalid_certs(true);
31+
if let Ok(https_proxy) = std::env::var("https_proxy") {
32+
debug(&format!("https_proxy detected: {}", https_proxy));
33+
34+
if std::env::var("CORGEA_ACCEPT_CERT").is_ok() {
35+
debug(&format!("Skipping CA cert validation"));
36+
builder = builder.danger_accept_invalid_certs(true);
37+
}
3238
}
39+
40+
builder.build().expect("Failed to build http client")
41+
});
42+
43+
pub struct HttpClient {
44+
inner: reqwest::blocking::Client,
45+
}
46+
47+
pub struct DebugRequestBuilder {
48+
client: reqwest::blocking::Client,
49+
inner: reqwest::blocking::RequestBuilder,
50+
}
51+
52+
impl HttpClient {
53+
pub fn get<U: reqwest::IntoUrl>(&self, url: U) -> DebugRequestBuilder {
54+
DebugRequestBuilder { client: self.inner.clone(), inner: self.inner.get(url) }
3355
}
3456

35-
builder.build().expect("Failed to build http client")
57+
pub fn post<U: reqwest::IntoUrl>(&self, url: U) -> DebugRequestBuilder {
58+
DebugRequestBuilder { client: self.inner.clone(), inner: self.inner.post(url) }
59+
}
60+
61+
pub fn patch<U: reqwest::IntoUrl>(&self, url: U) -> DebugRequestBuilder {
62+
DebugRequestBuilder { client: self.inner.clone(), inner: self.inner.patch(url) }
63+
}
64+
}
65+
66+
impl DebugRequestBuilder {
67+
pub fn header<K, V>(self, key: K, value: V) -> Self
68+
where
69+
reqwest::header::HeaderName: TryFrom<K>,
70+
<reqwest::header::HeaderName as TryFrom<K>>::Error: Into<http::Error>,
71+
reqwest::header::HeaderValue: TryFrom<V>,
72+
<reqwest::header::HeaderValue as TryFrom<V>>::Error: Into<http::Error>,
73+
{
74+
Self { inner: self.inner.header(key, value), client: self.client }
75+
}
76+
77+
pub fn headers(self, headers: reqwest::header::HeaderMap) -> Self {
78+
Self { inner: self.inner.headers(headers), client: self.client }
79+
}
80+
81+
pub fn query<T: Serialize + ?Sized>(self, query: &T) -> Self {
82+
Self { inner: self.inner.query(query), client: self.client }
83+
}
84+
85+
pub fn multipart(self, form: reqwest::blocking::multipart::Form) -> Self {
86+
Self { inner: self.inner.multipart(form), client: self.client }
87+
}
88+
89+
pub fn body<T: Into<reqwest::blocking::Body>>(self, body: T) -> Self {
90+
Self { inner: self.inner.body(body), client: self.client }
91+
}
92+
93+
pub fn send(self) -> reqwest::Result<reqwest::blocking::Response> {
94+
use reqwest::cookie::CookieStore;
95+
96+
let request = self.inner.build()?;
97+
98+
debug(&format!("→ {} {}", request.method(), request.url()));
99+
debug(&format!(" Request headers: {:?}", request.headers()));
100+
match COOKIE_JAR.cookies(request.url()) {
101+
Some(cookies) => debug(&format!(" Cookie: {}", cookies.to_str().unwrap_or("<binary>"))),
102+
None => debug(" Cookie: (none in jar for this URL)"),
103+
}
104+
105+
let response = self.client.execute(request)?;
106+
107+
debug(&format!("← {} {}", response.status(), response.url()));
108+
debug(&format!(" Response headers: {:?}", response.headers()));
109+
110+
Ok(response)
111+
}
112+
}
113+
114+
pub fn http_client() -> HttpClient {
115+
HttpClient { inner: SHARED_CLIENT.clone() }
36116
}
37117

38118
fn check_for_warnings(headers: &HeaderMap, status: StatusCode) {

0 commit comments

Comments
 (0)