-
Notifications
You must be signed in to change notification settings - Fork 29.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
http: fix parsing of binary upgrade response body #17806
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -458,7 +458,6 @@ function parserOnIncomingClient(res, shouldKeepAlive) { | |
var socket = this.socket; | ||
var req = socket._httpMessage; | ||
|
||
|
||
// propagate "domain" setting... | ||
if (req.domain && !res.domain) { | ||
debug('setting "res.domain"'); | ||
|
@@ -471,29 +470,22 @@ function parserOnIncomingClient(res, shouldKeepAlive) { | |
// We already have a response object, this means the server | ||
// sent a double response. | ||
socket.destroy(); | ||
return; | ||
return 0; // No special treatment. | ||
} | ||
req.res = res; | ||
|
||
// Responses to CONNECT request is handled as Upgrade. | ||
if (req.method === 'CONNECT') { | ||
const method = req.method; | ||
if (method === 'CONNECT') { | ||
res.upgrade = true; | ||
return 2; // skip body, and the rest | ||
return 2; // Skip body and treat as Upgrade. | ||
} | ||
|
||
// Responses to HEAD requests are crazy. | ||
// HEAD responses aren't allowed to have an entity-body | ||
// but *can* have a content-length which actually corresponds | ||
// to the content-length of the entity-body had the request | ||
// been a GET. | ||
var isHeadResponse = req.method === 'HEAD'; | ||
debug('AGENT isHeadResponse', isHeadResponse); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removed because not very useful and slightly misleading - this is not inside the agent. |
||
|
||
if (res.statusCode === 100) { | ||
// restart the parser, as this is a continue message. | ||
req.res = null; // Clear res so that we don't hit double-responses. | ||
req.emit('continue'); | ||
return true; | ||
return 1; // Skip body but don't treat as Upgrade. | ||
} | ||
|
||
if (req.shouldKeepAlive && !shouldKeepAlive && !req.upgradeOrConnect) { | ||
|
@@ -503,7 +495,6 @@ function parserOnIncomingClient(res, shouldKeepAlive) { | |
req.shouldKeepAlive = false; | ||
} | ||
|
||
|
||
DTRACE_HTTP_CLIENT_RESPONSE(socket, req); | ||
LTTNG_HTTP_CLIENT_RESPONSE(socket, req); | ||
COUNTER_HTTP_CLIENT_RESPONSE(); | ||
|
@@ -521,7 +512,10 @@ function parserOnIncomingClient(res, shouldKeepAlive) { | |
if (!handled) | ||
res._dump(); | ||
|
||
return isHeadResponse; | ||
if (method === 'HEAD') | ||
return 1; // Skip body but don't treat as Upgrade. | ||
|
||
return 0; // No special treatment. | ||
} | ||
|
||
// client | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -106,19 +106,10 @@ function parserOnHeadersComplete(versionMajor, versionMinor, headers, method, | |
|
||
parser.incoming.upgrade = upgrade; | ||
|
||
var skipBody = 0; // response to HEAD or CONNECT | ||
if (upgrade) | ||
return 2; // Skip body and treat as Upgrade. | ||
|
||
if (!upgrade) { | ||
// For upgraded connections and CONNECT method request, we'll emit this | ||
// after parser.execute so that we can capture the first part of the new | ||
// protocol. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Slightly wrong comment, removed. |
||
skipBody = parser.onIncoming(parser.incoming, shouldKeepAlive); | ||
} | ||
|
||
if (typeof skipBody !== 'number') | ||
return skipBody ? 1 : 0; | ||
else | ||
return skipBody; | ||
return parser.onIncoming(parser.incoming, shouldKeepAlive); | ||
} | ||
|
||
// XXX This is a mess. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -627,7 +627,7 @@ function parserOnIncoming(server, socket, state, req, keepAlive) { | |
} else { | ||
server.emit('request', req, res); | ||
} | ||
return false; // Not a HEAD response. (Not even a response!) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bogus comment, removed. |
||
return 0; // No special treatment. | ||
} | ||
|
||
function resetSocketTimeout(server, socket, state) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
'use strict'; | ||
const { mustCall } = require('../common'); | ||
const assert = require('assert'); | ||
const http = require('http'); | ||
const net = require('net'); | ||
|
||
// https://github.com/nodejs/node/issues/17789 - a connection upgrade response | ||
// that has a Transfer-Encoding header and a body whose first byte is > 127 | ||
// triggers a bug where said byte is skipped over. | ||
net.createServer(mustCall(function(conn) { | ||
conn.write('HTTP/1.1 101 Switching Protocols\r\n' + | ||
'Connection: upgrade\r\n' + | ||
'Transfer-Encoding: chunked\r\n' + | ||
'Upgrade: websocket\r\n' + | ||
'\r\n' + | ||
'\u0080', 'latin1'); | ||
this.close(); | ||
})).listen(0, mustCall(function() { | ||
http.get({ | ||
host: this.address().host, | ||
port: this.address().port, | ||
headers: { 'Connection': 'upgrade', 'Upgrade': 'websocket' }, | ||
}).on('upgrade', mustCall((res, conn, head) => { | ||
assert.strictEqual(head.length, 1); | ||
assert.strictEqual(head[0], 128); | ||
conn.destroy(); | ||
})); | ||
})); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Before anyone asks - yes, these could be constants, please suggest good names. :-)
(They don't have names in http_parser either though.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just made these up on the spot. Feel free to ignore. How about
kSkipBodyAsUpgrade
,kSkipBodyNoUpgrade
... and I don't know for the 0 case.