Skip to content

Commit 50a4471

Browse files
mscdexMyles Borins
authored andcommitted
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 9389572 commit 50a4471

File tree

3 files changed

+65
-47
lines changed

3 files changed

+65
-47
lines changed

‎lib/_http_common.js‎

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -78,17 +78,11 @@ function parserOnHeadersComplete(versionMajor, versionMinor, headers, method,
7878
parser.incoming.statusMessage=statusMessage;
7979
}
8080

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

‎lib/_http_outgoing.js‎

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,18 @@ const Buffer = require('buffer').Buffer;
99
constcommon=require('_http_common');
1010

1111
constCRLF=common.CRLF;
12-
constchunkExpression=common.chunkExpression;
12+
consttrfrEncChunkExpression=common.chunkExpression;
1313
constdebug=common.debug;
1414

15-
constconnectionExpression=/^Connection$/i;
15+
constupgradeExpression=/^Upgrade$/i;
1616
consttransferEncodingExpression=/^Transfer-Encoding$/i;
17-
constcloseExpression=/close/i;
1817
constcontentLengthExpression=/^Content-Length$/i;
1918
constdateExpression=/^Date$/i;
2019
constexpectExpression=/^Expect$/i;
2120
consttrailerExpression=/^Trailer$/i;
21+
constconnectionExpression=/^Connection$/i;
22+
constconnCloseExpression=/(^|\W)close(\W|$)/i;
23+
constconnUpgradeExpression=/(^|\W)upgrade(\W|$)/i;
2224
constlenientHttpHeaders=!!process.REVERT_CVE_2016_2216;
2325

2426
constautomaticHeaders={
@@ -62,6 +64,7 @@ function OutgoingMessage(){
6264
this.writable=true;
6365

6466
this._last=false;
67+
this.upgrading=false;
6568
this.chunkedEncoding=false;
6669
this.shouldKeepAlive=true;
6770
this.useChunkedEncodingByDefault=true;
@@ -191,11 +194,13 @@ OutgoingMessage.prototype._storeHeader = function(firstLine, headers){
191194
// in the case of response it is: 'HTTP/1.1 200 OK\r\n'
192195
varstate={
193196
sentConnectionHeader: false,
197+
sentConnectionUpgrade: false,
194198
sentContentLengthHeader: false,
195199
sentTransferEncodingHeader: false,
196200
sentDateHeader: false,
197201
sentExpect: false,
198202
sentTrailer: false,
203+
sentUpgrade: false,
199204
messageHeader: firstLine
200205
};
201206

@@ -224,6 +229,10 @@ OutgoingMessage.prototype._storeHeader = function(firstLine, headers){
224229
}
225230
}
226231

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

314323
if(connectionExpression.test(field)){
315324
state.sentConnectionHeader=true;
316-
if(closeExpression.test(value)){
325+
if(connCloseExpression.test(value)){
317326
self._last=true;
318327
}else{
319328
self.shouldKeepAlive=true;
320329
}
321-
330+
if(connUpgradeExpression.test(value))
331+
state.sentConnectionUpgrade=true;
322332
}elseif(transferEncodingExpression.test(field)){
323333
state.sentTransferEncodingHeader=true;
324-
if(chunkExpression.test(value))self.chunkedEncoding=true;
334+
if(trfrEncChunkExpression.test(value))self.chunkedEncoding=true;
325335

326336
}elseif(contentLengthExpression.test(field)){
327337
state.sentContentLengthHeader=true;
@@ -331,6 +341,8 @@ function storeHeader(self, state, field, value){
331341
state.sentExpect=true;
332342
}elseif(trailerExpression.test(field)){
333343
state.sentTrailer=true;
344+
}elseif(upgradeExpression.test(field)){
345+
state.sentUpgrade=true;
334346
}
335347
}
336348

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

Lines changed: 42 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -25,40 +25,52 @@ var srv = net.createServer(function(c){
2525
});
2626
});
2727

28-
vargotUpgrade=false;
29-
30-
srv.listen(0,'127.0.0.1',function(){
31-
32-
varreq=http.get({
33-
port: this.address().port,
34-
headers: {
28+
srv.listen(0,'127.0.0.1',common.mustCall(function(){
29+
varport=this.address().port;
30+
varheaders=[
31+
{
3532
connection: 'upgrade',
3633
upgrade: 'websocket'
37-
}
38-
});
39-
req.on('upgrade',function(res,socket,upgradeHead){
40-
varrecvData=upgradeHead;
41-
socket.on('data',function(d){
42-
recvData+=d;
34+
},
35+
[
36+
['Host','echo.websocket.org'],
37+
['Connection','Upgrade'],
38+
['Upgrade','websocket'],
39+
['Origin','http://www.websocket.org']
40+
]
41+
];
42+
varleft=headers.length;
43+
headers.forEach(function(h){
44+
varreq=http.get({
45+
port: port,
46+
headers: h
4347
});
48+
varsawUpgrade=false;
49+
req.on('upgrade',common.mustCall(function(res,socket,upgradeHead){
50+
sawUpgrade=true;
51+
varrecvData=upgradeHead;
52+
socket.on('data',function(d){
53+
recvData+=d;
54+
});
4455

45-
socket.on('close',common.mustCall(function(){
46-
assert.equal(recvData,'nurtzo');
47-
}));
48-
49-
console.log(res.headers);
50-
varexpectedHeaders={'hello': 'world',
51-
'connection': 'upgrade',
52-
'upgrade': 'websocket'};
53-
assert.deepEqual(expectedHeaders,res.headers);
56+
socket.on('close',common.mustCall(function(){
57+
assert.equal(recvData,'nurtzo');
58+
}));
5459

55-
socket.end();
56-
srv.close();
60+
console.log(res.headers);
61+
varexpectedHeaders={
62+
hello: 'world',
63+
connection: 'upgrade',
64+
upgrade: 'websocket'
65+
};
66+
assert.deepStrictEqual(expectedHeaders,res.headers);
5767

58-
gotUpgrade=true;
68+
socket.end();
69+
if(--left==0)
70+
srv.close();
71+
}));
72+
req.on('close',common.mustCall(function(){
73+
assert.strictEqual(sawUpgrade,true);
74+
}));
5975
});
60-
});
61-
62-
process.on('exit',function(){
63-
assert.ok(gotUpgrade);
64-
});
76+
}));

0 commit comments

Comments
(0)