Skip to content

fix: clamp range last-pos to EOF per RFC 9110 - #738

Open
Huang-404-Q wants to merge 1 commit into
sigoden:mainfrom
Huang-404-Q:fix/range-clamp-last-pos-735
Open

Huang-404-Q wants to merge 1 commit into
sigoden:mainfrom
Huang-404-Q:fix/range-clamp-last-pos-735

Conversation

@Huang-404-Q

Copy link
Copy Markdown

Problem

A Range header whose last-pos lies beyond the end of the file — e.g. bytes=0-999 against a 500-byte file, or the exact-EOF case bytes=0-5 against a 5-byte file (#735) — is answered 416 Range Not Satisfiable, although the first part of the range is fully satisfiable.

RFC 9110 §14.1.2 requires the opposite: when last-pos is present and greater than or equal to the current length, the valid range is first-pos .. length-1 and the server responds 206 Partial Content with the clamped range. 416 is only for first-pos >= length.

Root cause

parse_range in src/utils.rs rejected any range whose end >= size instead of clamping it:

if end < size && start <= end { /* satisfiable */ } else { return None; }

The None made GET /file (src/server.rs) set 416 + Content-Range: bytes */{size}.

Fix

Clamp end to size - 1 in parse_range (single and multipart ranges alike); first-pos >= size still returns unsatisfiable. The second caller, parse_upload_offset (WebDAV PATCH), only uses the start position and is unaffected.

Tests

  • Unit (src/utils.rs): 6 new assertions — exact EOF, beyond-EOF, mid-range beyond EOF, multipart with an out-of-range part, the two exact-EOF cases from the issue, and first-pos == size still unsatisfiable.
  • Integration (tests/range.rs): 6 new tests against a real server — 206 with clamped Content-Range and the full body, the boundary bytes=0-<len-1>, mid-range clamping, HEAD, multipart where both parts are clamped, and mixed satisfiable + unsatisfiable parts still 416 (regression guard). The existing get_file_range_beyond expectation was corrected from 416 to the RFC-correct 206 bytes 12-17/18.

Verification

  • New unit tests fail on the old code and pass with the fix (red → green).
  • cargo test --all — all 21 suites pass.
  • cargo clippy --all --all-targets — no new warnings; cargo fmt --all --check clean.

Fixes #735

A range whose last-pos exceeds the file size was rejected with 416,
but per RFC 9110 §14.1.2 it is satisfiable and last-pos must be
clamped to size - 1. Clamp in parse_range() so such ranges return 206
with the clamped end position (e.g. bytes=0-9 on a 5-byte file now
returns 206, content-range: bytes 0-4/5).

Ranges with first-pos >= size are still rejected with 416, as before.

Fixes sigoden#735
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.

Range with last-pos beyond EOF returns 416 instead of being clamped

1 participant