Description
Version
v23.6.1, v22.13.1, v20.18.2, v18.20.6
Platform
Darwin xxx 24.1.0 Darwin Kernel Version 24.1.0: Thu Oct 10 21:03:15 PDT 2024; root:xnu-11215.41.3~2/RELEASE_ARM64_T6000 arm64
Subsystem
http2
What steps will reproduce the bug?
Any response header value that starts or ends with a space or tab character is omitted in the HTTP/2 headers object of the response.
The server sends:
res.writeHead(200, {
"key1": "foo ",
"key2": " foo",
"key3": "foo\t",
"key4": "\tfoo",
"key5": "foo",
})
;
The http2
client receives:
[Object: null prototype] {
':status': 200,
key5: 'foo',
date: 'Wed, 22 Jan 2025 13:03:04 GMT',
[Symbol(nodejs.http2.sensitiveHeaders)]: []
}
Script to reproduce
import { createServer, connect } from "node:http2";
const port = 8011;
const server = createServer(async (req, res) => {
res.writeHead(200, {
"key1": "foo ",
"key2": " foo",
"key3": "foo\t",
"key4": "\tfoo",
"key5": "foo",
});
res.end();
});
server.listen(port, () => {
// Alternatively, comment out call(), and run:
// curl -v http://localhost:8011 --http2-prior-knowledge
// This will show response headers with surrounding whitespace, proving that the issue is in the client.
call();
});
function call() {
const session = connect(`http://localhost:${port}`);
session.on("error", (err) => console.error(err));
const stream = session.request({
":path": "/",
});
stream.on("response", (headers, flags) => {
console.log("client received response headers:", headers);
});
stream.on("end", () => {
session.close();
server.close();
});
stream.end();
}
How often does it reproduce? Is there a required condition?
Always, for response header values that start or end with a space or tab character.
What is the expected behavior? Why is that the expected behavior?
I expect to receive trimmed field values. I do not expect fields to be silently dropped.
Expected output:
[Object: null prototype] {
':status': 200,
key1: 'foo',
key2: 'foo',
key3: 'foo',
key4: 'foo',
key5: 'foo',
date: 'Wed, 22 Jan 2025 13:03:04 GMT',
[Symbol(nodejs.http2.sensitiveHeaders)]: []
}
What do you see instead?
Omitted header fields key 1 to 4:
[Object: null prototype] {
':status': 200,
key5: 'foo',
date: 'Wed, 22 Jan 2025 13:03:04 GMT',
[Symbol(nodejs.http2.sensitiveHeaders)]: []
}
Additional information
Any request or response that contains a character not permitted in a header field value MUST be treated as malformed (Section 8.1.2.6). Valid characters are defined by the "field-content" ABNF rule in Section 3.2 of [RFC7230].
Each header field consists of a case-insensitive field name followed by a colon (":"), optional leading whitespace, the field value, and optional trailing whitespace.
header-field = field-name ":" OWS field-value OWS [...] field-value = *( field-content / obs-fold ) field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
Appendix B defines OWS = *( SP / HTAB )
.
Based on this definition, I expect space and tab around field-content to be permitted in a header-field, but to be discarded when parsing the field-value.