Skip to content

fix(serve-static): correct Range header parsing edge cases#372

Open
otnc wants to merge 2 commits into
honojs:mainfrom
otnc:fix/serve-static-range-parsing
Open

fix(serve-static): correct Range header parsing edge cases#372
otnc wants to merge 2 commits into
honojs:mainfrom
otnc:fix/serve-static-range-parsing

Conversation

@otnc

@otnc otnc commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

serveStatic's Range parsing had three edge cases that didn't match RFC 9110:

  • bytes=-N (suffix range) returned the first N+1 bytes instead of the last N.
  • bytes=0-0 returned the whole file instead of 1 byte (parseInt(...) || size - 1 treated a valid 0 as falsy).
  • An unsatisfiable range (start beyond EOF, or start > end, e.g. bytes=100-200 on a 17-byte file) crashed createReadStream with RangeError instead of returning 416.

Replaced the ad-hoc parsing with a parseRange() helper that handles all three correctly while keeping the existing behavior for malformed headers (e.g. Range: hello still serves the whole file, per the existing test).

Added tests for each case; all existing tests still pass unchanged.

- `bytes=-N` (suffix range) now returns the last N bytes instead of
  the first N+1 bytes.
- `bytes=0-0` now returns 1 byte instead of the whole file (the old
  `parseInt(...) || size - 1` treated a valid 0 as falsy).
- An unsatisfiable range (start beyond EOF, or start > end) now
  returns 416 instead of crashing createReadStream with a RangeError.
@yusukebe

Copy link
Copy Markdown
Member

@otnc Thank you for the PR.

Hey @usualoma , what do you think of this PR's direction? I think these are actual bugs. If this PR is acceptable, I'll review it.

@usualoma

Copy link
Copy Markdown
Member

Hi @otnc
cc: @yusukebe

Thank you for creating the PR. I think this is a great way to implement it!

I have two concerns:

Malformed ranges

Now that range parsing has been extracted into its own helper, I think bytes=x-1 and bytes=0-1x should be treated as malformed rather than partially parsed. Currently, both appear to be interpreted as 0-1.

Empty files

For an empty file, Range: hello returns { start: 0, end: -1 }, causing createReadStream() to throw and respond with 500. We should add a regression test for this case.

…rash

- Range segments with trailing garbage (e.g. `bytes=0-1x`) were silently
  truncated by parseInt into `0-1` instead of being treated as malformed.
  first-pos/last-pos/suffix-length are now validated as `1*DIGIT`.
- A Range request on an empty file fell through to createReadStream with
  end=-1 and crashed with 500 instead of returning 416.

Addresses review feedback from @usualoma on honojs#372.
@otnc

otnc commented Jul 11, 2026

Copy link
Copy Markdown
Contributor Author

@usualoma
cc: @yusukebe
Thank you for your review!
Fixed a mistake in the code.

@yusukebe yusukebe left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM!

@yusukebe

Copy link
Copy Markdown
Member

@usualoma Could you review this again?

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.

3 participants