Skip to content

Commit 6ff152c

Browse files
ronagMylesBorins
authored andcommitted
http: allow passing array of key/val into writeHead
Enables an optimization when the user already has the headers in an array form, e.g. when proxying. PR-URL: #35274 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: Rich Trott <[email protected]>
1 parent 7d1cdd4 commit 6ff152c

File tree

4 files changed

+95
-4
lines changed

4 files changed

+95
-4
lines changed

doc/api/http.md

+9-1
Original file line numberDiff line numberDiff line change
@@ -1786,6 +1786,9 @@ the request body should be sent. See the [`'checkContinue'`][] event on
17861786
<!-- YAML
17871787
added: v0.1.30
17881788
changes:
1789+
- version: REPLACEME
1790+
pr-url: https://github.com/nodejs/node/pull/35274
1791+
description: Allow passing headers as an array.
17891792
- version:
17901793
- v11.10.0
17911794
- v10.17.0
@@ -1802,14 +1805,19 @@ changes:
18021805

18031806
* `statusCode` {number}
18041807
* `statusMessage` {string}
1805-
* `headers` {Object}
1808+
* `headers` {Object|Array}
18061809
* Returns: {http.ServerResponse}
18071810

18081811
Sends a response header to the request. The status code is a 3-digit HTTP
18091812
status code, like `404`. The last argument, `headers`, are the response headers.
18101813
Optionally one can give a human-readable `statusMessage` as the second
18111814
argument.
18121815

1816+
`headers` may be an `Array` where the keys and values are in the same list.
1817+
It is *not* a list of tuples. So, the even-numbered offsets are key values,
1818+
and the odd-numbered offsets are the associated values. The array is in the same
1819+
format as `request.rawHeaders`.
1820+
18131821
Returns a reference to the `ServerResponse`, so that calls can be chained.
18141822

18151823
```js

lib/_http_outgoing.js

+13-2
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ const {
5353
ERR_HTTP_TRAILER_INVALID,
5454
ERR_INVALID_HTTP_TOKEN,
5555
ERR_INVALID_ARG_TYPE,
56+
ERR_INVALID_ARG_VALUE,
5657
ERR_INVALID_CHAR,
5758
ERR_METHOD_NOT_IMPLEMENTED,
5859
ERR_STREAM_CANNOT_PIPE,
@@ -376,8 +377,18 @@ function _storeHeader(firstLine, headers) {
376377
processHeader(this, state, entry[0], entry[1], false);
377378
}
378379
} else if (ArrayIsArray(headers)) {
379-
for (const entry of headers) {
380-
processHeader(this, state, entry[0], entry[1], true);
380+
if (headers.length && ArrayIsArray(headers[0])) {
381+
for (const entry of headers) {
382+
processHeader(this, state, entry[0], entry[1], true);
383+
}
384+
} else {
385+
if (headers.length % 2 !== 0) {
386+
throw new ERR_INVALID_ARG_VALUE('headers', headers);
387+
}
388+
389+
for (let n = 0; n < headers.length; n += 2) {
390+
processHeader(this, state, headers[n + 0], headers[n + 1], true);
391+
}
381392
}
382393
} else {
383394
for (const key in headers) {

lib/_http_server.js

+12-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
'use strict';
2323

2424
const {
25+
ArrayIsArray,
2526
Error,
2627
ObjectKeys,
2728
ObjectSetPrototypeOf,
@@ -62,6 +63,7 @@ const {
6263
ERR_HTTP_HEADERS_SENT,
6364
ERR_HTTP_INVALID_STATUS_CODE,
6465
ERR_INVALID_ARG_TYPE,
66+
ERR_INVALID_ARG_VALUE,
6567
ERR_INVALID_CHAR
6668
} = require('internal/errors').codes;
6769
const {
@@ -269,7 +271,16 @@ function writeHead(statusCode, reason, obj) {
269271
if (this[kOutHeaders]) {
270272
// Slow-case: when progressive API and header fields are passed.
271273
let k;
272-
if (obj) {
274+
if (ArrayIsArray(obj)) {
275+
if (obj.length % 2 !== 0) {
276+
throw new ERR_INVALID_ARG_VALUE('headers', obj);
277+
}
278+
279+
for (let n = 0; n < obj.length; n += 2) {
280+
k = obj[n + 0];
281+
if (k) this.setHeader(k, obj[n + 1]);
282+
}
283+
} else if (obj) {
273284
const keys = ObjectKeys(obj);
274285
// Retain for(;;) loop for performance reasons
275286
// Refs: https://github.com/nodejs/node/pull/30958
+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
'use strict';
2+
const common = require('../common');
3+
const assert = require('assert');
4+
const http = require('http');
5+
6+
// Verify that ServerResponse.writeHead() works with arrays.
7+
8+
{
9+
const server = http.createServer(common.mustCall((req, res) => {
10+
res.setHeader('test', '1');
11+
res.writeHead(200, [ 'test', '2', 'test2', '2' ]);
12+
res.end();
13+
}));
14+
15+
server.listen(0, common.mustCall(() => {
16+
http.get({ port: server.address().port }, common.mustCall((res) => {
17+
assert.strictEqual(res.headers.test, '2');
18+
assert.strictEqual(res.headers.test2, '2');
19+
res.resume().on('end', common.mustCall(() => {
20+
server.close();
21+
}));
22+
}));
23+
}));
24+
}
25+
26+
{
27+
const server = http.createServer(common.mustCall((req, res) => {
28+
res.writeHead(200, [ 'test', '1', 'test2', '2' ]);
29+
res.end();
30+
}));
31+
32+
server.listen(0, common.mustCall(function() {
33+
http.get({ port: server.address().port }, common.mustCall((res) => {
34+
assert.strictEqual(res.headers.test, '1');
35+
assert.strictEqual(res.headers.test2, '2');
36+
res.resume().on('end', common.mustCall(() => {
37+
server.close();
38+
}));
39+
}));
40+
}));
41+
}
42+
43+
44+
{
45+
const server = http.createServer(common.mustCall((req, res) => {
46+
try {
47+
res.writeHead(200, [ 'test', '1', 'test2', '2', 'asd' ]);
48+
} catch (err) {
49+
assert.strictEqual(err.code, 'ERR_INVALID_ARG_VALUE');
50+
}
51+
res.end();
52+
}));
53+
54+
server.listen(0, common.mustCall(function() {
55+
http.get({ port: server.address().port }, common.mustCall((res) => {
56+
res.resume().on('end', common.mustCall(() => {
57+
server.close();
58+
}));
59+
}));
60+
}));
61+
}

0 commit comments

Comments
 (0)