Skip to content

Commit 1050594

Browse files
committed
http: fix connection upgrade checks
This commit fixes connection upgrade checks, specifically when headers are passed as an array instead of a plain object to http.request() Fixes: #8235 PR-URL: #8238 Reviewed-By: James M Snell <[email protected]>
1 parent 7053922 commit 1050594

File tree

3 files changed

+64
-38
lines changed

3 files changed

+64
-38
lines changed

lib/_http_common.js

+5-11
Original file line numberDiff line numberDiff line change
@@ -80,17 +80,11 @@ function parserOnHeadersComplete(versionMajor, versionMinor, headers, method,
8080
parser.incoming.statusMessage = statusMessage;
8181
}
8282

83-
// The client made non-upgrade request, and server is just advertising
84-
// supported protocols.
85-
//
86-
// See RFC7230 Section 6.7
87-
//
88-
// NOTE: RegExp below matches `upgrade` in `Connection: abc, upgrade, def`
89-
// header.
90-
if (upgrade &&
91-
parser.outgoing !== null &&
92-
(parser.outgoing._headers.upgrade === undefined ||
93-
!/(^|\W)upgrade(\W|$)/i.test(parser.outgoing._headers.connection))) {
83+
if (upgrade && parser.outgoing !== null && !parser.outgoing.upgrading) {
84+
// The client made non-upgrade request, and server is just advertising
85+
// supported protocols.
86+
//
87+
// See RFC7230 Section 6.7
9488
upgrade = false;
9589
}
9690

lib/_http_outgoing.js

+18-6
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,18 @@ const Buffer = require('buffer').Buffer;
99
const common = require('_http_common');
1010

1111
const CRLF = common.CRLF;
12-
const chunkExpression = common.chunkExpression;
12+
const trfrEncChunkExpression = common.chunkExpression;
1313
const debug = common.debug;
1414

15-
const connectionExpression = /^Connection$/i;
15+
const upgradeExpression = /^Upgrade$/i;
1616
const transferEncodingExpression = /^Transfer-Encoding$/i;
17-
const closeExpression = /close/i;
1817
const contentLengthExpression = /^Content-Length$/i;
1918
const dateExpression = /^Date$/i;
2019
const expectExpression = /^Expect$/i;
2120
const trailerExpression = /^Trailer$/i;
21+
const connectionExpression = /^Connection$/i;
22+
const connCloseExpression = /(^|\W)close(\W|$)/i;
23+
const connUpgradeExpression = /(^|\W)upgrade(\W|$)/i;
2224

2325
const automaticHeaders = {
2426
connection: true,
@@ -61,6 +63,7 @@ function OutgoingMessage() {
6163
this.writable = true;
6264

6365
this._last = false;
66+
this.upgrading = false;
6467
this.chunkedEncoding = false;
6568
this.shouldKeepAlive = true;
6669
this.useChunkedEncodingByDefault = true;
@@ -192,11 +195,13 @@ OutgoingMessage.prototype._storeHeader = function(firstLine, headers) {
192195
// in the case of response it is: 'HTTP/1.1 200 OK\r\n'
193196
var state = {
194197
sentConnectionHeader: false,
198+
sentConnectionUpgrade: false,
195199
sentContentLengthHeader: false,
196200
sentTransferEncodingHeader: false,
197201
sentDateHeader: false,
198202
sentExpect: false,
199203
sentTrailer: false,
204+
sentUpgrade: false,
200205
messageHeader: firstLine
201206
};
202207

@@ -225,6 +230,10 @@ OutgoingMessage.prototype._storeHeader = function(firstLine, headers) {
225230
}
226231
}
227232

233+
// Are we upgrading the connection?
234+
if (state.sentConnectionUpgrade && state.sentUpgrade)
235+
this.upgrading = true;
236+
228237
// Date header
229238
if (this.sendDate === true && state.sentDateHeader === false) {
230239
state.messageHeader += 'Date: ' + utcDate() + CRLF;
@@ -312,15 +321,16 @@ function storeHeader(self, state, field, value) {
312321

313322
if (connectionExpression.test(field)) {
314323
state.sentConnectionHeader = true;
315-
if (closeExpression.test(value)) {
324+
if (connCloseExpression.test(value)) {
316325
self._last = true;
317326
} else {
318327
self.shouldKeepAlive = true;
319328
}
320-
329+
if (connUpgradeExpression.test(value))
330+
state.sentConnectionUpgrade = true;
321331
} else if (transferEncodingExpression.test(field)) {
322332
state.sentTransferEncodingHeader = true;
323-
if (chunkExpression.test(value)) self.chunkedEncoding = true;
333+
if (trfrEncChunkExpression.test(value)) self.chunkedEncoding = true;
324334

325335
} else if (contentLengthExpression.test(field)) {
326336
state.sentContentLengthHeader = true;
@@ -330,6 +340,8 @@ function storeHeader(self, state, field, value) {
330340
state.sentExpect = true;
331341
} else if (trailerExpression.test(field)) {
332342
state.sentTrailer = true;
343+
} else if (upgradeExpression.test(field)) {
344+
state.sentUpgrade = true;
333345
}
334346
}
335347

test/parallel/test-http-upgrade-client.js

+41-21
Original file line numberDiff line numberDiff line change
@@ -26,31 +26,51 @@ var srv = net.createServer(function(c) {
2626
});
2727

2828
srv.listen(0, '127.0.0.1', common.mustCall(function() {
29-
30-
var req = http.get({
31-
port: this.address().port,
32-
headers: {
29+
var port = this.address().port;
30+
var headers = [
31+
{
3332
connection: 'upgrade',
3433
upgrade: 'websocket'
35-
}
36-
});
37-
req.on('upgrade', common.mustCall(function(res, socket, upgradeHead) {
38-
var recvData = upgradeHead;
39-
socket.on('data', function(d) {
40-
recvData += d;
34+
},
35+
[
36+
['Host', 'echo.websocket.org'],
37+
['Connection', 'Upgrade'],
38+
['Upgrade', 'websocket'],
39+
['Origin', 'http://www.websocket.org']
40+
]
41+
];
42+
var left = headers.length;
43+
headers.forEach(function(h) {
44+
var req = http.get({
45+
port: port,
46+
headers: h
4147
});
48+
var sawUpgrade = false;
49+
req.on('upgrade', common.mustCall(function(res, socket, upgradeHead) {
50+
sawUpgrade = true;
51+
var recvData = upgradeHead;
52+
socket.on('data', function(d) {
53+
recvData += d;
54+
});
4255

43-
socket.on('close', common.mustCall(function() {
44-
assert.equal(recvData, 'nurtzo');
45-
}));
56+
socket.on('close', common.mustCall(function() {
57+
assert.equal(recvData, 'nurtzo');
58+
}));
4659

47-
console.log(res.headers);
48-
var expectedHeaders = {'hello': 'world',
49-
'connection': 'upgrade',
50-
'upgrade': 'websocket' };
51-
assert.deepStrictEqual(expectedHeaders, res.headers);
60+
console.log(res.headers);
61+
var expectedHeaders = {
62+
hello: 'world',
63+
connection: 'upgrade',
64+
upgrade: 'websocket'
65+
};
66+
assert.deepStrictEqual(expectedHeaders, res.headers);
5267

53-
socket.end();
54-
srv.close();
55-
}));
68+
socket.end();
69+
if (--left == 0)
70+
srv.close();
71+
}));
72+
req.on('close', common.mustCall(function() {
73+
assert.strictEqual(sawUpgrade, true);
74+
}));
75+
});
5676
}));

0 commit comments

Comments
 (0)