Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

http: response.setHeaders() #46109

Merged
merged 12 commits into from
Jan 9, 2023
46 changes: 46 additions & 0 deletions doc/api/http.md
Original file line number Diff line number Diff line change
Expand Up @@ -1953,6 +1953,8 @@ non-string values. However, the non-string values will be converted to strings
for network transmission. The same response object is returned to the caller,
to enable call chaining.

> To set multiple header values at once see [`response.setHeaders()`][].

```js
response.setHeader('Content-Type', 'text/html');
```
Expand Down Expand Up @@ -1987,6 +1989,49 @@ header will not yield the expected result. If progressive population of headers
is desired with potential future retrieval and modification, use
[`response.setHeader()`][] instead of [`response.writeHead()`][].

### `response.setHeaders(headers)`

<!-- YAML
added: REPLACEME
-->

* `headers` {Object|Array}
marco-ippolito marked this conversation as resolved.
Show resolved Hide resolved
* Returns: {http.ServerResponse}

Returns the response object.

Sets multiple header values for implicit headers.
`headers` may be an `Array` where the keys and values are in the same list.
It is _not_ a list of tuples. So, the even-numbered offsets are key values,
and the odd-numbered offsets are the associated values. The array is in the same
format as `request.rawHeaders`.

```js
response.setHeaders(['foo', 'bar', 'fizz', 'buzz']);
```

or

```js
response.setHeaders({
foo: 'bar',
fizz: 'buzz',
});
```

When headers have been set with [`response.setHeaders()`][], they will be merged
with any headers passed to [`response.writeHead()`][], with the headers passed
to [`response.writeHead()`][] given precedence.

```js
// Returns content-type = text/plain
const server = http.createServer((req, res) => {
res.setHeaders(['Content-Type', 'text/html', 'X-Foo', 'bar']);
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('ok');
});
```

### `response.setTimeout(msecs[, callback])`

<!-- YAML
Expand Down Expand Up @@ -3803,6 +3848,7 @@ Set the maximum number of idle HTTP parsers. **Default:** `1000`.
[`response.end()`]: #responseenddata-encoding-callback
[`response.getHeader()`]: #responsegetheadername
[`response.setHeader()`]: #responsesetheadername-value
[`response.setHeaders()`]: #responsesetheadersheaders
[`response.socket`]: #responsesocket
[`response.writableEnded`]: #responsewritableended
[`response.writableFinished`]: #responsewritablefinished
Expand Down
28 changes: 28 additions & 0 deletions lib/_http_outgoing.js
Original file line number Diff line number Diff line change
Expand Up @@ -673,6 +673,34 @@ OutgoingMessage.prototype.setHeader = function setHeader(name, value) {
return this;
};

OutgoingMessage.prototype.setHeaders = function setHeaders(obj) {
marco-ippolito marked this conversation as resolved.
Show resolved Hide resolved
if (this._header) {
throw new ERR_HTTP_HEADERS_SENT('set');
}

let k;
if (ArrayIsArray(obj)) {
if (obj.length % 2 !== 0) {
throw new ERR_INVALID_ARG_VALUE('headers', obj);
}

for (let n = 0; n < obj.length; n += 2) {
k = obj[n + 0];
if (k) this.setHeader(k, obj[n + 1]);
}
} else if (obj) {
marco-ippolito marked this conversation as resolved.
Show resolved Hide resolved
const keys = ObjectKeys(obj);
// Retain for(;;) loop for performance reasons
// Refs: https://github.com/nodejs/node/pull/30958
for (let i = 0; i < keys.length; i++) {
k = keys[i];
if (k) this.setHeader(k, obj[k]);
}
}

return this;
};

OutgoingMessage.prototype.appendHeader = function appendHeader(name, value) {
if (this._header) {
throw new ERR_HTTP_HEADERS_SENT('append');
Expand Down
25 changes: 1 addition & 24 deletions lib/_http_server.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
'use strict';

const {
ArrayIsArray,
Error,
MathMin,
ObjectKeys,
Expand Down Expand Up @@ -76,7 +75,6 @@ const {
ERR_HTTP_INVALID_STATUS_CODE,
ERR_HTTP_SOCKET_ENCODING,
ERR_INVALID_ARG_TYPE,
ERR_INVALID_ARG_VALUE,
ERR_INVALID_CHAR
} = codes;
const {
Expand Down Expand Up @@ -359,28 +357,7 @@ function writeHead(statusCode, reason, obj) {
let headers;
if (this[kOutHeaders]) {
// Slow-case: when progressive API and header fields are passed.
let k;
if (ArrayIsArray(obj)) {
if (obj.length % 2 !== 0) {
throw new ERR_INVALID_ARG_VALUE('headers', obj);
}

for (let n = 0; n < obj.length; n += 2) {
k = obj[n + 0];
if (k) this.setHeader(k, obj[n + 1]);
}
} else if (obj) {
const keys = ObjectKeys(obj);
// Retain for(;;) loop for performance reasons
// Refs: https://github.com/nodejs/node/pull/30958
for (let i = 0; i < keys.length; i++) {
k = keys[i];
if (k) this.setHeader(k, obj[k]);
}
}
if (k === undefined && this._header) {
ShogunPanda marked this conversation as resolved.
Show resolved Hide resolved
throw new ERR_HTTP_HEADERS_SENT('render');
}
this.setHeaders(obj);
marco-ippolito marked this conversation as resolved.
Show resolved Hide resolved
// Only progressive api is used
headers = this[kOutHeaders];
} else {
Expand Down
69 changes: 69 additions & 0 deletions test/parallel/test-http-response-setheaders.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
'use strict';
const common = require('../common');
const http = require('http');
const assert = require('assert');


{
const server = http.createServer({ requireHostHeader: false }, common.mustCall((req, res) => {
res.setHeaders(['foo', '1', 'bar', '2' ]);
res.writeHead(200);
res.end();
}));

server.listen(0, common.mustCall(() => {
http.get({ port: server.address().port, headers: [] }, (res) => {
assert.strictEqual(res.statusCode, 200);
assert.strictEqual(res.headers.foo, '1');
assert.strictEqual(res.headers.bar, '2');
res.resume().on('end', common.mustCall(() => {
server.close();
}));
});
}));
}

{
const server = http.createServer({ requireHostHeader: false }, common.mustCall((req, res) => {
res.setHeaders({
foo: '1',
bar: '2'
});
res.writeHead(200);
res.end();
}));

server.listen(0, common.mustCall(() => {
http.get({ port: server.address().port }, (res) => {
assert.strictEqual(res.statusCode, 200);
assert.strictEqual(res.headers.foo, '1');
assert.strictEqual(res.headers.bar, '2');
res.resume().on('end', common.mustCall(() => {
server.close();
}));
});
}));
}

{
const server = http.createServer({ requireHostHeader: false }, common.mustCall((req, res) => {
res.writeHead(200); // headers already sent
assert.throws(() => {
res.setHeaders({
foo: 'bar',
});
}, {
code: 'ERR_HTTP_HEADERS_SENT'
});
res.end();
}));

server.listen(0, common.mustCall(() => {
http.get({ port: server.address().port }, (res) => {
assert.strictEqual(res.headers.foo, undefined);
res.resume().on('end', common.mustCall(() => {
server.close();
}));
});
}));
}