Skip to content

fix: treat HTAB as optional whitespace in If-None-Match / If-Match lists#77

Merged
blakeembrey merged 1 commit into
jshttp:masterfrom
spokodev:fix-ows-htab-token-list
Jun 23, 2026
Merged

fix: treat HTAB as optional whitespace in If-None-Match / If-Match lists#77
blakeembrey merged 1 commit into
jshttp:masterfrom
spokodev:fix-ows-htab-token-list

Conversation

@spokodev

Copy link
Copy Markdown
Contributor

Problem

parseTokenList only skips SP (0x20) between entity-tags, so a tab around the comma in an If-None-Match (or If-Match) list is treated as part of the next tag, which then never matches:

fresh({ 'if-none-match': '"foo", "bar"' },  { etag: '"bar"' }) // true  (correct)
fresh({ 'if-none-match': '"foo",\t"bar"' }, { etag: '"bar"' }) // false (wrong)

"bar" is a member of the list in both cases.

Why it's a bug

Per RFC 7230 §3.2.3, OWS = *( SP / HTAB ), and the list rule of §7 is 1#element => element *( OWS "," OWS element ). A tab around the comma is therefore valid wire syntax that any client or proxy may emit, so the list must still match. When it doesn't, a conditional request that should produce 304 Not Modified returns a full 200, defeating caching.

The sibling jshttp library negotiator already treats HTAB as whitespace (it parses its lists with String.prototype.trim()), so honoring OWS-with-tab is the established convention here — the SP-only handling is an oversight.

Fix

Add 0x09 (HTAB) to the whitespace case in parseTokenList:

case 0x09: /* HTAB */
case 0x20: /*   */

Verification

  • New test in test/fresh.js (tab-separated list); fails before, passes after.
  • Existing suite: 24 passing, no regressions.
  • Differential vs an independent RFC 7232/7230 oracle over 200,000 randomized lists with mixed SP/HTAB OWS: 0 divergences.

`parseTokenList` only skipped SP (0x20) between entity-tags, so a tab
around the comma in an `If-None-Match` (or `If-Match`) list left the tab
glued to the next tag, which then never compared equal:

  fresh({ 'if-none-match': '"foo",\t"bar"' }, { etag: '"bar"' }) // was false

Per RFC 7230 section 3.2.3, `OWS = *( SP / HTAB )`, and the list rule of
section 7 allows OWS around the comma, so a tab-separated list is valid and
must match. A missed match makes a conditional request return 200 instead
of 304, defeating caching. Treat HTAB (0x09) as whitespace too.
@blakeembrey blakeembrey merged commit ee73673 into jshttp:master Jun 23, 2026
10 checks passed
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