This is an automated email from the ASF dual-hosted git repository.

fanningpj pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/pekko-connectors.git


The following commit(s) were added to refs/heads/main by this push:
     new ae0294e30 S3: Fix multipart upload signature mismatch with AWS SDK v2 
(#1544)
ae0294e30 is described below

commit ae0294e3072d14b6396aca4108653b060674fe31
Author: PJ Fanning <[email protected]>
AuthorDate: Thu Apr 2 10:16:13 2026 +0200

    S3: Fix multipart upload signature mismatch with AWS SDK v2 (#1544)
    
    * Initial plan
    
    * Fix UploadPart signature mismatch: add Content-Type and x-amz-trailer to 
allowlist; add Content-Length and Content-MD5 to InitiateMultipartUpload
    
    Agent-Logs-Url: 
https://github.com/pjfanning/incubator-pekko-connectors/sessions/73f72669-0f29-44d1-a6ea-f3b21f7b0704
    
    Co-authored-by: pjfanning <[email protected]>
    
    ---------
    
    Co-authored-by: copilot-swe-agent[bot] 
<[email protected]>
    Co-authored-by: pjfanning <[email protected]>
---
 s3/src/main/resources/reference.conf               |  4 +++
 .../pekko/stream/connectors/s3/S3HeadersSpec.scala | 32 ++++++++++++++++++++++
 2 files changed, 36 insertions(+)

diff --git a/s3/src/main/resources/reference.conf 
b/s3/src/main/resources/reference.conf
index 4521e6475..c095d11ed 100644
--- a/s3/src/main/resources/reference.conf
+++ b/s3/src/main/resources/reference.conf
@@ -207,6 +207,8 @@ pekko.connectors.s3 {
       "Content-Disposition",
       "Content-Encoding",
       "Content-Language",
+      "Content-Length",
+      "Content-MD5",
       "Content-Type",
       "Expires",
       "x-amz-grant-full-control",
@@ -236,12 +238,14 @@ pekko.connectors.s3 {
       "Host",
       "Content-Length",
       "Content-MD5",
+      "Content-Type",
       "x-amz-sdk-checksum-algorithm",
       "x-amz-checksum-crc32",
       "x-amz-checksum-crc32c",
       "x-amz-checksum-crc64nvme",
       "x-amz-checksum-sha1",
       "x-amz-checksum-sha256",
+      "x-amz-trailer",
       "x-amz-server-side-encryption-customer-algorithm",
       "x-amz-server-side-encryption-customer-key",
       "x-amz-server-side-encryption-customer-key-MD5",
diff --git 
a/s3/src/test/scala/org/apache/pekko/stream/connectors/s3/S3HeadersSpec.scala 
b/s3/src/test/scala/org/apache/pekko/stream/connectors/s3/S3HeadersSpec.scala
index a5e7cec31..421d7b532 100644
--- 
a/s3/src/test/scala/org/apache/pekko/stream/connectors/s3/S3HeadersSpec.scala
+++ 
b/s3/src/test/scala/org/apache/pekko/stream/connectors/s3/S3HeadersSpec.scala
@@ -25,6 +25,12 @@ import org.scalatest.matchers.should.Matchers
 import pekko.stream.connectors.s3.impl._
 
 class S3HeadersSpec extends AnyFlatSpecLike with Matchers {
+
+  private lazy val defaultSettings: S3Settings = {
+    val defaultConfig = ConfigFactory.load()
+    S3Settings.apply(defaultConfig.getConfig("pekko.connectors.s3"))
+  }
+
   it should "filter headers based on what's allowed" in {
     val testOverrideConfig = ConfigFactory.parseString("""
       | pekko.connectors.s3 {
@@ -85,6 +91,32 @@ class S3HeadersSpec extends AnyFlatSpecLike with Matchers {
 
   }
 
+  it should "allow Content-Type header for UploadPart (fix for signature 
mismatch with AWS SDK v2)" in {
+    val header = S3Headers().withCustomHeaders(Map("Content-Type" -> 
"application/octet-stream"))
+    val result = header.headersFor(UploadPart)(defaultSettings)
+    result.map(_.name()) should contain("Content-Type")
+  }
+
+  it should "allow x-amz-trailer header for UploadPart" in {
+    val header = S3Headers().withCustomHeaders(Map("x-amz-trailer" -> 
"x-amz-checksum-sha256"))
+    val result = header.headersFor(UploadPart)(defaultSettings)
+    result.map(_.name()) should contain("x-amz-trailer")
+  }
+
+  it should "allow Content-Length and Content-MD5 headers for 
InitiateMultipartUpload" in {
+    val header = S3Headers().withCustomHeaders(Map("Content-Length" -> "0", 
"Content-MD5" -> "abc123"))
+    val result = header.headersFor(InitiateMultipartUpload)(defaultSettings)
+    val resultNames = result.map(_.name())
+    resultNames should contain("Content-Length")
+    resultNames should contain("Content-MD5")
+  }
+
+  it should "filter headers not in the allowlist for UploadPart" in {
+    val header = S3Headers().withCustomHeaders(Map("x-amz-acl" -> 
"public-read"))
+    val result = header.headersFor(UploadPart)(defaultSettings)
+    result.map(_.name()) should not contain "x-amz-acl"
+  }
+
   it should "contain all S3Request types" in {
     val actualSet = S3Request.allRequests
 


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to