forked from proxmox-mirrors/proxmox
http: teach the Client how to decode deflate content
The Backup Server can compress the content using deflate so we teach the client how to decode it. If a request is sent with the `Accept-Encoding` [2] header set to `deflate`, and the response's `Content-Encoding` [1] header is equal to `deflate` we wrap the Body stream with a stream that can decode `zlib` on the run. Note that from the `Accept-Encoding` docs [2], the `deflate` encoding is actually `zlib`. This can be also tested against http://eu.httpbin.org/#/Response_formats/get_deflate by adding the following test: ```rust #[tokio::test] async fn test_client() { let client = Client::new(); let headers = HashMap::from([( hyper::header::ACCEPT_ENCODING.to_string(), "deflate".to_string(), )]); let response = client .get_string("https://eu.httpbin.org/deflate", Some(&headers)) .await; assert!(response.is_ok()); } ``` at `proxmox-http/src/client/simple.rs` and running ``` cargo test --features=client,client-trait ``` [1] https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding [2] https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding Suggested-by: Lukas Wagner <l.wagner@proxmox.com> Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com> Reviewed-by: Max Carrara <m.carrara@proxmox.com> Tested-by: Max Carrara <m.carrara@proxmox.com>
This commit is contained in:
parent
8b8957b5ba
commit
4d1c4ec829
@ -26,6 +26,11 @@ proxmox-async = { workspace = true, optional = true }
|
||||
proxmox-sys = { workspace = true, optional = true }
|
||||
proxmox-io = { workspace = true, optional = true }
|
||||
proxmox-lang = { workspace = true, optional = true }
|
||||
proxmox-compression = { workspace = true, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { workspace = true, features = [ "macros" ] }
|
||||
flate2 = { workspace = true }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
@ -42,12 +47,14 @@ client = [
|
||||
"dep:futures",
|
||||
"dep:hyper",
|
||||
"dep:openssl",
|
||||
"dep:proxmox-compression",
|
||||
"dep:tokio",
|
||||
"dep:tokio-openssl",
|
||||
"http-helpers",
|
||||
"hyper?/client",
|
||||
"hyper?/http1",
|
||||
"hyper?/http2",
|
||||
"hyper?/stream",
|
||||
"hyper?/tcp",
|
||||
"rate-limited-stream",
|
||||
"tokio?/io-util",
|
||||
|
@ -78,7 +78,8 @@ impl Client {
|
||||
|
||||
self.add_proxy_headers(&mut request)?;
|
||||
|
||||
self.client.request(request).map_err(Error::from).await
|
||||
let encoded_response = self.client.request(request).map_err(Error::from).await?;
|
||||
decode_response(encoded_response).await
|
||||
}
|
||||
|
||||
pub async fn post(
|
||||
@ -245,3 +246,65 @@ impl crate::HttpClient<String, String> for Client {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Wraps the `Body` stream in a DeflateDecoder stream if the `Content-Encoding`
|
||||
/// header of the response is `deflate`, otherwise returns the original
|
||||
/// response.
|
||||
async fn decode_response(mut res: Response<Body>) -> Result<Response<Body>, Error> {
|
||||
let Some(content_encoding) = res.headers_mut().remove(&hyper::header::CONTENT_ENCODING) else {
|
||||
return Ok(res);
|
||||
};
|
||||
|
||||
let encodings = content_encoding.to_str()?;
|
||||
if encodings == "deflate" {
|
||||
let (parts, body) = res.into_parts();
|
||||
let decoder = proxmox_compression::DeflateDecoder::builder(body)
|
||||
.zlib(true)
|
||||
.build();
|
||||
let decoded_body = Body::wrap_stream(decoder);
|
||||
Ok(Response::from_parts(parts, decoded_body))
|
||||
} else {
|
||||
bail!("Unknown encoding format: {encodings}");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
use std::io::Write;
|
||||
|
||||
const BODY: &str = r#"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
|
||||
eiusmod tempor incididunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut
|
||||
enim aeque doleamus animo, cum corpore dolemus, fieri tamen permagna accessio potest,
|
||||
si aliquod aeternum et infinitum impendere."#;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_parse_response_deflate() {
|
||||
let encoded = encode_deflate(BODY.as_bytes()).unwrap();
|
||||
let encoded_body = Body::from(encoded);
|
||||
let encoded_response = Response::builder()
|
||||
.header(hyper::header::CONTENT_ENCODING, "deflate")
|
||||
.body(encoded_body)
|
||||
.unwrap();
|
||||
|
||||
let decoded_response = decode_response(encoded_response).await.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
Client::response_body_string(decoded_response)
|
||||
.await
|
||||
.unwrap(),
|
||||
BODY
|
||||
);
|
||||
}
|
||||
|
||||
fn encode_deflate(bytes: &[u8]) -> Result<Vec<u8>, std::io::Error> {
|
||||
use flate2::write::ZlibEncoder;
|
||||
use flate2::Compression;
|
||||
|
||||
let mut e = ZlibEncoder::new(Vec::new(), Compression::default());
|
||||
e.write_all(bytes).unwrap();
|
||||
|
||||
e.finish()
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user