Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit de4600e

Browse files
bnoordhuisBridgeAR
authored andcommittedJan 21, 2018
http: fix parsing of binary upgrade response body
Fix a bug where a connection upgrade response with a Transfer-Encoding header and a body whose first byte is > 127 causes said byte to be dropped on the floor when passing the remainder of the message to the 'upgrade' event listeners. Fixes: #17789 PR-URL: #17806 Fixes: #17789 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Luigi Pinca <[email protected]> Reviewed-By: Tiancheng "Timothy" Gu <[email protected]> Reviewed-By: Richard Lau <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]>
1 parent e1c29f2 commit de4600e

File tree

4 files changed

+41
-28
lines changed

4 files changed

+41
-28
lines changed
 

‎lib/_http_client.js

+9-15
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,6 @@ function parserOnIncomingClient(res, shouldKeepAlive) {
458458
var socket = this.socket;
459459
var req = socket._httpMessage;
460460

461-
462461
// propagate "domain" setting...
463462
if (req.domain && !res.domain) {
464463
debug('setting "res.domain"');
@@ -471,29 +470,22 @@ function parserOnIncomingClient(res, shouldKeepAlive) {
471470
// We already have a response object, this means the server
472471
// sent a double response.
473472
socket.destroy();
474-
return;
473+
return 0; // No special treatment.
475474
}
476475
req.res = res;
477476

478477
// Responses to CONNECT request is handled as Upgrade.
479-
if (req.method === 'CONNECT') {
478+
const method = req.method;
479+
if (method === 'CONNECT') {
480480
res.upgrade = true;
481-
return 2; // skip body, and the rest
481+
return 2; // Skip body and treat as Upgrade.
482482
}
483483

484-
// Responses to HEAD requests are crazy.
485-
// HEAD responses aren't allowed to have an entity-body
486-
// but *can* have a content-length which actually corresponds
487-
// to the content-length of the entity-body had the request
488-
// been a GET.
489-
var isHeadResponse = req.method === 'HEAD';
490-
debug('AGENT isHeadResponse', isHeadResponse);
491-
492484
if (res.statusCode === 100) {
493485
// restart the parser, as this is a continue message.
494486
req.res = null; // Clear res so that we don't hit double-responses.
495487
req.emit('continue');
496-
return true;
488+
return 1; // Skip body but don't treat as Upgrade.
497489
}
498490

499491
if (req.shouldKeepAlive && !shouldKeepAlive && !req.upgradeOrConnect) {
@@ -503,7 +495,6 @@ function parserOnIncomingClient(res, shouldKeepAlive) {
503495
req.shouldKeepAlive = false;
504496
}
505497

506-
507498
DTRACE_HTTP_CLIENT_RESPONSE(socket, req);
508499
LTTNG_HTTP_CLIENT_RESPONSE(socket, req);
509500
COUNTER_HTTP_CLIENT_RESPONSE();
@@ -521,7 +512,10 @@ function parserOnIncomingClient(res, shouldKeepAlive) {
521512
if (!handled)
522513
res._dump();
523514

524-
return isHeadResponse;
515+
if (method === 'HEAD')
516+
return 1; // Skip body but don't treat as Upgrade.
517+
518+
return 0; // No special treatment.
525519
}
526520

527521
// client

‎lib/_http_common.js

+3-12
Original file line numberDiff line numberDiff line change
@@ -106,19 +106,10 @@ function parserOnHeadersComplete(versionMajor, versionMinor, headers, method,
106106

107107
parser.incoming.upgrade = upgrade;
108108

109-
var skipBody = 0; // response to HEAD or CONNECT
109+
if (upgrade)
110+
return 2; // Skip body and treat as Upgrade.
110111

111-
if (!upgrade) {
112-
// For upgraded connections and CONNECT method request, we'll emit this
113-
// after parser.execute so that we can capture the first part of the new
114-
// protocol.
115-
skipBody = parser.onIncoming(parser.incoming, shouldKeepAlive);
116-
}
117-
118-
if (typeof skipBody !== 'number')
119-
return skipBody ? 1 : 0;
120-
else
121-
return skipBody;
112+
return parser.onIncoming(parser.incoming, shouldKeepAlive);
122113
}
123114

124115
// XXX This is a mess.

‎lib/_http_server.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -627,7 +627,7 @@ function parserOnIncoming(server, socket, state, req, keepAlive) {
627627
} else {
628628
server.emit('request', req, res);
629629
}
630-
return false; // Not a HEAD response. (Not even a response!)
630+
return 0; // No special treatment.
631631
}
632632

633633
function resetSocketTimeout(server, socket, state) {
+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
'use strict';
2+
const { mustCall } = require('../common');
3+
const assert = require('assert');
4+
const http = require('http');
5+
const net = require('net');
6+
7+
// https://github.com/nodejs/node/issues/17789 - a connection upgrade response
8+
// that has a Transfer-Encoding header and a body whose first byte is > 127
9+
// triggers a bug where said byte is skipped over.
10+
net.createServer(mustCall(function(conn) {
11+
conn.write('HTTP/1.1 101 Switching Protocols\r\n' +
12+
'Connection: upgrade\r\n' +
13+
'Transfer-Encoding: chunked\r\n' +
14+
'Upgrade: websocket\r\n' +
15+
'\r\n' +
16+
'\u0080', 'latin1');
17+
this.close();
18+
})).listen(0, mustCall(function() {
19+
http.get({
20+
host: this.address().host,
21+
port: this.address().port,
22+
headers: { 'Connection': 'upgrade', 'Upgrade': 'websocket' },
23+
}).on('upgrade', mustCall((res, conn, head) => {
24+
assert.strictEqual(head.length, 1);
25+
assert.strictEqual(head[0], 128);
26+
conn.destroy();
27+
}));
28+
}));

0 commit comments

Comments
 (0)
Please sign in to comment.