From 19e479683b04cb01444b6aed6aef584f82dd1d14 Mon Sep 17 00:00:00 2001 From: Yarchik Date: Tue, 23 Jun 2026 02:29:22 +0100 Subject: [PATCH] fix: treat HTAB as optional whitespace in If-None-Match / If-Match lists `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. --- index.js | 1 + test/fresh.js | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/index.js b/index.js index fc3dea7..121f264 100644 --- a/index.js +++ b/index.js @@ -114,6 +114,7 @@ function parseTokenList (str) { // gather tokens for (var i = 0, len = str.length; i < len; i++) { switch (str.charCodeAt(i)) { + case 0x09: /* HTAB */ case 0x20: /* */ if (start === end) { start = end = i + 1 diff --git a/test/fresh.js b/test/fresh.js index ebedc03..7c72e5f 100644 --- a/test/fresh.js +++ b/test/fresh.js @@ -34,6 +34,12 @@ describe('fresh(reqHeaders, resHeaders)', function () { var resHeaders = { etag: '"foo"' } assert.ok(fresh(reqHeaders, resHeaders)) }) + + it('should be fresh when the list is separated with tabs', function () { + var reqHeaders = { 'if-none-match': '"bar",\t"foo"' } + var resHeaders = { etag: '"foo"' } + assert.ok(fresh(reqHeaders, resHeaders)) + }) }) describe('when etag is missing', function () {