Repository navigation
Closed
Labels
confirmed-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.
Description
- Version: 15.8.*
- Platform: All
- Subsystem: http
What steps will reproduce the bug?
import net from 'net';
import http from 'http';
// create a simple server with too small of content-length
const body = 'HTTP/1.1 200 OK\r\n' +
'Content-Length: 5\r\n' +
'Connection: close\r\n' +
'\r\n' +
'2ad731e3-4dcd-4f70-b871-0ad284b29ffc'
const server = net.createServer((conn) => conn.end(body));
const port = 9191;
server.listen(port, () => {
// try to GET from the server
http.get('http://localhost:' + port);
});
// causes Error: Parse Error: Expected HTTP/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: close response. Unclear on expected HTTP semantics and common leniency here. Likely shouldn't do either I suspect.
What do you see instead?
$ node bug.js
node:events:355
throw er; // Unhandled 'error' event
^
Error: Parse Error: Expected HTTP/
at Socket.socketOnData (node:_http_client:502:22)
at Socket.emit (node:events:378:20)
at addChunk (node:internal/streams/readable:313:12)
at readableAddChunk (node:internal/streams/readable:288:9)
at Socket.Readable.push (node:internal/streams/readable:227:10)
at TCP.onStreamRead (node:internal/stream_base_commons:190:23)
Emitted 'error' event on ClientRequest instance at:
at Socket.socketOnData (node:_http_client:509:9)
at Socket.emit (node:events:378:20)
[... lines matching original stack trace ...]
at TCP.onStreamRead (node:internal/stream_base_commons:190:23) {
bytesParsed: 62,
code: 'HPE_INVALID_CONSTANT',
reason: 'Expected HTTP/',
rawPacket: Buffer(93) [Uint8Array] [
72, 84, 84, 80, 47, 49, 46, 49, 32, 50, 48, 48,
32, 79, 75, 13, 10, 67, 111, 110, 116, 101, 110, 116,
45, 76, 101, 110, 103, 116, 104, 58, 32, 53, 13, 10,
67, 111, 110, 110, 101, 99, 116, 105, 111, 110, 58, 32,
99, 108, 111, 115, 101, 13, 10, 13, 10, 50, 97, 100,
55, 51, 49, 101, 51, 45, 52, 100, 99, 100, 45, 52,
102, 55, 48, 45, 98, 56, 55, 49, 45, 48, 97, 100,
50, 56, 52, 98, 50, 57, 102, 102, 99
]
}Additional information
Metadata
Metadata
Assignees
Labels
confirmed-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.
Activity
jasnell commentedon Mar 9, 2021
/cc @indutny @nodejs/http
indutny commentedon 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:
The spec describes how to determine the message body length and there is only one note about
Connection: closecase thatloose(not strict) mode violates:Which makes me worry that loose mode is implemented this way, but doesn't change the fact that error is expected anyway.
bmeck commentedon Mar 9, 2021
@indutny something seems odd here though, if I put a valid response in that location like below, there isn't the same error.
This seems problematic given the:
point it does seem to be processing it.
indutny commentedon Mar 9, 2021
@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.bmeck commentedon Mar 9, 2021
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.
indutny commentedon Mar 9, 2021
@bmeck yeah, discarding is fine too according to spec.
strictmode can error, andloosecan discard. Is this what you are suggesting?bmeck commentedon Mar 9, 2021
@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).
mcollina commentedon Mar 10, 2021
I agree that discarding would be a good approach.
indutny commentedon Mar 27, 2021
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:
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.
indutny commentedon Mar 27, 2021
Here's the Pull Request. Would appreciate a review for it, so that I can merge and release it.
indutny commentedon Mar 27, 2021
(...and sorry for a very long delay in responding to y'all. Things have been quite busy at my new job)
bmeck commentedon Mar 30, 2021
@indutny LGTM
mcollina commentedon Apr 12, 2021
Fixed in nodejs/llhttp#94 as well as #38146.