Skip to content

Fix ranged GET signing failure with Cloudflare R2#452

Open
w4nderlust wants to merge 4 commits into
durch:masterfrom
w4nderlust:fix/ranged-get-signing-r2
Open

Fix ranged GET signing failure with Cloudflare R2#452
w4nderlust wants to merge 4 commits into
durch:masterfrom
w4nderlust:fix/ranged-get-signing-r2

Conversation

@w4nderlust
Copy link
Copy Markdown
Contributor

@w4nderlust w4nderlust commented Apr 6, 2026

Problem

get_object_range (ranged GET) requests fail with SignatureDoesNotMatch on Cloudflare R2.

The headers() method in request_trait.rs had a skip-list of commands that should not include content-length and content-type headers. GetObjectRange was missing from this list, so it fell through to the catch-all _ => arm that adds content-length: 0 and content-type: text/plain.

These empty/meaningless headers get included in the AWS4-HMAC-SHA256 canonical request. AWS S3 happens to tolerate this, but Cloudflare R2 rejects the signature because the canonical request doesn't match what R2 computes server-side.

The same class of bug also affected other body-less commands routed through PUT/POST/DELETE (DeleteObject, AbortMultipartUpload, InitiateMultipartUpload, etc.).

Fix

Added a has_body() helper on Command that returns true only for commands that actually serialize request content (PutObject, UploadPart, CompleteMultipartUpload, CreateBucket, PutBucketLifecycle, PutBucketCors, PutObjectTagging). The header insertion now checks has_body() instead of using a skip-list or matching on the HTTP verb. This prevents the same bug from happening if new body-less commands are added in the future.

Testing

All existing tests pass. Verified the fix against a real Cloudflare R2 bucket: ranged GET downloads that were failing with SignatureDoesNotMatch now succeed.


This change is Reviewable

Summary by CodeRabbit

  • Refactor

    • Request header logic updated so copy-source is handled separately and content headers are only added for requests that carry a body.
  • Behavior

    • Broadened recognition of which S3 operations include a request body (uploads, multipart flows, bucket creation and certain bucket settings), reducing signature mismatches for ranged/other body-less requests.
  • Tests

    • Added tests validating body-detection and signing/header behavior.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 6, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Replaced per-variant header branching with logic that inserts Content-Length and Content-Type only when Command::has_body() is true; added has_body() on Command. CopyObject handling kept separate, inserting only x-amz-copy-source. x-amz-content-sha256 and x-amz-date behavior unchanged.

Changes

Request header construction

Layer / File(s) Summary
Data Shape / API
s3/src/command.rs
Added pub fn has_body(&self) -> bool to mark which Command variants are considered body-bearing (includes PutObject, PutObjectTagging, UploadPart, InitiateMultipartUpload, CompleteMultipartUpload, CreateBucket, PutBucketLifecycle, PutBucketCors, DeleteObjects).
Core Implementation
s3/src/request/request_trait.rs
headers() now checks self.command().has_body() before inserting CONTENT_LENGTH and CONTENT_TYPE. CopyObject is handled via if let to insert only x-amz-copy-source. x-amz-content-sha256 and x-amz-date behavior unchanged.
Tests / Validation
s3/src/command.rs (new test module)
Added unit tests validating has_body() behavior for ranged GetObjectRange, DeleteObject/CopyObject, InitiateMultipartUpload (POST with content_length() == 0), body-bearing commands (PutObject, UploadPart), and DeleteObjects (POST, application/xml, non-zero length).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

  • durch/rust-s3#455: Related edits to request header construction and conditional inclusion of content headers for specific Command variants.

Poem

🐇 I hopped through headers, small and spry,
I told bodies when to live or lie,
Copy-source kept a single key,
Signatures match where they should be,
A tiny hop — hooray, goodbye!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title accurately captures the primary fix: addressing ranged GET signing failures with Cloudflare R2 by introducing the has_body() helper.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@brownian-motion-v0
Copy link
Copy Markdown

Brownian Motion (Brass)

Recommendation: Proceed

Summary: Fixes signing issue for ranged GET requests with Cloudflare R2.
Risk: Medium · Confidence: 80%

Highlights

  • Good test coverage
  • Clear commit history

Unknowns

  • No linked issue

Next actions

  • Keep: the change to check http_verb() for headers
  • Drop: the previous skip-list approach
  • Add: documentation on the new approach for future maintainers

Reflection questions

  • What core assumption underpins this PR's approach?
  • How does this change align with the project's longer-term goals?
  • Could there be a simpler way to achieve the primary objective here?

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
s3/src/request/request_trait.rs (1)

703-719: ⚠️ Potential issue | 🟠 Major

http_verb() is still too broad a proxy for signing body headers.

This fixes the GET/HEAD case, but Lines 712-719 still sign Content-Length and Content-Type for zero-body commands like CopyObject, AbortMultipartUpload, DeleteObject, DeleteBucket, and InitiateMultipartUpload because they are PUT/POST/DELETE. request_body() on Lines 231-254 treats those as empty, so the same signature-drift class can still happen on providers that normalize or ignore empty-request Content-* headers. Please drive this off a dedicated “does this command serialize a body?” helper instead of the HTTP verb.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@s3/src/request/request_trait.rs` around lines 703 - 719, The code currently
decides to sign Content-Length/Content-Type based on http_verb(), which still
signs headers for zero-body commands (e.g., CopyObject, AbortMultipartUpload,
DeleteObject, DeleteBucket, InitiateMultipartUpload); instead, check whether the
command actually serializes a body (use an existing request_body() or add a
helper like serializes_body()/has_request_body() on the Command) and only insert
CONTENT_LENGTH/CONTENT_TYPE when that helper returns true; update the block that
now matches on self.command().http_verb() to call
self.command().request_body().is_some() (or the new helper) and keep
special-case CopyObject header handling as-is.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@s3/src/request/request_trait.rs`:
- Around line 703-719: The code currently decides to sign
Content-Length/Content-Type based on http_verb(), which still signs headers for
zero-body commands (e.g., CopyObject, AbortMultipartUpload, DeleteObject,
DeleteBucket, InitiateMultipartUpload); instead, check whether the command
actually serializes a body (use an existing request_body() or add a helper like
serializes_body()/has_request_body() on the Command) and only insert
CONTENT_LENGTH/CONTENT_TYPE when that helper returns true; update the block that
now matches on self.command().http_verb() to call
self.command().request_body().is_some() (or the new helper) and keep
special-case CopyObject header handling as-is.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 12ead844-6852-400e-923b-8eb7e89e5af0

📥 Commits

Reviewing files that changed from the base of the PR and between 3a9314f and 3eb5fdf.

📒 Files selected for processing (1)
  • s3/src/request/request_trait.rs

@w4nderlust w4nderlust force-pushed the fix/ranged-get-signing-r2 branch from 3eb5fdf to e32c578 Compare April 7, 2026 05:23
@brownian-motion-v0
Copy link
Copy Markdown

Brownian Motion (Brass)

Recommendation: Proceed

Summary: Fixes a signing issue with ranged GET requests for Cloudflare R2.
Risk: Medium · Confidence: 80%

Highlights

  • Good test coverage
  • Addresses a specific compatibility issue

Unknowns

  • No linked issue
  • Potential edge cases with new commands

Next actions

  • Keep: The new has_body() method for clarity.
  • Drop: The skip-list approach for header management.
  • Add: Documentation on the rationale for the changes.

Reflection questions

  • What core assumption underpins this PR's approach?
  • How does this change align with the project's longer-term goals?
  • Could there be a simpler way to achieve the primary objective here?

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@s3/src/command.rs`:
- Around line 214-227: The headers() implementation in request_trait.rs still
checks HTTP verb via self.command().http_verb() to decide whether to insert
CONTENT_LENGTH and CONTENT_TYPE; change that logic to call
self.command().has_body() instead so headers are added for commands that
actually carry bodies (refer to the Command::has_body() method you added).
Replace the current match on HttpMethod with an if self.command().has_body() {
insert CONTENT_LENGTH and CONTENT_TYPE } branch, ensuring you use the same
header keys (CONTENT_LENGTH, CONTENT_TYPE) and value computation already
present; also add a short doctest/example to the public has_body() method’s doc
comment showing typical usage (e.g., calling Command::has_body() to gate header
insertion).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 936ed796-81ba-470d-9dc3-4b663a75cb5a

📥 Commits

Reviewing files that changed from the base of the PR and between 3eb5fdf and e32c578.

📒 Files selected for processing (2)
  • s3/src/command.rs
  • s3/src/request/request_trait.rs
✅ Files skipped from review due to trivial changes (1)
  • s3/src/request/request_trait.rs

Comment thread s3/src/command.rs Outdated
@w4nderlust w4nderlust force-pushed the fix/ranged-get-signing-r2 branch from e32c578 to 3ae1dd0 Compare April 7, 2026 05:38
@brownian-motion-v0
Copy link
Copy Markdown

Brownian Motion (Brass)

Recommendation: Proceed

Summary: Fixes signing issue for body-less commands in Cloudflare R2 requests.
Risk: Medium · Confidence: 80%

Highlights

  • Good test coverage
  • Clear commit history

Unknowns

  • No linked issue
  • Missing performance metrics on signing efficiency

Next actions

  • Keep: the has_body() helper for future-proofing against new commands.
  • Drop: the skip-list approach which adds unnecessary complexity.
  • Add: documentation on the new has_body() method for clarity.

Reflection questions

  • What core assumption underpins this PR's approach?
  • How does this change align with the project's longer-term goals?
  • Could there be a simpler way to achieve the primary objective here?

@durch
Copy link
Copy Markdown
Owner

durch commented May 4, 2026

Thanks for the R2 signing fix — I verified locally that this branch fixes the targeted R2 provider failure:

cd s3
cargo test streaming_big_r2_put_head_get_delete_object -- --ignored --nocapture

On this PR (3ae1dd0), that test passes.

However, I also found a deterministic GCS regression in the same local environment. The targeted GCS streaming multipart test passes repeatedly on current master, but fails repeatedly on this PR:

cd s3
cargo test streaming_big_gc_put_head_get_delete_object -- --ignored --nocapture

Observed results:

  • master / 08545c5: passed 3/3 runs
  • this PR / 3ae1dd0: failed 4/4 runs

The failure is:

HttpFailWithBody(411, "... POST requests require a Content-length header. ...")

The likely cause is the new Command::has_body() gate. It omits Content-Length / Content-Type unless the command is listed as body-bearing:

Command::PutObject { .. }
    | Command::PutObjectTagging { .. }
    | Command::UploadPart { .. }
    | Command::CompleteMultipartUpload { .. }
    | Command::CreateBucket { .. }
    | Command::PutBucketLifecycle { .. }
    | Command::PutBucketCors { .. }

But InitiateMultipartUpload is not included. The failing GCS path uses streaming multipart upload, which sends a POST initiate-multipart request. GCS rejects that request when Content-Length is omitted, even if the body is empty.

So I agree with the direction — avoiding empty signed body headers fixes R2 — but this PR needs one more adjustment before merge:

  • keep omitting empty Content-Length / Content-Type for truly body-less requests that break R2 signatures
  • preserve required Content-Length behavior for provider-sensitive POST flows such as InitiateMultipartUpload
  • ideally add provider-agnostic regression coverage for both cases:
    • ranged GET / body-less commands do not get bogus signed body headers
    • initiate multipart upload still gets the headers required by GCS-compatible behavior

Once that is fixed, this should be a strong compatibility improvement.

w4nderlust added 2 commits May 6, 2026 14:59
GetObjectRange (and other body-less commands like DeleteObject,
AbortMultipartUpload, etc.) were getting content-length and content-type
headers included in the signed request. For commands with no body these
headers are empty/meaningless, but they still end up in the canonical
request signature. Cloudflare R2 rejects the resulting signature
(SignatureDoesNotMatch), while AWS S3 happens to tolerate it.

Added a `has_body()` helper on Command that returns true only for
commands that actually serialize request content (PutObject, UploadPart,
CompleteMultipartUpload, etc.). The header insertion in request_trait.rs
now checks `has_body()` instead of matching on the HTTP verb, which
avoids signing empty headers for any current or future body-less command.
GCS rejects POST requests without a Content-Length header (HTTP 411),
even when the body is empty. The previous has_body() definition omitted
Content-Length for InitiateMultipartUpload, regressing streaming
multipart uploads against GCS.

Add InitiateMultipartUpload to has_body() so Content-Length: 0 is sent
on the initiate-multipart POST. Document why the helper covers a
body-less command. Add provider-agnostic unit tests for has_body()
covering ranged GET, body-less DELETE/CopyObject, InitiateMultipartUpload,
and body-bearing commands.
@w4nderlust w4nderlust force-pushed the fix/ranged-get-signing-r2 branch from 3ae1dd0 to 52881b0 Compare May 6, 2026 21:59
@brownian-motion-v0
Copy link
Copy Markdown

Brownian Motion (Brass)

Recommendation: Proceed

Summary: Fixes signature mismatch for ranged GET requests on Cloudflare R2.
Risk: Medium · Confidence: 80%

Highlights

  • Good test coverage
  • Clear commit history

Unknowns

  • No linked issue
  • Potential edge cases with other providers

Next actions

  • Keep: the has_body() helper for future-proofing against new commands.
  • Drop: any redundant checks or complexity in header management.
  • Add: documentation on the rationale for has_body() and its implications.

Reflection questions

  • What core assumption underpins this PR's approach?
  • How does this change align with the project's longer-term goals?
  • Could there be a simpler way to achieve the primary objective here?

@w4nderlust
Copy link
Copy Markdown
Contributor Author

w4nderlust commented May 6, 2026

Thanks for the careful message. You're right, the has_body() gate was too narrow and dropped Content-Length from the InitiateMultipartUpload POST, which GCS rejects with HTTP 411.

I've pushed a fix and rebased the branch onto current master:

Change (s3/src/command.rs): added Command::InitiateMultipartUpload to has_body(). The helper's doc comment now spells out both reasons it governs Content-Length / Content-Type:

  • the command actually serializes a body, or
  • the command is a body-less POST whose provider-side contract still requires Content-Length (currently just InitiateMultipartUpload for GCS).

I left the rest of the body-less POST/PUT/DELETE commands out of has_body() on purpose, the original skip-list behaviour was that DeleteObject, AbortMultipartUpload, etc. did get Content-Length: 0 via the _ => arm, but you didn't observe a regression on those, and keeping them omitted preserves the R2 signing fix for the broadest surface. Happy to widen the helper if you'd rather match the previous behaviour exactly for DELETEs.

Also worth noting that InitiateMultipartUpload's Content-Type isn't a description of the (empty) request body, it's the content type to associate with the eventual multipart object. So signing it for that command is semantically correct, not just GCS-driven.

Provider-agnostic regression coverage added in s3/src/command.rs under mod tests:

  • ranged_get_does_not_have_body — pins the R2 fix (ranged GET / GET / HEAD / ListBuckets all report false).
  • delete_and_copy_do_not_have_body — pins body-less DELETE / CopyObject behaviour.
  • initiate_multipart_upload_has_body_for_gcs_compat — pins the GCS fix (has_body() == true, http_verb() == POST, content_length() == 0).
  • body_bearing_commands_have_bodyPutObject / UploadPart still report a body.

Verification on my end:

cargo test --lib   # 63 passed, 0 failed, 25 ignored

I don't have a GCS bucket set up locally to re-run streaming_big_gc_put_head_get_delete_object directly, so if you can re-run that against the rebased branch (52881b0) along with streaming_big_r2_put_head_get_delete_object, that should confirm both providers are happy.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@s3/src/command.rs`:
- Around line 404-464: Add tests that exercise Request::headers() to ensure
body-related headers match the Command classification: create requests for
Command::GetObjectRange (and Command::GetObject/HeadObject/ListBuckets) and
assert that headers do not contain Content-Length/Content-Type; create requests
for Command::InitiateMultipartUpload to assert Content-Length: 0 is present; and
create requests for Command::DeleteObject/AbortMultipartUpload/CopyObject to
assert no body headers. Use the existing Command variants (e.g.,
Command::GetObjectRange, Command::InitiateMultipartUpload,
Command::DeleteObject) and the Request::headers() method to validate the actual
header set so future changes to has_body() won’t drift from the signed headers.
- Around line 236-247: The has_body() matcher is missing the
Command::DeleteObjects variant so multi-object delete requests are treated as
body-less; update the Command::has_body() function to include
Command::DeleteObjects { .. } alongside the other variants (e.g., PutObject,
UploadPart, InitiateMultipartUpload, CompleteMultipartUpload, CreateBucket,
PutBucketLifecycle, PutBucketCors) so DeleteObjects returns true and the
request/request_trait logic will include Content-Length/Content-Type and body
signing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b3c2ed91-1d3f-49c8-bbc0-e43e3d8b3627

📥 Commits

Reviewing files that changed from the base of the PR and between 3ae1dd0 and 52881b0.

📒 Files selected for processing (2)
  • s3/src/command.rs
  • s3/src/request/request_trait.rs

Comment thread s3/src/command.rs
Comment thread s3/src/command.rs
Comment on lines +404 to +464
#[cfg(test)]
mod tests {
use super::*;

/// Body-less `GET`s (notably ranged `GET`) must not advertise body
/// headers, otherwise Cloudflare R2 rejects the AWS4-HMAC-SHA256
/// signature for ranged downloads.
#[test]
fn ranged_get_does_not_have_body() {
let cmd = Command::GetObjectRange {
start: 0,
end: Some(1023),
};
assert!(!cmd.has_body());
assert!(!Command::GetObject.has_body());
assert!(!Command::HeadObject.has_body());
assert!(!Command::ListBuckets.has_body());
}

/// `DELETE` and `CopyObject` carry no request body.
#[test]
fn delete_and_copy_do_not_have_body() {
assert!(!Command::DeleteObject.has_body());
assert!(!Command::AbortMultipartUpload { upload_id: "u" }.has_body());
assert!(!Command::CopyObject { from: "x" }.has_body());
}

/// `InitiateMultipartUpload` is body-less but must still be reported as
/// having a body so that `Content-Length: 0` is sent. GCS returns HTTP
/// 411 on `POST` requests without `Content-Length`, even when the body
/// is empty.
#[test]
fn initiate_multipart_upload_has_body_for_gcs_compat() {
let cmd = Command::InitiateMultipartUpload {
content_type: "application/octet-stream",
};
assert!(cmd.has_body());
assert_eq!(cmd.http_verb(), HttpMethod::Post);
assert_eq!(cmd.content_length().unwrap(), 0);
}

/// Body-bearing commands report `has_body() == true` so signing
/// includes accurate `Content-Length` / `Content-Type`.
#[test]
fn body_bearing_commands_have_body() {
let put = Command::PutObject {
content: b"hello",
content_type: "text/plain",
custom_headers: None,
multipart: None,
};
assert!(put.has_body());

let upload = Command::UploadPart {
part_number: 1,
content: b"data",
upload_id: "u",
};
assert!(upload.has_body());
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | 🏗️ Heavy lift

Please cover the header integration, not just has_body().

These tests prove the enum classification, but the regression surface is Request::headers() in s3/src/request/request_trait.rs. A header-level test for ranged GET vs InitiateMultipartUpload/DeleteObjects would catch future drifts between has_body() and the signed header set.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@s3/src/command.rs` around lines 404 - 464, Add tests that exercise
Request::headers() to ensure body-related headers match the Command
classification: create requests for Command::GetObjectRange (and
Command::GetObject/HeadObject/ListBuckets) and assert that headers do not
contain Content-Length/Content-Type; create requests for
Command::InitiateMultipartUpload to assert Content-Length: 0 is present; and
create requests for Command::DeleteObject/AbortMultipartUpload/CopyObject to
assert no body headers. Use the existing Command variants (e.g.,
Command::GetObjectRange, Command::InitiateMultipartUpload,
Command::DeleteObject) and the Request::headers() method to validate the actual
header set so future changes to has_body() won’t drift from the signed headers.

DeleteObjects is a POST with an XML body listing the keys to delete
(Content-Type: application/xml, content_length > 0). It was missing
from has_body(), so multi-object delete requests would have been signed
without Content-Length/Content-Type, causing failures and signature
mismatches on strict providers.

Add a unit test pinning the body-bearing classification, content type,
and non-zero content length.
@brownian-motion-v0
Copy link
Copy Markdown

Brownian Motion (Brass)

Recommendation: Proceed

Summary: PR effectively fixes signing issues for body-less commands in S3 requests.
Risk: Medium · Confidence: 80%

Highlights

  • Good test coverage
  • Clear commit history

Unknowns

  • No linked issue

Next actions

  • Keep: the new has_body() method for future-proofing
  • Drop: unnecessary complexity in header handling
  • Add: documentation on the implications of changes

Reflection questions

  • What core assumption underpins this PR's approach?
  • How does this change align with the project's longer-term goals?
  • Could there be a simpler way to achieve the primary objective here?

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
s3/src/command.rs (1)

236-249: ⚡ Quick win

Add a # Examples doctest to has_body().

The method's narrative doc comment is thorough, but a short compilable example is still missing. The coding guideline requires documentation examples for all public APIs.

📝 Suggested doctest addition
+    /// # Examples
+    ///
+    /// ```
+    /// # use rust_s3::command::Command;
+    /// assert!(Command::PutObject { content: b"hi", content_type: "text/plain", custom_headers: None, multipart: None }.has_body());
+    /// assert!(!Command::GetObject.has_body());
+    /// assert!(!Command::GetObjectRange { start: 0, end: Some(1023) }.has_body());
+    /// ```
     pub fn has_body(&self) -> bool {

As per coding guidelines: "All public APIs should have documentation examples."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@s3/src/command.rs` around lines 236 - 249, The public method
Command::has_body is missing a doctest example; add a short, compilable Rust doc
example under the function's doc comment showing usage and assertions—for
example construct a Command::PutObject with sample fields and assert has_body()
is true, and assert that Command::GetObject and Command::GetObjectRange { start:
0, end: Some(1023) } return false; ensure the example uses fully qualified path
if necessary (e.g., use rust_s3::command::Command;) and is fenced as a doctest
so it compiles during cargo test.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@s3/src/command.rs`:
- Around line 236-249: The public method Command::has_body is missing a doctest
example; add a short, compilable Rust doc example under the function's doc
comment showing usage and assertions—for example construct a Command::PutObject
with sample fields and assert has_body() is true, and assert that
Command::GetObject and Command::GetObjectRange { start: 0, end: Some(1023) }
return false; ensure the example uses fully qualified path if necessary (e.g.,
use rust_s3::command::Command;) and is fenced as a doctest so it compiles during
cargo test.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1d22905d-32ce-4d8d-ab03-e99e968984a7

📥 Commits

Reviewing files that changed from the base of the PR and between 52881b0 and c86795c.

📒 Files selected for processing (1)
  • s3/src/command.rs

@brownian-motion-v0
Copy link
Copy Markdown

Brownian Motion (Brass)

Recommendation: Proceed

Summary: Fixes signature mismatch for ranged GET requests on Cloudflare R2.
Risk: Medium · Confidence: 80%

Highlights

  • Good test coverage
  • Clear commit history

Unknowns

  • No linked issue
  • Unclear impact on other providers

Next actions

  • Keep: the has_body() helper for future-proofing against new commands.
  • Drop: unnecessary complexity in header management.
  • Add: documentation on the implications of this change for future developers.

Reflection questions

  • What core assumption underpins this PR's approach?
  • How does this change align with the project's longer-term goals?
  • Could there be a simpler way to achieve the primary objective here?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants