Skip to content

Commit 3e6f103

Browse files
committed
http: add new functions to OutgoingMessage
PR-URL: #10805 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Roman Reiss <me@silverwind.io> Reviewed-By: Sakthipriyan Vairamani <thechargingvolcano@gmail.com> Reviewed-By: Sam Roberts <vieuxtech@gmail.com>
1 parent 52ddb41 commit 3e6f103

File tree

3 files changed

+140
-1
lines changed

3 files changed

+140
-1
lines changed

doc/api/http.md

+60
Original file line numberDiff line numberDiff line change
@@ -934,6 +934,66 @@ Example:
934934
var contentType = response.getHeader('content-type');
935935
```
936936

937+
### response.getHeaderNames()
938+
<!-- YAML
939+
added: REPLACEME
940+
-->
941+
942+
* Returns: {Array}
943+
944+
Returns an array containing the unique names of the current outgoing headers.
945+
All header names are lowercase.
946+
947+
Example:
948+
949+
```js
950+
response.setHeader('Foo', 'bar');
951+
response.setHeader('Set-Cookie', ['foo=bar', 'bar=baz']);
952+
953+
var headerNames = response.getHeaderNames();
954+
// headerNames === ['foo', 'set-cookie']
955+
```
956+
957+
### response.getHeaders()
958+
<!-- YAML
959+
added: REPLACEME
960+
-->
961+
962+
* Returns: {Object}
963+
964+
Returns a shallow copy of the current outgoing headers. Since a shallow copy
965+
is used, array values may be mutated without additional calls to various
966+
header-related http module methods. The keys of the returned object are the
967+
header names and the values are the respective header values. All header names
968+
are lowercase.
969+
970+
Example:
971+
972+
```js
973+
response.setHeader('Foo', 'bar');
974+
response.setHeader('Set-Cookie', ['foo=bar', 'bar=baz']);
975+
976+
var headers = response.getHeaders();
977+
// headers === { foo: 'bar', 'set-cookie': ['foo=bar', 'bar=baz'] }
978+
```
979+
980+
### response.hasHeader(name)
981+
<!-- YAML
982+
added: REPLACEME
983+
-->
984+
985+
* `name` {String}
986+
* Returns: {Boolean}
987+
988+
Returns `true` if the header identified by `name` is currently set in the
989+
outgoing headers. Note that the header name matching is case-insensitive.
990+
991+
Example:
992+
993+
```js
994+
var hasContentType = response.hasHeader('content-type');
995+
```
996+
937997
### response.headersSent
938998
<!-- YAML
939999
added: v0.9.3

lib/_http_outgoing.js

+35
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ var RE_FIELDS = new RegExp('^(?:Connection|Transfer-Encoding|Content-Length|' +
1818
var RE_CONN_VALUES = /(?:^|\W)close|upgrade(?:$|\W)/ig;
1919
var RE_TE_CHUNKED = common.chunkExpression;
2020

21+
// Used to store headers returned by getHeaders()
22+
function OutgoingHeaders() {}
23+
OutgoingHeaders.prototype = Object.create(null);
24+
2125
var dateCache;
2226
function utcDate() {
2327
if (!dateCache) {
@@ -426,6 +430,37 @@ OutgoingMessage.prototype.getHeader = function getHeader(name) {
426430
};
427431

428432

433+
// Returns an array of the names of the current outgoing headers.
434+
OutgoingMessage.prototype.getHeaderNames = function getHeaderNames() {
435+
return (this._headers ? Object.keys(this._headers) : []);
436+
};
437+
438+
439+
// Returns a shallow copy of the current outgoing headers.
440+
OutgoingMessage.prototype.getHeaders = function getHeaders() {
441+
const headers = this._headers;
442+
const ret = new OutgoingHeaders();
443+
if (headers) {
444+
const keys = Object.keys(headers);
445+
for (var i = 0; i < keys.length; ++i) {
446+
const key = keys[i];
447+
const val = headers[key][1];
448+
ret[key] = val;
449+
}
450+
}
451+
return ret;
452+
};
453+
454+
455+
OutgoingMessage.prototype.hasHeader = function hasHeader(name) {
456+
if (typeof name !== 'string') {
457+
throw new TypeError('"name" argument must be a string');
458+
}
459+
460+
return !!(this._headers && this._headers[name.toLowerCase()]);
461+
};
462+
463+
429464
OutgoingMessage.prototype.removeHeader = function removeHeader(name) {
430465
if (typeof name !== 'string') {
431466
throw new TypeError('"name" argument must be a string');

test/parallel/test-http-mutable-headers.js

+45-1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@ const cookies = [
2121
const s = http.createServer(common.mustCall((req, res) => {
2222
switch (test) {
2323
case 'headers':
24+
// Check that header-related functions work before setting any headers
25+
// eslint-disable-next-line no-restricted-properties
26+
assert.deepEqual(res.getHeaders(), {});
27+
assert.deepStrictEqual(res.getHeaderNames(), []);
28+
assert.deepStrictEqual(res.hasHeader('Connection'), false);
29+
assert.deepStrictEqual(res.getHeader('Connection'), undefined);
30+
2431
assert.throws(() => {
2532
res.setHeader();
2633
}, /^TypeError: Header name must be a valid HTTP Token \["undefined"\]$/);
@@ -34,15 +41,52 @@ const s = http.createServer(common.mustCall((req, res) => {
3441
res.removeHeader();
3542
}, /^TypeError: "name" argument must be a string$/);
3643

44+
const arrayValues = [1, 2, 3];
3745
res.setHeader('x-test-header', 'testing');
3846
res.setHeader('X-TEST-HEADER2', 'testing');
3947
res.setHeader('set-cookie', cookies);
40-
res.setHeader('x-test-array-header', [1, 2, 3]);
48+
res.setHeader('x-test-array-header', arrayValues);
4149

4250
assert.strictEqual(res.getHeader('x-test-header'), 'testing');
4351
assert.strictEqual(res.getHeader('x-test-header2'), 'testing');
4452

53+
const headersCopy = res.getHeaders();
54+
// eslint-disable-next-line no-restricted-properties
55+
assert.deepEqual(headersCopy, {
56+
'x-test-header': 'testing',
57+
'x-test-header2': 'testing',
58+
'set-cookie': cookies,
59+
'x-test-array-header': arrayValues
60+
});
61+
// eslint-disable-next-line no-restricted-properties
62+
assert.deepEqual(headersCopy['set-cookie'], cookies);
63+
assert.strictEqual(headersCopy['x-test-array-header'], arrayValues);
64+
65+
assert.deepStrictEqual(res.getHeaderNames(),
66+
['x-test-header', 'x-test-header2',
67+
'set-cookie', 'x-test-array-header']);
68+
69+
assert.strictEqual(res.hasHeader('x-test-header2'), true);
70+
assert.strictEqual(res.hasHeader('X-TEST-HEADER2'), true);
71+
assert.strictEqual(res.hasHeader('X-Test-Header2'), true);
72+
assert.throws(() => {
73+
res.hasHeader();
74+
}, /^TypeError: "name" argument must be a string$/);
75+
assert.throws(() => {
76+
res.hasHeader(null);
77+
}, /^TypeError: "name" argument must be a string$/);
78+
assert.throws(() => {
79+
res.hasHeader(true);
80+
}, /^TypeError: "name" argument must be a string$/);
81+
assert.throws(() => {
82+
res.hasHeader({ toString: () => 'X-TEST-HEADER2' });
83+
}, /^TypeError: "name" argument must be a string$/);
84+
4585
res.removeHeader('x-test-header2');
86+
87+
assert.strictEqual(res.hasHeader('x-test-header2'), false);
88+
assert.strictEqual(res.hasHeader('X-TEST-HEADER2'), false);
89+
assert.strictEqual(res.hasHeader('X-Test-Header2'), false);
4690
break;
4791

4892
case 'contentLength':

0 commit comments

Comments
 (0)