Skip to content

Commit 3eb5fdf

Browse files
committed
Fix ranged GET signing failure with Cloudflare R2
GetObjectRange was missing from the skip-list in headers(), so it fell through to the catch-all arm that adds content-length and content-type. For GET requests these headers are empty/meaningless, but they still get included in the canonical request signature. Cloudflare R2 rejects the resulting signature (SignatureDoesNotMatch), while AWS S3 happens to tolerate it. Instead of maintaining a skip-list of commands that should not include body headers, this switches to checking http_verb(): only PUT, POST and DELETE may carry a body, so only those get content-length/content-type. This also prevents the same bug from happening if new GET-like commands are added in the future.
1 parent 3a9314f commit 3eb5fdf

File tree

1 file changed

+12
-11
lines changed

1 file changed

+12
-11
lines changed

s3/src/request/request_trait.rs

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use url::Url;
1111

1212
use crate::LONG_DATETIME;
1313
use crate::bucket::Bucket;
14-
use crate::command::Command;
14+
use crate::command::{Command, HttpMethod};
1515
use crate::error::S3Error;
1616
use crate::signing;
1717
use bytes::Bytes;
@@ -700,16 +700,17 @@ pub trait Request {
700700

701701
headers.insert(HOST, host_header.parse()?);
702702

703-
match self.command() {
704-
Command::CopyObject { from } => {
705-
headers.insert(HeaderName::from_static("x-amz-copy-source"), from.parse()?);
706-
}
707-
Command::ListObjects { .. } => {}
708-
Command::ListObjectsV2 { .. } => {}
709-
Command::GetObject => {}
710-
Command::GetObjectTagging => {}
711-
Command::GetBucketLocation => {}
712-
Command::ListBuckets => {}
703+
if let Command::CopyObject { from } = self.command() {
704+
headers.insert(HeaderName::from_static("x-amz-copy-source"), from.parse()?);
705+
}
706+
707+
// Only include content-length and content-type for methods that carry
708+
// a request body. GET and HEAD requests must not have these headers in
709+
// the signed request, otherwise providers like Cloudflare R2 reject
710+
// the signature (the empty content-length value corrupts the canonical
711+
// request).
712+
match self.command().http_verb() {
713+
HttpMethod::Get | HttpMethod::Head => {}
713714
_ => {
714715
headers.insert(
715716
CONTENT_LENGTH,

0 commit comments

Comments
 (0)