Skip to content

Commit

Permalink
http: res.setHeaders first implementation
Browse files Browse the repository at this point in the history
PR-URL: #46109
Backport-PR-URL: #46365
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Paolo Insogna <paolo@cowtech.it>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
  • Loading branch information
marco-ippolito authored and juanarbol committed Mar 5, 2023
1 parent 3ea53c5 commit 5019b54
Show file tree
Hide file tree
Showing 3 changed files with 197 additions and 0 deletions.
44 changes: 44 additions & 0 deletions doc/api/http.md
Expand Up @@ -2950,6 +2950,48 @@ Sets a single header value. If the header already exists in the to-be-sent
headers, its value will be replaced. Use an array of strings to send multiple
headers with the same name.

### `outgoingMessage.setHeaders(headers)`

<!-- YAML
added: REPLACEME
-->

* `headers` {Headers|Map}
* Returns: {http.ServerResponse}

Returns the response object.

Sets multiple header values for implicit headers.
`headers` must be an instance of [`Headers`][] or `Map`,
if a header already exists in the to-be-sent headers,
its value will be replaced.

```js
const headers = new Headers({ foo: 'bar' });
response.setHeaders(headers);
```

or

```js
const headers = new Map([['foo', 'bar']]);
res.setHeaders(headers);
```

When headers have been set with [`outgoingMessage.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) => {
const headers = new Headers({ 'Content-Type': 'text/html' });
res.setHeaders(headers);
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('ok');
});
```

### `outgoingMessage.setTimeout(msesc[, callback])`

<!-- YAML
Expand Down Expand Up @@ -3721,6 +3763,7 @@ Set the maximum number of idle HTTP parsers.
[`Buffer.byteLength()`]: buffer.md#static-method-bufferbytelengthstring-encoding
[`Duplex`]: stream.md#class-streamduplex
[`HPE_HEADER_OVERFLOW`]: errors.md#hpe_header_overflow
[`Headers`]: globals.md#class-headers
[`TypeError`]: errors.md#class-typeerror
[`URL`]: url.md#the-whatwg-url-api
[`agent.createConnection()`]: #agentcreateconnectionoptions-callback
Expand All @@ -3747,6 +3790,7 @@ Set the maximum number of idle HTTP parsers.
[`net.createConnection()`]: net.md#netcreateconnectionoptions-connectlistener
[`new URL()`]: url.md#new-urlinput-base
[`outgoingMessage.setHeader(name, value)`]: #outgoingmessagesetheadername-value
[`outgoingMessage.setHeaders()`]: #outgoingmessagesetheadersheaders
[`outgoingMessage.socket`]: #outgoingmessagesocket
[`removeHeader(name)`]: #requestremoveheadername
[`request.destroy()`]: #requestdestroyerror
Expand Down
22 changes: 22 additions & 0 deletions lib/_http_outgoing.js
Expand Up @@ -673,6 +673,28 @@ OutgoingMessage.prototype.setHeader = function setHeader(name, value) {
return this;
};

OutgoingMessage.prototype.setHeaders = function setHeaders(headers) {
if (this._header) {
throw new ERR_HTTP_HEADERS_SENT('set');
}


if (
!headers ||
ArrayIsArray(headers) ||
typeof headers.keys !== 'function' ||
typeof headers.get !== 'function'
) {
throw new ERR_INVALID_ARG_TYPE('headers', ['Headers', 'Map'], headers);
}

for (const key of headers.keys()) {
this.setHeader(key, headers.get(key));
}

return this;
};

OutgoingMessage.prototype.appendHeader = function appendHeader(name, value) {
if (this._header) {
throw new ERR_HTTP_HEADERS_SENT('append');
Expand Down
131 changes: 131 additions & 0 deletions test/parallel/test-http-response-setheaders.js
@@ -0,0 +1,131 @@
'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.writeHead(200); // Headers already sent
const headers = new globalThis.Headers({ foo: '1' });
assert.throws(() => {
res.setHeaders(headers);
}, {
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();
}));
});
}));
}

{
const server = http.createServer({ requireHostHeader: false }, common.mustCall((req, res) => {
assert.throws(() => {
res.setHeaders(['foo', '1']);
}, {
code: 'ERR_INVALID_ARG_TYPE'
});
assert.throws(() => {
res.setHeaders({ foo: '1' });
}, {
code: 'ERR_INVALID_ARG_TYPE'
});
assert.throws(() => {
res.setHeaders(null);
}, {
code: 'ERR_INVALID_ARG_TYPE'
});
assert.throws(() => {
res.setHeaders(undefined);
}, {
code: 'ERR_INVALID_ARG_TYPE'
});
assert.throws(() => {
res.setHeaders('test');
}, {
code: 'ERR_INVALID_ARG_TYPE'
});
assert.throws(() => {
res.setHeaders(1);
}, {
code: 'ERR_INVALID_ARG_TYPE'
});
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();
}));
});
}));
}

{
const server = http.createServer({ requireHostHeader: false }, common.mustCall((req, res) => {
const headers = new globalThis.Headers({ foo: '1', bar: '2' });
res.setHeaders(headers);
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) => {
const headers = new globalThis.Headers({ foo: '1', bar: '2' });
res.setHeaders(headers);
res.writeHead(200, ['foo', '3']);
res.end();
}));

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

{
const server = http.createServer({ requireHostHeader: false }, common.mustCall((req, res) => {
const headers = new Map([['foo', '1'], ['bar', '2']]);
res.setHeaders(headers);
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();
}));
});
}));
}

0 comments on commit 5019b54

Please sign in to comment.