Skip to content

fix(headers): normalize header values by trimming HTTP whitespace - #1898

Open
spokodev wants to merge 1 commit into
node-fetch:mainfrom
spokodev:fix/headers-normalize-value-whitespace
Open

fix(headers): normalize header values by trimming HTTP whitespace#1898
spokodev wants to merge 1 commit into
node-fetch:mainfrom
spokodev:fix/headers-normalize-value-whitespace

Conversation

@spokodev

Copy link
Copy Markdown

Problem

Headers does not normalize header values. The WHATWG Fetch standard
requires append/set (and construction) to normalize a value
remove leading and trailing HTTP whitespace bytes (0x09 tab, 0x0A LF,
0x0D CR, 0x20 space) — before running the validity check. node-fetch
stored String(value) verbatim and validated the un-normalized value, so
it both keeps surrounding whitespace and rejects values that are valid
after normalization.

Reproduction against Node's built-in Headers as the oracle:

import NodeFetchHeaders from 'node-fetch/src/headers.js';

// Node built-in Headers (oracle)
const a = new Headers(); a.set('x', '  hello  ');
a.get('x');                              // "hello"
new Headers([['z', 'val\r\n']]).get('z'); // "val"

// node-fetch (current)
const b = new NodeFetchHeaders(); b.set('x', '  hello  ');
b.get('x');                              // "  hello  "  (whitespace kept)
new NodeFetchHeaders([['z', 'val\r\n']]); // throws ERR_INVALID_CHAR

Spec

WHATWG Fetch 5.1 "Headers class":
append(name, value) and set(name, value) run "normalize value"
(strip leading/trailing HTTP whitespace) and only then check that the
name is a valid header name and the value is a valid header value.

Root cause

src/headers.js calls validateHeaderValue(name, String(value)) and
stores String(value) with no normalization, in two places:

  • the constructor's result.map(...) over the init pairs
  • the append/set branch of the Proxy trap

grep -rn "\.trim" src/ returned nothing.

Fix

Add a normalizeValue helper that strips leading/trailing HTTP
whitespace, and apply it to the value before validation in both the
constructor and the append/set trap, storing the normalized value.

Tests

Added a test/headers.js case asserting that surrounding whitespace is
trimmed on set/append/construct and that a trailing \r\n value is
accepted (normalized away) rather than thrown.

  • Without the fix: the new test fails (' hello ' !== 'hello').
  • With the fix: it passes.

Full suite: header tests all green (16/16). The repo has 3 pre-existing
network/timing failures (chunked-packet split, agent family IP
resolution) on a clean checkout in this environment; this change does not
add or remove any of them (388 → 389 passing, same 3 failing).

Per WHATWG Fetch 5.1, Headers append/set/construct must normalize a
value (strip leading and trailing HTTP whitespace: 0x09, 0x0A, 0x0D,
0x20) before validating it. node-fetch stored the raw value and
validated it un-normalized, so it kept surrounding whitespace and threw
ERR_INVALID_CHAR on an otherwise-valid value with a trailing CRLF.

Add a normalizeValue helper applied before validation in both the
constructor map and the append/set Proxy trap.
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.

1 participant