From 0487b3bc0993adfb604b4b7a4ea9b50a8f7ffac5 Mon Sep 17 00:00:00 2001 From: Joseph Koshakow Date: Thu, 20 Mar 2025 17:36:35 -0400 Subject: [PATCH 1/4] Add content length to PUT GCP multipart complete This commit fixes the GCP mulipart complete implementation by adding the Content-Length header to the PUT XML requests that happens when the completed parts are empty. GCP is strict about setting the Content-Length header on requests with empty bodies, so previously this would result in a 411 error. --- src/gcp/client.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gcp/client.rs b/src/gcp/client.rs index 1cc72964..db4aa0a2 100644 --- a/src/gcp/client.rs +++ b/src/gcp/client.rs @@ -517,6 +517,7 @@ impl GoogleCloudStorageClient { // GCS doesn't allow empty multipart uploads let result = self .request(Method::PUT, path) + .header(&CONTENT_LENGTH, "0") .idempotent(true) .do_put() .await?; From 7f70f53b1e7bd995517268b4165a2bda2296a439 Mon Sep 17 00:00:00 2001 From: Joseph Koshakow Date: Fri, 21 Mar 2025 08:00:33 -0400 Subject: [PATCH 2/4] Use put method --- src/gcp/client.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/gcp/client.rs b/src/gcp/client.rs index db4aa0a2..e97f95c5 100644 --- a/src/gcp/client.rs +++ b/src/gcp/client.rs @@ -397,8 +397,13 @@ impl GoogleCloudStorageClient { extensions, } = opts; - let builder = self - .request(Method::PUT, path) + let builder = self.request(Method::PUT, path); + let builder = if payload.content_length() == 0 { + builder.header(&CONTENT_LENGTH, "0") + } else { + builder + }; + let builder = builder .with_payload(payload) .with_attributes(attributes) .with_extensions(extensions); @@ -516,10 +521,7 @@ impl GoogleCloudStorageClient { if completed_parts.is_empty() { // GCS doesn't allow empty multipart uploads let result = self - .request(Method::PUT, path) - .header(&CONTENT_LENGTH, "0") - .idempotent(true) - .do_put() + .put(path, PutPayload::new(), Default::default()) .await?; self.multipart_cleanup(path, multipart_id).await?; return Ok(result); From a1b0e3d45ca1e282d81fd8b6e3e526774b6104c4 Mon Sep 17 00:00:00 2001 From: Joseph Koshakow Date: Fri, 21 Mar 2025 08:41:09 -0400 Subject: [PATCH 3/4] Revert header change in put --- src/gcp/client.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/gcp/client.rs b/src/gcp/client.rs index e97f95c5..88910831 100644 --- a/src/gcp/client.rs +++ b/src/gcp/client.rs @@ -397,13 +397,8 @@ impl GoogleCloudStorageClient { extensions, } = opts; - let builder = self.request(Method::PUT, path); - let builder = if payload.content_length() == 0 { - builder.header(&CONTENT_LENGTH, "0") - } else { - builder - }; - let builder = builder + let builder = self + .request(Method::PUT, path) .with_payload(payload) .with_attributes(attributes) .with_extensions(extensions); From cf9edd12f711b7692c46286e096db670d706d15b Mon Sep 17 00:00:00 2001 From: Joseph Koshakow Date: Sat, 22 Mar 2025 15:20:49 -0400 Subject: [PATCH 4/4] fixup --- src/gcp/client.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gcp/client.rs b/src/gcp/client.rs index 88910831..9a54dd7d 100644 --- a/src/gcp/client.rs +++ b/src/gcp/client.rs @@ -514,11 +514,11 @@ impl GoogleCloudStorageClient { completed_parts: Vec, ) -> Result { if completed_parts.is_empty() { - // GCS doesn't allow empty multipart uploads + // GCS doesn't allow empty multipart uploads, so fallback to regular upload. + self.multipart_cleanup(path, multipart_id).await?; let result = self .put(path, PutPayload::new(), Default::default()) .await?; - self.multipart_cleanup(path, multipart_id).await?; return Ok(result); }