all lists on lists.proxmox.com
 help / color / mirror / Atom feed
* [pbs-devel] [PATCH proxmox 1/2] http: teach the Client how to speak gzip
@ 2024-03-26 15:28 Maximiliano Sandoval
  2024-03-26 15:28 ` [pbs-devel] [PATCH proxmox 2/2] http: Teach client how to speak deflate Maximiliano Sandoval
  2024-03-27 11:44 ` [pbs-devel] [PATCH proxmox 1/2] http: teach the Client how to speak gzip Max Carrara
  0 siblings, 2 replies; 4+ messages in thread
From: Maximiliano Sandoval @ 2024-03-26 15:28 UTC (permalink / raw)
  To: pbs-devel

Proxmox VE already supports using gzip to encode and decode the body of
http requests. We teach the proxmox-http how to do the same.

This can be also tested against
http://eu.httpbin.org/#/Response_formats/get_gzip via

```rust
async fn test_client() {
    use crate::client_trait::HttpClient;
    let client = Client::new();
    let response = client.get_string("http://eu.httpbin.org/gzip", None).await.unwrap();
    assert_eq!(response, "..");
}
```

Suggested-by: Lukas Wagner <l.wagner@proxmox.com>
Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
---
 proxmox-http/Cargo.toml           |  5 +++
 proxmox-http/src/client/simple.rs | 62 +++++++++++++++++++++++++++++--
 2 files changed, 63 insertions(+), 4 deletions(-)

diff --git a/proxmox-http/Cargo.toml b/proxmox-http/Cargo.toml
index 11c6f0b0..9ec32e7f 100644
--- a/proxmox-http/Cargo.toml
+++ b/proxmox-http/Cargo.toml
@@ -21,12 +21,16 @@ tokio = { workspace = true, features = [], optional = true }
 tokio-openssl = { workspace = true, optional = true }
 ureq = { version = "2.4", features = ["native-certs"], optional = true }
 url = { workspace = true, optional = true }
+flate2 = { workspace = true, optional = true }
 
 proxmox-async = { workspace = true, optional = true }
 proxmox-sys = { workspace = true, optional = true }
 proxmox-io = { workspace = true, optional = true }
 proxmox-lang = { workspace = true, optional = true }
 
+[dev-dependencies]
+tokio = { workspace = true, features = [ "macros" ] }
+
 [features]
 default = []
 
@@ -39,6 +43,7 @@ rate-limited-stream = [
     "tokio?/time",
 ]
 client = [
+    "dep:flate2",
     "dep:futures",
     "dep:hyper",
     "dep:openssl",
diff --git a/proxmox-http/src/client/simple.rs b/proxmox-http/src/client/simple.rs
index e9910802..b33154be 100644
--- a/proxmox-http/src/client/simple.rs
+++ b/proxmox-http/src/client/simple.rs
@@ -1,9 +1,11 @@
 use anyhow::{bail, format_err, Error};
 use std::collections::HashMap;
-
+use std::io::Read;
 #[cfg(all(feature = "client-trait", feature = "proxmox-async"))]
 use std::str::FromStr;
 
+use flate2::read::GzDecoder;
+
 use futures::*;
 #[cfg(all(feature = "client-trait", feature = "proxmox-async"))]
 use http::header::HeaderName;
@@ -72,6 +74,10 @@ impl Client {
             HeaderValue::from_str(Self::DEFAULT_USER_AGENT_STRING)?
         };
 
+        request.headers_mut().insert(
+            hyper::header::ACCEPT_ENCODING,
+            HeaderValue::from_static("gzip"),
+        );
         request
             .headers_mut()
             .insert(hyper::header::USER_AGENT, user_agent);
@@ -142,11 +148,22 @@ impl Client {
     ) -> Result<Response<String>, Error> {
         match response {
             Ok(res) => {
-                let (parts, body) = res.into_parts();
+                let (mut parts, body) = res.into_parts();
+                let is_gzip_encoded = parts
+                    .headers
+                    .remove(&hyper::header::CONTENT_ENCODING)
+                    .is_some_and(|h| h == "gzip");
 
                 let buf = hyper::body::to_bytes(body).await?;
-                let new_body = String::from_utf8(buf.to_vec())
-                    .map_err(|err| format_err!("Error converting HTTP result data: {}", err))?;
+                let new_body = if is_gzip_encoded {
+                    let mut gz = GzDecoder::new(&buf[..]);
+                    let mut s = String::new();
+                    gz.read_to_string(&mut s)?;
+                    s
+                } else {
+                    String::from_utf8(buf.to_vec())
+                        .map_err(|err| format_err!("Error converting HTTP result data: {}", err))?
+                };
 
                 Ok(Response::from_parts(parts, new_body))
             }
@@ -245,3 +262,40 @@ impl crate::HttpClient<String, String> for Client {
         })
     }
 }
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    const BODY: &str = "hello world";
+
+    #[tokio::test]
+    async fn test_parse_response_plain_text() {
+        let body = Body::from(BODY);
+        let response = Response::new(body);
+        assert_eq!(Client::response_body_string(response).await.unwrap(), BODY);
+    }
+
+    #[tokio::test]
+    async fn test_parse_response_gzip() {
+        let encoded = encode_gzip(BODY.as_bytes()).unwrap();
+        let body = Body::from(encoded);
+
+        let response = Response::builder()
+            .header(hyper::header::CONTENT_ENCODING, "gzip")
+            .body(body)
+            .unwrap();
+        assert_eq!(Client::response_body_string(response).await.unwrap(), BODY);
+    }
+
+    fn encode_gzip(bytes: &[u8]) -> Result<Vec<u8>, std::io::Error> {
+        use flate2::write::GzEncoder;
+        use flate2::Compression;
+        use std::io::Write;
+
+        let mut e = GzEncoder::new(Vec::new(), Compression::default());
+        e.write_all(bytes).unwrap();
+
+        e.finish()
+    }
+}
-- 
2.39.2





^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2024-03-27 11:44 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-03-26 15:28 [pbs-devel] [PATCH proxmox 1/2] http: teach the Client how to speak gzip Maximiliano Sandoval
2024-03-26 15:28 ` [pbs-devel] [PATCH proxmox 2/2] http: Teach client how to speak deflate Maximiliano Sandoval
2024-03-27  8:59   ` Lukas Wagner
2024-03-27 11:44 ` [pbs-devel] [PATCH proxmox 1/2] http: teach the Client how to speak gzip Max Carrara

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal