This is an automated email from the ASF dual-hosted git repository.
xuanwo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/opendal.git
The following commit(s) were added to refs/heads/main by this push:
new b71870c28 fix(gcs): headers missing in XML multipart API and incorrect
x-goog-acl header values in XML API (#6275)
b71870c28 is described below
commit b71870c28957c2ad75d9d2427023a343740351e6
Author: William Linna <[email protected]>
AuthorDate: Tue Jun 10 14:18:36 2025 +0700
fix(gcs): headers missing in XML multipart API and incorrect x-goog-acl
header values in XML API (#6275)
* gcs: Add headers in multipart requests (xml api)
* gcs: Write predefined acl in correct format when using XML API
Fixes https://github.com/apache/opendal/issues/5612
* Run cargo fmt
* Fix eprintln with log::warn
---
core/src/services/gcs/core.rs | 63 ++++++++++++++++++++++++++++++++++++++---
core/src/services/gcs/writer.rs | 2 +-
2 files changed, 60 insertions(+), 5 deletions(-)
diff --git a/core/src/services/gcs/core.rs b/core/src/services/gcs/core.rs
index df666671c..34eafda8c 100644
--- a/core/src/services/gcs/core.rs
+++ b/core/src/services/gcs/core.rs
@@ -28,6 +28,8 @@ use backon::Retryable;
use bytes::Buf;
use bytes::Bytes;
use constants::*;
+use http::header::CACHE_CONTROL;
+use http::header::CONTENT_DISPOSITION;
use http::header::CONTENT_ENCODING;
use http::header::CONTENT_LENGTH;
use http::header::CONTENT_TYPE;
@@ -370,7 +372,11 @@ impl GcsCore {
}
if let Some(acl) = &self.predefined_acl {
- req = req.header(X_GOOG_ACL, acl);
+ if let Some(predefined_acl_in_xml_spec) =
predefined_acl_to_xml_header(acl) {
+ req = req.header(X_GOOG_ACL, predefined_acl_in_xml_spec);
+ } else {
+ log::warn!("Unrecognized predefined_acl. Ignoring");
+ }
}
if let Some(storage_class) = &self.default_storage_class {
@@ -561,14 +567,50 @@ impl GcsCore {
self.send(req).await
}
- pub async fn gcs_initiate_multipart_upload(&self, path: &str) ->
Result<Response<Buffer>> {
+ pub async fn gcs_initiate_multipart_upload(
+ &self,
+ path: &str,
+ op: &OpWrite,
+ ) -> Result<Response<Buffer>> {
let p = build_abs_path(&self.root, path);
let url = format!("{}/{}/{}?uploads", self.endpoint, self.bucket, p);
- let mut req = Request::post(&url)
+ let mut builder = Request::post(&url)
.header(CONTENT_LENGTH, 0)
- .extension(Operation::Write)
+ .extension(Operation::Write);
+
+ if let Some(header_val) = op.content_disposition() {
+ builder = builder.header(CONTENT_DISPOSITION, header_val);
+ }
+
+ if let Some(header_val) = op.content_encoding() {
+ builder = builder.header(CONTENT_ENCODING, header_val);
+ }
+
+ if let Some(header_val) = op.content_type() {
+ builder = builder.header(CONTENT_TYPE, header_val);
+ }
+
+ if let Some(header_val) = op.cache_control() {
+ builder = builder.header(CACHE_CONTROL, header_val);
+ }
+
+ if let Some(metadata) = op.user_metadata() {
+ for (k, v) in metadata {
+ builder = builder.header(&format!("x-goog-meta-{k}"), v);
+ }
+ }
+
+ if let Some(acl) = self.predefined_acl.as_ref() {
+ if let Some(predefined_acl_in_xml_spec) =
predefined_acl_to_xml_header(acl) {
+ builder = builder.header(X_GOOG_ACL,
predefined_acl_in_xml_spec);
+ } else {
+ log::warn!("Unrecognized predefined_acl. Ignoring");
+ }
+ }
+
+ let mut req = builder
.body(Buffer::new())
.map_err(new_request_build_error)?;
@@ -709,6 +751,19 @@ impl GcsCore {
}
}
+// https://cloud.google.com/storage/docs/xml-api/reference-headers#xgoogacl
+fn predefined_acl_to_xml_header(predefined_acl: &str) -> Option<&'static str> {
+ match predefined_acl {
+ "projectPrivate" => Some("project-private"),
+ "private" => Some("private"),
+ "bucketOwnerRead" => Some("bucket-owner-read"),
+ "bucketOwnerFullControl" => Some("bucket-owner-full-control"),
+ "publicRead" => Some("public-read"),
+ "authenticatedRead" => Some("authenticated-read"),
+ _ => None,
+ }
+}
+
#[derive(Debug, Serialize)]
#[serde(default, rename_all = "camelCase")]
pub struct InsertRequestMetadata<'a> {
diff --git a/core/src/services/gcs/writer.rs b/core/src/services/gcs/writer.rs
index c2b763c20..377762fdb 100644
--- a/core/src/services/gcs/writer.rs
+++ b/core/src/services/gcs/writer.rs
@@ -74,7 +74,7 @@ impl oio::MultipartWrite for GcsWriter {
async fn initiate_part(&self) -> Result<String> {
let resp = self
.core
- .gcs_initiate_multipart_upload(&percent_encode_path(&self.path))
+ .gcs_initiate_multipart_upload(&percent_encode_path(&self.path),
&self.op)
.await?;
if !resp.status().is_success() {