Skip to content

Commit 32ac376

Browse files
committed
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 0b43c08 commit 32ac376

5 files changed

+82
-1
lines changed

lib/_http_client.js

+1
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,7 @@ function tickOnSocket(req, socket) {
466466
parser.reinitialize(HTTPParser.RESPONSE);
467467
parser.socket = socket;
468468
parser.incoming = null;
469+
parser.outgoing = req;
469470
req.parser = parser;
470471

471472
socket.parser = parser;

lib/_http_common.js

+19
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,20 @@ function parserOnHeadersComplete(versionMajor, versionMinor, headers, method,
7777
parser.incoming.statusMessage = statusMessage;
7878
}
7979

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

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

159+
parser.socket = null;
160+
parser.incoming = null;
161+
parser.outgoing = null;
162+
145163
// Only called in the slow case where slow means
146164
// that the request headers were either fragmented
147165
// across multiple TCP packets or too large to be
@@ -175,6 +193,7 @@ function freeParser(parser, req, socket) {
175193
parser.socket.parser = null;
176194
parser.socket = null;
177195
parser.incoming = null;
196+
parser.outgoing = null;
178197
if (parsers.free(parser) === false)
179198
parser.close();
180199
parser = null;
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
// XXX: This test isn't fantastic, as it assumes that the entire response
3844
// from the server will arrive in a single data callback

0 commit comments

Comments
 (0)