Node.js incorrectly parses HTTP methods
chmod777self.com
chmod777self.com
Reading the HTTP method in a high-performance way does lead to superficially ugly code. That's why code generation is good. Ragel has been mentioned and I intend to seriously consider using it, but for the moment my own HTTP method reading code is generated with:
generate_branchified_method(
writer,
branchify!(case sensitive,
"CONNECT" => Connect,
"DELETE" => Delete,
"GET" => Get,
"HEAD" => Head,
"OPTIONS" => Options,
"PATCH" => Patch,
"POST" => Post,
"PUT" => Put,
"TRACE" => Trace
),
1,
"self.stream.read_byte()",
"SP",
"MAX_METHOD_LEN",
"is_token_item(b)",
"ExtensionMethod(%s)");
This is pleasantly easy to read and meaningful.This generates the high performance artwork shown at http://sprunge.us/HdTH, which supports extension methods correctly. (Rust's algebraic data types are marvellous for many things in implementing such a spec.)
Have you seen that there's a project for a Rust backend for Ragel[0] which would allow direct reuse of the Mongrel HTTP 1.1 ragel spec?
It's been a while since I checked out the Ragel backend, does it still generate decent Rust code?
I would not be using the Mongrel spec directly for licensing reasons. I want the entire thing to be MIT + AL2. I don't know if it ends up the most efficient. I'll see when I get to experimenting around that.
Sounds a lot like a "confused deputy" situation: imagine that your L7 firewall has a rule to reject any PUT request, but it sees PUN and thus allows the request to pass through to node.js, which then treats it as though it were actually PUT.
Please back this up with evidence.
Anyone looking to purchase a souped up Honda Civic?
People might notice that it now works correctly. And it might even be faster.
> Ragel is great but it can't take shortcuts like a human programmer can and once you start jumping around in your grammar, things become complicated fast (and impossible to debug.)
https://github.com/joyent/http-parser/pull/156#issuecomment-...
It's presumptuous to simply say 'all this is unnecessary' unless you have measured it and we have no reason to believe the author hasn't measured it.
BTW, the file is copyright nginx.
- I value software that keeps to the spec, because it's the spec that I (as a dev or non-dev) refer to. You never hear "NodeJS has HTTPish module", nor do you read documentation of that module's concepts and behaviour. Those are defined in the spec, and the __fill_in_with_any_language__ HTTP module just implements those definitions.
- Optimizations, simplifications, corrections should be done in the spec, whenever the you find them at implementation-time.
But until now there has not been ONE HTTP server that grasps and handles the HTTP specs in their whole. So then, I find it hilarious to read that about optimizations when neither of us have the whole picture.
That said, I don't think it's Node.js to blame here (albeit they do have weird views of standards: https://github.com/joyent/node/issues/4850) but HTTP itself because the spec's abstraction levels have been far away from the implementations' reach. HTTPs concepts are gorgeous but they are worth nil if implementation is "hard" and never done properly.
Longer story at: http://andreineculau.github.io/hyperrest/2013-06-10-http-hel...
Out of interest has anyone seen what other web servers support for these more 'esoteric' verbs is like?
http://trac.nginx.org/nginx/browser/nginx/src/http/ngx_http_...
[0]: http://en.wikipedia.org/wiki/Hyper_Text_Coffee_Pot_Control_P...
https://gist.github.com/NickPresta/6276195
It seems to pass the correct method to the application.
Is there a discussion about that somewhere? The public repo still uses the HTTP_METHOD_MAP macros.
https://github.com/joyent/node/blob/v0.11.5-release/src/node...
It starts when somebody makes a legitimate observation that a very specific set of functionality has been shown to have serious flaws in practice, and then suggests that perhaps the best course of action is to remove this very specific, and broken, functionality.
Then somebody else comes along, and responds like you did with a smart-ass comment taking it to an overly-broad, unreasonable and stupid extreme. These kinds of comments are useless.
Sometimes the best way to fix broken functionality is to remove it. That in no way means it's the only possible solution, however. Nor does it mean that it needs to be applied without bound.
I missed that. I saw a link that says that we shouldn't have verbs because no one implemented them, LOL SPACEJUMP.
Do you really think that it's a "legitimate observation" that because node.js has a shitty http parser, the parts of http that it parsed badly should be thrown out?
I read two smart-ass comments here, and a civility troll.
A quick look now suggests they are using a parser generator now.
When the length of verb is 4, it checks if the second character is 'O' before trying the full string comparisons, probably because there are 4 possibilities with it (POST, COPY, MOVE, LOCK) vs only one without (HEAD).
It starts at line 180.
That being said, in nginx case, it's a preliminary check, just to avoid doing some full string comparisons in a very specific case. On the other hand, the nodejs directly does a 'parser->method = HTTP_CONNECT' after checking one character. It seems like it actually checks the other characters afterwards in some cases, but this affectation is quite misleading.
[0] http://hg.nginx.org/nginx/file/abf7813b927e/src/http/ngx_htt...
Actually, if you look at the nginx source I linked, the comparisons of the full strings are hidden behind macros because they do the opposite: they group characters 4 by 4, and do 32bit int comparisons as much as possible.
You can improve HTTP throughput far more dramatically with higher-level optimizations, like moving request parsing into the kernel or otherwise doing things that address larger bottlenecks involved in getting HTTP requests from the network card. The Windows version of Node performs a lot better if you have it use Microsoft's kernel mode HTTP interfaces than if you use the crazy micro-optimized HTTP support in Node that is responsible for this bug.
Ultimately, the prologue in a HTTP request - 'HTTP/1.1 GET' or whatever - is a tiny string of bytes and parsing it, even using the most slow language on the planet, really isn't that expensive. Optimizing that tiny bit of work isn't going to produce enormous gains. Mostly just an ill-advised attempt at being clever.
HTTP (and SIP) have idiotically complex grammars for no reason other than the authors getting all clever on us because it's a "text based" format. I'd actually be shocked to find that HTTP is actually properly implemented in most cases (fully handling comments and line folding, for example).
And then, even if you do implement it properly, you have to worry about other software interpreting it differently. So Proxy A determines a request is allowed and appends a header, but Server B reads the request in a different way and violates policy.
curl -v -s -X "GE$(printf '%.s ' {0..100000})" localhost:8888
It doesn't kill node process.. but there could be buffer overflow or similar bug in that parser code.If your goal is really optimization, you'd do it as a series of vector operations (GCC will let you do this in a platform independent way for almost all of these methods), using mm_cmpeq_*, which will do 4 chars at a time, or 8 chars at a time with AVX/etc.
In reality, strncmp is likely to be a lot more optimized than you think, and in newer glibc, it will do 16 chars at a time using SSE4.2/AVX/whatever (see http://repo.or.cz/w/glibc.git/blob/ba63ba088227987c8215e93f7...).
The short answer is: I'd love to see benchmarks that show this as a real bottleneck.