HTTP/2 compat API assumes the wrong format when accepting raw headers
Version
v18.0.0 (and v14.19.1)
Platform
Linux Zapp 5.4.0-109-generic #123 (closed)-Ubuntu SMP Fri Apr 8 09:10:54 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux
Subsystem
http2
What steps will reproduce the bug?
This code runs an HTTP/2 server, sends a request to it, and responds using writeHead
with a raw header array:
const http2 = require('http2');
const server = http2.createServer((req, res) => {
res.setHeader('Content-Type', 'text/html');
res.setHeader('X-Foo', 'bar');
res.writeHead(200, [ 'Content-Type', 'text/plain; charset=utf-8' ]);
res.end('ok');
});
server.listen(8080)
const client = http2.connect('http://localhost:8080');
client.on('error', (err) => console.error(err));
const req = client.request({ ':path': '/' });
req.on('response', (headers, flags) => {
for (const name in headers) {
console.log(`HEADER: ${name}: ${headers[name]}`);
}
});
req.setEncoding('utf8');
let data = '';
req.on('data', (chunk) => { data += chunk; });
req.on('end', () => {
console.log(`\n${data}`);
client.close();
});
req.end();
How often does it reproduce? Is there a required condition?
Always, I'm fairly sure this applies for all node versions, all platforms.
What is the expected behavior?
The client should print headers including Content-Type: text/plain; charset=utf-8
What do you see instead?
The script above actually prints:
HEADER: :status: 200
HEADER: content-type: text/html
HEADER: x-foo: bar
HEADER: c: o
HEADER: t: e
HEADER: date: Thu, 28 Apr 2022 17:50:19 GMT
ok
Note the c: o
, t: e
headers (from COntent...
, TExt/plain...
)
Additional information
The issue is in the writeHead
last argument. The writeHead
method should support raw headers provided as a flat array of [key, value, key, value]
as documented in the HTTP/1 docs:
headers
may be an Array where the keys and values are in the same list. It is not a list of tuples.
The HTTP/2 implementation has an array case that tries to do this (here) but it incorrectly assumes that the array will actually be [[key, value], [key, value]]
, and so it ends up pulling out individual characters instead.
In fact, the HTTP/1 API does support both flat arrays and also (undocumented) tuple arrays (only in some cases, see here) which may have caused this confusion. The flat array is the documented format though, and the only one that works in all cases, so that's what HTTP/2 should support (or both, I guess).