s3: keep a signed content-length in the canonical request (#11849)

Co-authored-by: Devin <devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Chris LuandDevin authored and GitHub committed 2026-10-19 18:32:44 -07:00
1 parent 0fb6c0e77d
commit 8b09c96cec
2 files changed
+22

No files matched your search

+6
View File
@@ -959,6 +959,12 @@ func extractSignedHeaders(signedHeaders []string, r *http.Request, externalHost
extractedSignedHeaders[header] = []string{hostHeaderValue}
continue
}
// The Go server strips Content-Length into r.ContentLength; clients do
// sign it, so rebuild the value the same way the client saw it.
if strings.EqualFold(header, "content-length") && r.ContentLength >= 0 {
extractedSignedHeaders[header] = []string{strconv.FormatInt(r.ContentLength, 10)}
continue
}
// For all other headers we need to find them in the HTTP headers and copy them over.
// We skip non-existent headers to be compatible with AWS signatures.
if values, ok := reqHeaders[http.CanonicalHeaderKey(header)]; ok {
+16
View File
@@ -681,6 +681,22 @@ func TestExtractSignedHeadersCase(t *testing.T) {
}
}
// A client may sign content-length even when the request carries no body
// (aws-cli's checksum HEAD does). The Go server strips the header into
// r.ContentLength, so extraction rebuilds it rather than dropping it.
func TestExtractSignedHeadersContentLength(t *testing.T) {
r, _ := http.NewRequest("HEAD", "http://bucket.domain.com/bucket/key", nil)
r.Host = "bucket.domain.com"
r.ContentLength = 0
extracted, errCode := extractSignedHeaders([]string{"host", "content-length"}, r, "")
if errCode != s3err.ErrNone {
t.Fatalf("extractSignedHeaders failed: %v", errCode)
}
if got := getCanonicalHeaders(extracted); got != "content-length:0\nhost:bucket.domain.com\n" {
t.Fatalf("unexpected canonical headers %q", got)
}
}
// A router that internally rewrites the request path (the Iceberg catalog
// collapses a doubled /v1 prefix) must still verify the signature against
// the path the client signed, which is kept in the request context.