Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions src/integration-tests/src/storage/read_gzip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,31 @@ pub async fn test(bucket: &storage::model::Bucket) -> anyhow::Result<()> {
let got = super::read_all(response).await?;
assert_eq!(got, compressed);

tracing::info!("Reading decompressed object");
let response = client
.read_object(&object.bucket, &object.name)
.set_generation(object.generation)
.with_automatic_decompression(true)
.send()
.await?;
let highlights = response.object();
tracing::info!("Decompressed object read: {:?}", highlights);
assert_eq!(highlights.content_encoding, "gzip", "{highlights:?}");
assert_eq!(highlights.content_type, "text/plain", "{highlights:?}");
assert_eq!(highlights.size as usize, compressed.len(), "{highlights:?}");
assert_eq!(highlights.generation, object.generation, "{highlights:?}");
assert_eq!(
highlights.metageneration, object.metageneration,
"{highlights:?}"
);
assert_eq!(highlights.checksums, object.checksums, "{highlights:?}");
assert_eq!(
highlights.storage_class, object.storage_class,
"{highlights:?}"
);
let got = super::read_all(response).await?;
assert_eq!(String::from_utf8(got), Ok(CONTENT.to_string()));

tracing::info!("Reading compressed object head");
let response = client
.read_object(&object.bucket, &object.name)
Expand Down
2 changes: 1 addition & 1 deletion src/storage/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

[package]
name = "google-cloud-storage"
version = "1.1.0"
version = "1.2.0"
description = "Google Cloud Client Libraries for Rust - Storage"
# Inherit other attributes from the workspace.
edition.workspace = true
Expand Down
63 changes: 60 additions & 3 deletions src/storage/src/storage/read_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,33 @@ where
self
}

/// Enables automatic decompression.
///
/// The Cloud Storage service [automatically decompresses] objects
/// with `content_encoding == "gzip"` during reads. The client library
/// disables this behavior by default, as it is not possible to
/// perform ranged reads or to resume interrupted downloads if automatic
/// decompression is enabled.
///
/// Use this option to enable automatic decompression.
///
/// # Example
/// ```
/// # use google_cloud_storage::client::Storage;
/// # async fn sample(client: &Storage) -> anyhow::Result<()> {
/// let response = client
/// .read_object("projects/_/buckets/my-bucket", "my-object")
/// .with_automatic_decompression(true)
/// .send()
/// .await?;
/// println!("response details={response:?}");
/// # Ok(()) }
/// ```
pub fn with_automatic_decompression(mut self, v: bool) -> Self {
self.options.automatic_decompression = v;
self
}

/// Sends the request.
pub async fn send(self) -> Result<ReadObjectResponse> {
self.stub.read_object(self.request, self.options).await
Expand Down Expand Up @@ -429,16 +456,21 @@ impl Reader {
.header(
"x-goog-api-client",
reqwest::header::HeaderValue::from_static(&self::info::X_GOOG_API_CLIENT_HEADER),
)
);

let builder = if self.options.automatic_decompression {
builder
} else {
// Disable decompressive transcoding: https://cloud.google.com/storage/docs/transcoding
//
// The default is to decompress objects that have `contentEncoding == "gzip"`. This header
// tells Cloud Storage to disable automatic decompression. It has no effect on objects
// with a different `contentEncoding` value.
.header(
builder.header(
"accept-encoding",
reqwest::header::HeaderValue::from_static("gzip"),
);
)
};

// Add the optional query parameters.
let builder = if self.request.generation != 0 {
Expand Down Expand Up @@ -997,6 +1029,31 @@ mod tests {
Ok(())
}

#[tokio::test]
async fn read_object_automatic_decompression_headers() -> Result {
// The API takes the unencoded byte array.
let inner = test_inner_client(test_builder());
let stub = crate::storage::transport::Storage::new(inner.clone());
let builder = ReadObject::new(
stub,
"projects/_/buckets/bucket",
"object",
inner.options.clone(),
)
.with_automatic_decompression(true);
let request = http_request_builder(inner, builder).await?.build()?;

assert_eq!(request.method(), reqwest::Method::GET);
assert_eq!(
request.url().as_str(),
"http://private.googleapis.com/storage/v1/b/bucket/o/object?alt=media"
);

let headers = request.headers();
assert!(headers.get("accept-encoding").is_none(), "{request:?}");
Ok(())
}

#[tokio::test]
async fn read_object_encryption_headers() -> Result {
// Make a 32-byte key.
Expand Down
2 changes: 2 additions & 0 deletions src/storage/src/storage/request_options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ pub struct RequestOptions {
pub(crate) resumable_upload_buffer_size: usize,
pub(crate) idempotency: Option<bool>,
pub(crate) checksum: Checksum,
pub(crate) automatic_decompression: bool,
}

const MIB: usize = 1024 * 1024_usize;
Expand All @@ -61,6 +62,7 @@ impl RequestOptions {
crc32c: Some(Crc32c::default()),
md5_hash: None,
},
automatic_decompression: false,
}
}
}
Loading