Skip to content

Commit 411d813

Browse files
indutnyrvagg
authored andcommitted
http: do not emit upgrade on advertisement
Do not emit `upgrade` if the server is just advertising its protocols support as per RFC 7230 Section 6.7. A server MAY send an Upgrade header field in any other response to advertise that it implements support for upgrading to the listed protocols, in order of descending preference, when appropriate for a future request. Fix: #4334 PR-URL: #4337 Reviewed-By: Ben Noordhuis <[email protected]>
1 parent 6d8eba0 commit 411d813

5 files changed

+82
-1
lines changed

lib/_http_client.js

+1
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,7 @@ function tickOnSocket(req, socket) {
471471
parser.reinitialize(HTTPParser.RESPONSE);
472472
parser.socket = socket;
473473
parser.incoming = null;
474+
parser.outgoing = req;
474475
req.parser = parser;
475476

476477
socket.parser = parser;

lib/_http_common.js

+19
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,20 @@ 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))) {
92+
upgrade = false;
93+
}
94+
8195
parser.incoming.upgrade = upgrade;
8296

8397
var skipBody = false; // response to HEAD or CONNECT
@@ -143,6 +157,10 @@ var parsers = new FreeList('parsers', 1000, function() {
143157
parser._url = '';
144158
parser._consumed = false;
145159

160+
parser.socket = null;
161+
parser.incoming = null;
162+
parser.outgoing = null;
163+
146164
// Only called in the slow case where slow means
147165
// that the request headers were either fragmented
148166
// across multiple TCP packets or too large to be
@@ -177,6 +195,7 @@ function freeParser(parser, req, socket) {
177195
parser.socket.parser = null;
178196
parser.socket = null;
179197
parser.incoming = null;
198+
parser.outgoing = null;
180199
parser[kOnExecute] = null;
181200
if (parsers.free(parser) === false)
182201
parser.close();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const assert = require('assert');
5+
const http = require('http');
6+
7+
const tests = [
8+
{ headers: {}, expected: 'regular' },
9+
{ headers: { upgrade: 'h2c' }, expected: 'regular' },
10+
{ headers: { connection: 'upgrade' }, expected: 'regular' },
11+
{ headers: { connection: 'upgrade', upgrade: 'h2c' }, expected: 'upgrade' }
12+
];
13+
14+
function fire() {
15+
if (tests.length === 0)
16+
return server.close();
17+
18+
const test = tests.shift();
19+
20+
const done = common.mustCall(function done(result) {
21+
assert.equal(result, test.expected);
22+
23+
fire();
24+
});
25+
26+
const req = http.request({
27+
port: common.PORT,
28+
path: '/',
29+
headers: test.headers
30+
}, function onResponse(res) {
31+
res.resume();
32+
done('regular');
33+
});
34+
35+
req.on('upgrade', function onUpgrade(res, socket) {
36+
socket.destroy();
37+
done('upgrade');
38+
});
39+
40+
req.end();
41+
}
42+
43+
const server = http.createServer(function(req, res) {
44+
res.writeHead(200, {
45+
Connection: 'upgrade, keep-alive',
46+
Upgrade: 'h2c'
47+
});
48+
res.end('hello world');
49+
}).on('upgrade', function(req, socket) {
50+
socket.end('HTTP/1.1 101 Switching protocols\r\n' +
51+
'Connection: upgrade\r\n' +
52+
'Upgrade: h2c\r\n\r\n' +
53+
'ohai');
54+
}).listen(common.PORT, fire);

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

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ srv.listen(common.PORT, '127.0.0.1', function() {
3636
port: common.PORT,
3737
host: '127.0.0.1',
3838
headers: {
39+
'connection': 'upgrade',
3940
'upgrade': 'websocket'
4041
}
4142
};

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

+7-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,13 @@ var gotUpgrade = false;
3232

3333
srv.listen(common.PORT, '127.0.0.1', function() {
3434

35-
var req = http.get({ port: common.PORT });
35+
var req = http.get({
36+
port: common.PORT,
37+
headers: {
38+
connection: 'upgrade',
39+
upgrade: 'websocket'
40+
}
41+
});
3642
req.on('upgrade', function(res, socket, upgradeHead) {
3743
var recvData = upgradeHead;
3844
socket.on('data', function(d) {

0 commit comments

Comments
 (0)