From 6e53ef0c22d4b1716dac0248cc91ff91f1c1ecb4 Mon Sep 17 00:00:00 2001 From: CosminPerRam Date: Sat, 16 Mar 2024 17:57:07 +0200 Subject: [PATCH] feat(http): add per-request headers option (#196) * http: Add per-request headers option * http: Improve tests --------- Co-authored-by: Douile --- crates/lib/src/games/eco/protocol.rs | 2 +- crates/lib/src/http.rs | 56 +++++++++++++++++++++------- 2 files changed, 43 insertions(+), 15 deletions(-) diff --git a/crates/lib/src/games/eco/protocol.rs b/crates/lib/src/games/eco/protocol.rs index 9e549f8..d55aad3 100644 --- a/crates/lib/src/games/eco/protocol.rs +++ b/crates/lib/src/games/eco/protocol.rs @@ -31,7 +31,7 @@ pub fn query_with_timeout_and_extra_settings( extra_settings.unwrap_or_default().into(), )?; - let response = client.get_json::("/frontpage")?; + let response = client.get_json::("/frontpage", None)?; Ok(response.into()) } diff --git a/crates/lib/src/http.rs b/crates/lib/src/http.rs index ae9ffd0..b14a3e5 100644 --- a/crates/lib/src/http.rs +++ b/crates/lib/src/http.rs @@ -37,6 +37,9 @@ pub struct HttpClient { headers: Vec<(String, String)>, } +/// HttpHeaders for use with a single request. +pub type HttpHeaders<'a> = Option<&'a [(&'a str, &'a str)]>; + /// HTTP Protocols. /// /// Note: if the `tls` feature is disabled this will only contain Http. @@ -65,10 +68,10 @@ impl HttpProtocol { /// /// # Can be created using builder functions: /// ```ignore, We cannot test private functionality -/// use gamedig::http::{HttpSettings, Protocol}; +/// use gamedig::http::{HttpSettings, HttpProtocol}; /// /// let _ = HttpSettings::default() -/// .protocol(Protocol::Http) +/// .protocol(HttpProtocol::Http) /// .hostname(String::from("test.com")) /// .header(String::from("Authorization"), String::from("Bearer Token")); /// ``` @@ -230,21 +233,29 @@ impl HttpClient { } /// Send a HTTP GET request and return the response data as a buffer. - pub fn get(&mut self, path: &str) -> GDResult> { self.request("GET", path) } + pub fn get(&mut self, path: &str, headers: HttpHeaders) -> GDResult> { self.request("GET", path, headers) } /// Send a HTTP GET request and parse the JSON resonse. - pub fn get_json(&mut self, path: &str) -> GDResult { self.request_json("GET", path) } + pub fn get_json(&mut self, path: &str, headers: HttpHeaders) -> GDResult { + self.request_json("GET", path, headers) + } /// Send a HTTP Post request with JSON data and parse a JSON response. - pub fn post_json(&mut self, path: &str, data: S) -> GDResult { - self.request_with_json_data("POST", path, data) + pub fn post_json( + &mut self, + path: &str, + headers: HttpHeaders, + data: S, + ) -> GDResult { + self.request_with_json_data("POST", path, headers, data) } // NOTE: More methods can be added here as required using the request_json or // request_with_json methods + /// Internal request method, makes a request with an arbitrary HTTP method. #[inline] - fn request(&mut self, method: &str, path: &str) -> GDResult> { + fn request(&mut self, method: &str, path: &str, headers: HttpHeaders) -> GDResult> { // Append the path to the pre-parsed URL and create a request object. self.address.set_path(path); let mut request = self.client.request_url(method, &self.address); @@ -254,6 +265,12 @@ impl HttpClient { request = request.set(key, value); } + if let Some(headers) = headers { + for (key, value) in headers { + request = request.set(key, value); + } + } + // Send the request. let http_response = request.call().map_err(|e| PacketSend.context(e))?; @@ -279,7 +296,7 @@ impl HttpClient { /// Send a HTTP request without any data and parse the JSON response. #[inline] - fn request_json(&mut self, method: &str, path: &str) -> GDResult { + fn request_json(&mut self, method: &str, path: &str, headers: HttpHeaders) -> GDResult { // Append the path to the pre-parsed URL and create a request object. self.address.set_path(path); let mut request = self.client.request_url(method, &self.address); @@ -288,6 +305,11 @@ impl HttpClient { for (key, value) in self.headers.iter() { request = request.set(key, value); } + if let Some(headers) = headers { + for (key, value) in headers { + request = request.set(key, value); + } + } // Send the request and parse the response as JSON. request @@ -303,6 +325,7 @@ impl HttpClient { &mut self, method: &str, path: &str, + headers: HttpHeaders, data: S, ) -> GDResult { self.address.set_path(path); @@ -311,6 +334,11 @@ impl HttpClient { for (key, value) in self.headers.iter() { request = request.set(key, value); } + if let Some(headers) = headers { + for (key, value) in headers { + request = request.set(key, value); + } + } request .send_json(data) @@ -384,7 +412,7 @@ mod tests { let mut client = HttpClient::new(&address, &None, settings).unwrap(); - let response: serde_json::Value = client.get_json("/events").unwrap(); + let response: serde_json::Value = client.get_json("/events", None).unwrap(); println!("{:?}", response); } @@ -402,7 +430,7 @@ mod tests { let mut client = HttpClient::new(&address, &None, settings).unwrap(); - let response: serde_json::Value = client.get_json("/get").unwrap(); + let response: serde_json::Value = client.get_json("/get", None).unwrap(); println!("{:?}", response); } @@ -418,7 +446,7 @@ mod tests { let mut client = HttpClient::new(&address, &None, settings).unwrap(); - let response = client.get("/").unwrap(); + let response = client.get("/", None).unwrap(); println!("{:?}", std::str::from_utf8(&response)); } @@ -428,7 +456,7 @@ mod tests { fn http_get_from_url() { let mut client = HttpClient::from_url("http://postman-echo.com/path-is-ignored", &None, None).unwrap(); - let response: serde_json::Value = client.get_json("/get").unwrap(); + let response: serde_json::Value = client.get_json("/get", None).unwrap(); println!("{:?}", response); } @@ -436,11 +464,11 @@ mod tests { #[test] #[ignore = "HTTP requests won't work without internet"] fn http_get_from_url_parsed() { - let url = Url::parse("http://postman-echo.com:443/path-is-ignored").unwrap(); + let url = Url::parse("http://postman-echo.com/path-is-ignored").unwrap(); let mut client = HttpClient::from_url(url, &None, None).unwrap(); - let response: serde_json::Value = client.get_json("/get").unwrap(); + let response: serde_json::Value = client.get_json("/get", None).unwrap(); println!("{:?}", response); }