-
Notifications
You must be signed in to change notification settings - Fork 30k
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: tell the parser about CONNECT responses #6198
Conversation
'use strict'; | ||
var common = require('../common'); | ||
var assert = require('assert'); | ||
var http = require('http'); |
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.
these can be const
@nodejs/http |
Parser* parser = Unwrap<Parser>(args.Holder()); | ||
|
||
bool upgrade = value->BooleanValue(); | ||
parser->parser_.upgrade = upgrade; |
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.
That doesn't look correct to me. http_parser.upgrade
is only supposed to be read, not written, by clients of libhttp_parser.
This commit fixes a bug in HTTP CONNECT response parsing. The parser normally continues to look for additional HTTP messages after the first message has completed. However, in the case of CONNECT responses, the parser should stop looking for additional messages and treat any further data as a separate protocol. Because of the way that HTTP messages are parsed in JavaScript, this bug only manifests itself in the case where the socket's `data' handler receives the end of the response message *and also* includes non-HTTP data. In order to implement the fix, an `.upgrade' accessor is exposed to JavaScript on the HTTPParser object that proxies the underlying http_parser's `upgrade' field. Likewise in JavaScript, the `http' client module sets this value to `true' when a response is received to a CONNECT request. The result of this is that callbacks on HTTPParser instances can signal that the message indicates a change in protocol and further HTTP parsing should not occur.
daae733
to
08284b3
Compare
I've updated the tests and the setter value to include changes from the comments. But the real issue is what @bnoordhuis raises regarding the setting of
From
In
The workaround would be to test if the message was complete, and if the request method is CONNECT. However, there would be no way to know where the CONNECT response message ended and the next protocol began, and therefore no way to send the correct head data to the request's The exception to this rule would be parser errors on the first byte after the CONNECT response. Indeed this is how I first noticed the bug and my initial solution was simply to check And the problems actually go deeper. If a parser error is not returned, it's possible that the changed protocol (after the CONNECT response) actually gets parsed as another HTTP message. This would cause all sorts of problems for the module's consumers.
Line 236:
This flag is being checked from within the
In sum, I believe that this is the most elegant and "correct" solution. It certainly isn't the only solution, and I'd gladly write a patch using other functionality if it is suggested. As a corollary, and in order to alleviate any concerns about TL;DR Setting |
FWIW, parser should set |
@indutny The Also, sorry for that big ol' wall of text. I think I got carried away ;) |
Oh, looks like I get it now. There is indeed no branch for this in http-parser, will see how to fix it in a bit. |
This is what I have in mind: diff --git a/deps/http_parser/http_parser.c b/deps/http_parser/http_parser.c
index d51a2e7..7196175 100644
--- a/deps/http_parser/http_parser.c
+++ b/deps/http_parser/http_parser.c
@@ -1812,6 +1812,9 @@ reexecute:
case 0:
break;
+ case 2:
+ parser->upgrade = 1;
+
case 1:
parser->flags |= F_SKIPBODY;
break;
diff --git a/lib/_http_client.js b/lib/_http_client.js
index bc29426..68eb125 100644
--- a/lib/_http_client.js
+++ b/lib/_http_client.js
@@ -432,7 +432,7 @@ function parserOnIncomingClient(res, shouldKeepAlive) {
// Responses to CONNECT request is handled as Upgrade.
if (req.method === 'CONNECT') {
res.upgrade = true;
- return true; // skip body
+ return 2; // skip body, and the rest
}
// Responses to HEAD requests are crazy.
diff --git a/lib/_http_common.js b/lib/_http_common.js
index 08f93d8..1e6490e 100644
--- a/lib/_http_common.js
+++ b/lib/_http_common.js
@@ -94,7 +94,7 @@ function parserOnHeadersComplete(versionMajor, versionMinor, headers, method,
parser.incoming.upgrade = upgrade;
- var skipBody = false; // response to HEAD or CONNECT
+ var skipBody = 0; // response to HEAD or CONNECT
if (!upgrade) {
// For upgraded connections and CONNECT method request, we'll emit this
@@ -103,7 +103,10 @@ function parserOnHeadersComplete(versionMajor, versionMinor, headers, method,
skipBody = parser.onIncoming(parser.incoming, shouldKeepAlive);
}
- return skipBody;
+ if (typeof skipBody !== 'number')
+ return skipBody ? 1 : 0;
+ else
+ return skipBody;
}
// XXX This is a mess.
diff --git a/src/node_http_parser.cc b/src/node_http_parser.cc
index 4087ed2..11f44fc 100644
--- a/src/node_http_parser.cc
+++ b/src/node_http_parser.cc
@@ -300,7 +300,7 @@ class Parser : public AsyncWrap {
return -1;
}
- return head_response->IsTrue() ? 1 : 0;
+ return head_response->IntegerValue();
}
May I ask you to give it a try? It appears to be fixing the problem without touching internal parser's state. Will require an http-parser upgrade, though. (@bnoordhuis @jasnell PTAL at that http-parser changes, if it looks ok to you - we will continue discussing it at relevant repo. Posted the patch here to show how it will work in a context of node.js) |
I think this is a better solution, overall. My tests seem to be passing, too. Thanks for this! Would it be difficult to backport this to node's LTS version, due to the dependency library change? |
@slushie I think it should not be that difficult, this dependency change is of semver-patch level so it should not cause any problems at all. |
Returning `2` from on_headers_complete will tell parser that it should not expect neither a body nor any futher responses on this connection. This is useful for handling responses to a CONNECT request which may not contain `Upgrade` or `Connection: upgrade` headers. See: nodejs/node#6198
Opened an http-parser PR just in case: nodejs/http-parser#299 |
@indutny ... at first glance this looks good. Will have to dig into the new PR in more detail tho |
Returning `2` from on_headers_complete will tell parser that it should not expect neither a body nor any futher responses on this connection. This is useful for handling responses to a CONNECT request which may not contain `Upgrade` or `Connection: upgrade` headers. See: nodejs/node#6198 PR-URL: #299 Reviewed-By: Brian White <mscdex@mscdex.net>
Adds `2` as a return value of `on_headers_complete`, this mode will be used to fix handling responses to `CONNECT` requests. See: nodejs#6198
When handling a response to `CONNECT` request - skip message body and do not attempt to parse the next message. `CONNECT` requests are used in similar sense to HTTP Upgrade. Fix: nodejs#6198
Adds `2` as a return value of `on_headers_complete`, this mode will be used to fix handling responses to `CONNECT` requests. See: nodejs#6198 PR-URL: nodejs#6279 Reviewed-By: Brian White <mscdex@mscdex.net> Reviewed-By: James M Snell <jasnell@gmail.com>
When handling a response to `CONNECT` request - skip message body and do not attempt to parse the next message. `CONNECT` requests are used in similar sense to HTTP Upgrade. Fix: nodejs#6198 PR-URL: nodejs#6279 Reviewed-By: Brian White <mscdex@mscdex.net> Reviewed-By: James M Snell <jasnell@gmail.com>
See: nodejs#6198 PR-URL: nodejs#6279 Reviewed-By: Brian White <mscdex@mscdex.net> Reviewed-By: James M Snell <jasnell@gmail.com>
Adds `2` as a return value of `on_headers_complete`, this mode will be used to fix handling responses to `CONNECT` requests. See: nodejs#6198 PR-URL: nodejs#6279 Reviewed-By: Brian White <mscdex@mscdex.net> Reviewed-By: James M Snell <jasnell@gmail.com>
When handling a response to `CONNECT` request - skip message body and do not attempt to parse the next message. `CONNECT` requests are used in similar sense to HTTP Upgrade. Fix: nodejs#6198 PR-URL: nodejs#6279 Reviewed-By: Brian White <mscdex@mscdex.net> Reviewed-By: James M Snell <jasnell@gmail.com>
See: nodejs#6198 PR-URL: nodejs#6279 Reviewed-By: Brian White <mscdex@mscdex.net> Reviewed-By: James M Snell <jasnell@gmail.com>
Adds `2` as a return value of `on_headers_complete`, this mode will be used to fix handling responses to `CONNECT` requests. See: nodejs#6198 PR-URL: nodejs#6279 Reviewed-By: Brian White <mscdex@mscdex.net> Reviewed-By: James M Snell <jasnell@gmail.com>
When handling a response to `CONNECT` request - skip message body and do not attempt to parse the next message. `CONNECT` requests are used in similar sense to HTTP Upgrade. Fix: nodejs#6198 PR-URL: nodejs#6279 Reviewed-By: Brian White <mscdex@mscdex.net> Reviewed-By: James M Snell <jasnell@gmail.com>
See: nodejs#6198 PR-URL: nodejs#6279 Reviewed-By: Brian White <mscdex@mscdex.net> Reviewed-By: James M Snell <jasnell@gmail.com>
Checklist
Affected core subsystem(s)
http
Description of change
This commit fixes a bug in HTTP CONNECT response parsing. The parser
normally continues to look for additional HTTP messages after the
first message has completed. However, in the case of CONNECT responses,
the parser should stop looking for additional messages and treat
any further data as a separate protocol.
Because of the way that HTTP messages are parsed in JavaScript, this
bug only manifests itself in the case where the socket's data
handler receives the end of the response message and also includes
non-HTTP data.
In order to implement the fix, an .upgrade accessor is exposed to
JavaScript on the
HTTPParser
object that proxies the underlyinghttp_parser
's upgrade field. Likewise in JavaScript, thehttp
client module sets this value to
true
when a response is receivedto a CONNECT request.
The result of this is that callbacks on
HTTPParser
instances cansignal that the message indicates a change in protocol and further
HTTP parsing should not occur.