Skip to content
This repository was archived by the owner on Apr 22, 2023. It is now read-only.

Commit ec57ecc

Browse files
rlidwkaindutny
authored andcommitted
http: concatenate duplicate headers by default
1 parent 38a07a9 commit ec57ecc

File tree

2 files changed

+129
-29
lines changed

2 files changed

+129
-29
lines changed

lib/_http_incoming.js

+21-29
Original file line numberDiff line numberDiff line change
@@ -154,40 +154,32 @@ IncomingMessage.prototype._addHeaderLine = function(field, value, dest) {
154154
}
155155
break;
156156

157-
// Comma separate. Maybe make these arrays?
158-
case 'accept':
159-
case 'accept-charset':
160-
case 'accept-encoding':
161-
case 'accept-language':
162-
case 'connection':
163-
case 'cookie':
164-
case 'pragma':
165-
case 'link':
166-
case 'www-authenticate':
167-
case 'proxy-authenticate':
168-
case 'sec-websocket-extensions':
169-
case 'sec-websocket-protocol':
170-
if (!util.isUndefined(dest[field])) {
171-
dest[field] += ', ' + value;
172-
} else {
157+
// list is taken from:
158+
// https://mxr.mozilla.org/mozilla/source/netwerk/protocol/http/src/nsHttpHeaderArray.cpp
159+
case 'content-type':
160+
case 'content-length':
161+
case 'user-agent':
162+
case 'referer':
163+
case 'host':
164+
case 'authorization':
165+
case 'proxy-authorization':
166+
case 'if-modified-since':
167+
case 'if-unmodified-since':
168+
case 'from':
169+
case 'location':
170+
case 'max-forwards':
171+
// drop duplicates
172+
if (util.isUndefined(dest[field]))
173173
dest[field] = value;
174-
}
175174
break;
176175

177-
178176
default:
179-
if (field.slice(0, 2) == 'x-') {
180-
// except for x-
181-
if (!util.isUndefined(dest[field])) {
182-
dest[field] += ', ' + value;
183-
} else {
184-
dest[field] = value;
185-
}
186-
} else {
187-
// drop duplicates
188-
if (util.isUndefined(dest[field])) dest[field] = value;
177+
// make comma-separated list
178+
if (!util.isUndefined(dest[field]))
179+
dest[field] += ', ' + value;
180+
else {
181+
dest[field] = value;
189182
}
190-
break;
191183
}
192184
};
193185

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// Copyright Joyent, Inc. and other Node contributors.
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a
4+
// copy of this software and associated documentation files (the
5+
// "Software"), to deal in the Software without restriction, including
6+
// without limitation the rights to use, copy, modify, merge, publish,
7+
// distribute, sublicense, and/or sell copies of the Software, and to permit
8+
// persons to whom the Software is furnished to do so, subject to the
9+
// following conditions:
10+
//
11+
// The above copyright notice and this permission notice shall be included
12+
// in all copies or substantial portions of the Software.
13+
//
14+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15+
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16+
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17+
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18+
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19+
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20+
// USE OR OTHER DEALINGS IN THE SOFTWARE.
21+
22+
// Verify that the HTTP server implementation handles multiple instances
23+
// of the same header as per RFC2616: joining the handful of fields by ', '
24+
// that support it, and dropping duplicates for other fields.
25+
26+
var common = require('../common');
27+
var assert = require('assert');
28+
var http = require('http');
29+
30+
var multipleAllowed = [
31+
'Accept',
32+
'Accept-Charset',
33+
'Accept-Encoding',
34+
'Accept-Language',
35+
'Connection',
36+
'Cookie',
37+
'DAV', // GH-2750
38+
'Pragma', // GH-715
39+
'Link', // GH-1187
40+
'WWW-Authenticate', // GH-1083
41+
'Proxy-Authenticate', // GH-4052
42+
'Sec-Websocket-Extensions', // GH-2764
43+
'Sec-Websocket-Protocol', // GH-2764
44+
'Via', // GH-6660
45+
46+
// not a special case, just making sure it's parsed correctly
47+
'X-Forwarded-For',
48+
49+
// make sure that unspecified headers is treated as multiple
50+
'Some-Random-Header',
51+
'X-Some-Random-Header',
52+
];
53+
54+
var multipleForbidden = [
55+
'Content-Type',
56+
'User-Agent',
57+
'Referer',
58+
'Host',
59+
'Authorization',
60+
'Proxy-Authorization',
61+
'If-Modified-Since',
62+
'If-Unmodified-Since',
63+
'From',
64+
'Location',
65+
'Max-Forwards',
66+
67+
// special case, tested differently
68+
//'Content-Length',
69+
];
70+
71+
var srv = http.createServer(function(req, res) {
72+
multipleForbidden.forEach(function(header) {
73+
assert.equal(req.headers[header.toLowerCase()], 'foo', 'header parsed incorrectly: ' + header);
74+
});
75+
multipleAllowed.forEach(function(header) {
76+
assert.equal(req.headers[header.toLowerCase()], 'foo, bar', 'header parsed incorrectly: ' + header);
77+
});
78+
assert.equal(req.headers['content-length'], 0);
79+
80+
res.writeHead(200, {'Content-Type' : 'text/plain'});
81+
res.end('EOF');
82+
83+
srv.close();
84+
});
85+
86+
function makeHeader(value) {
87+
return function(header) {
88+
return [header, value];
89+
}
90+
}
91+
92+
var headers = []
93+
.concat(multipleAllowed.map(makeHeader('foo')))
94+
.concat(multipleForbidden.map(makeHeader('foo')))
95+
.concat(multipleAllowed.map(makeHeader('bar')))
96+
.concat(multipleForbidden.map(makeHeader('bar')))
97+
// content-length is a special case since node.js
98+
// is dropping connetions with non-numeric headers
99+
.concat([['content-length', 0], ['content-length', 123]]);
100+
101+
srv.listen(common.PORT, function() {
102+
http.get({
103+
host: 'localhost',
104+
port: common.PORT,
105+
path: '/',
106+
headers: headers,
107+
});
108+
});

0 commit comments

Comments
 (0)