Repository navigation
HTTP Response read past end #37676
Description
Activity
/cc @indutny @nodejs/http
- addedconfirmed-bugIssues and PRs for confirmed bugs.Issues and PRs for confirmed bugs.httpIssues and PRs related to the http subsystem.Issues and PRs related to the http subsystem.http_parserIssues and PRs related to the HTTP parser implementation or http_parser binding.Issues and PRs related to the HTTP parser implementation or http_parser binding.
on Mar 9, 2021 I believe this is working as expected. The error message would have been clearer if Node.js used strict mode of llhttp:
off=71 error code=5 reason="Data after `Connection: close`"The spec describes how to determine the message body length and there is only one note about
Connection: closecase thatloose(not strict) mode violates:If the final response to the last request on a connection has been completely received and there remains additional data to read, a user agent MAY discard the remaining data or attempt to determine if that data belongs as part of the prior response body, which might be the case if the prior message's Content-Length value is incorrect. A client MUST NOT process, cache, or forward such extra data as a separate response, since such behavior would be vulnerable to cache poisoning.Which makes me worry that loose mode is implemented this way, but doesn't change the fact that error is expected anyway.
@indutny something seems odd here though, if I put a valid response in that location like below, there isn't the same error.
import net from 'net'; import http from 'http'; const body = 'HTTP/1.1 200 OK\r\n' + 'Content-Length: 5\r\n' + 'Connection: close\r\n' + '\r\n' + '2ad73HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n' const server = net.createServer((conn) => conn.end(body)); const port = 9191; server.listen(port, () => { http.get('http://localhost:' + port, (res) => { // drain it res.on('data', console.log) res.on('close', () => server.close()); }).end(); });
This seems problematic given the:
MUST NOT process, cache, or forward such extra data as a
separate response, since such behavior would be vulnerable to cache
poisoning.point it does seem to be processing it.
@bmeck yeah, precisely! I'm thinking about patching this up in llhttp, but it was ported over from http_parser AFAIK so I'm not sure how much servers/clients we'll break...
The reason why this happens is that during the build of llhttp (and http_parser) you have to decide whether to build it in
strictorloosemode.strictis rather hardcore, but follows the spec very very closely.looseallows certain characters out of the normal range, and overall is more lenient. This leniency includes parsing the second request withConnection: close. IMO, this is a bug and I don't mind fixing it if everyone is on the same page about it.Is there any reason to error instead of discarding? I would expect not to error in the result and it actually was found due to https://github.com/http-tests/cache-tests being unable to run against node due to the unrecoverable error.
@bmeck yeah, discarding is fine too according to spec.
strictmode can error, andloosecan discard. Is this what you are suggesting?@indutny yes, I would prefer discarding since node's http client can actually pass those tests if we do. Also in theory it could be less compute (since we already track the length).
I agree that discarding would be a good approach.
I technically have a patch doing this, but just thought I'd run by you all the tests that no longer pass with discarding of the extra data:
- https://github.com/nodejs/llhttp/blob/242cfd4ebc2b57dd84904cb67b617bf4a6b6fa93/test/request/connection.md#resetting-flags-when-keep-alive-is-off-10-and-parser-is-in-loose-mode
- https://github.com/nodejs/llhttp/blob/242cfd4ebc2b57dd84904cb67b617bf4a6b6fa93/test/request/connection.md#crlf-between-requests-explicit-close-loose-mode
- https://github.com/nodejs/llhttp/blob/242cfd4ebc2b57dd84904cb67b617bf4a6b6fa93/test/response/connection.md#http11-with-keep-alive-disabled-and-204-status-in-loose-mode
It is definitely going to be a major version bump for llhttp, but more importantly I think I have to make it continue work with the lenient flags so that affected users will still have a way of allowing it.
Here's the Pull Request. Would appreciate a review for it, so that I can merge and release it.
(...and sorry for a very long delay in responding to y'all. Things have been quite busy at my new job)
@indutny LGTM
Fixed in nodejs/llhttp#94 as well as #38146.
What steps will reproduce the bug?
How often does it reproduce? Is there a required condition?
Always
What is the expected behavior?
No error, doesn't read past content-length and/or doesn't try to parse a second response from a
connection: closeresponse. Unclear on expected HTTP semantics and common leniency here. Likely shouldn't do either I suspect.What do you see instead?
Additional information