-
Notifications
You must be signed in to change notification settings - Fork 605
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
fix: return content length mismatch error #742
fix: return content length mismatch error #742
Conversation
Why are you not happy with the solution? |
this[kClient][kBodySize] !== this[kClient][kResponseContentLength]) { | ||
util.destroy(this, new ResponseContentLengthMismatchError('end')) | ||
return | ||
} |
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.
This check should be in onBody and onMessageComplete
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.
Should probably also check for the strictcontentlength flag
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 flag refers to the sending headers, I think we should rename it, because in the current situation there are 2 cases and both return an error:
- the server content-length is greater than the actual body => connection is closed by the server, client returns "UND_ERR_SOCKET" >> fix sending "UND_ERR_RESPONSE_CONTENT_LENGTH_MISMATCH"
- the server content-length is less than the actual body => connection is closed by the client, client returns "UND_ERR_INFO" >> ? fix sending "UND_ERR_RESPONSE_CONTENT_LENGTH_MISMATCH"
We can add another flag to suppress these errors, because I think those flags have different meanings: as client we can control what we send, so if we see an error/warning on sending headers we can fix it, but we can't control what the server is sending.
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.
The only place the check works is in onSocketEnd
, doing in onMessage
and/or onBody
don't work, and also break other tests.
I think we can't use the strictContentLength
option, or another one, because the connection always ends with an error, so we can only send the right one instead of the generic
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.
Doing it in onSocketEnd is unfortunately just plain wrong. It should basically only be call if the server unexpectedly closes the connection.
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.
With keep-alive the server can give a body that doesn't match content-length without closing the connection.
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.
I've seen body can't be larger than content-length because of https://github.com/nodejs/undici/blob/e2a9ddc07d9e0ff6be25d243523b53d1d71a3e9e/deps/llhttp/include/llhttp.h - unless we want to add this option
I think you were intending to link to a specific line?
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.
yes, sorry
undici/deps/llhttp/include/llhttp.h
Line 472 in e2a9ddc
/* Enables/disables lenient handling of `Connection: close` and HTTP/1.0 |
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.
With keep-alive the server can give a body that doesn't match content-length without closing the connection.
ok, I'll work on that
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.
@ronag to me it's working fine also with keep-alive option
because I added |
I don't think there is any different way to do that. |
+1 to move the parser into its own file |
@simone-sanfratello What's your timeline on this? Should we aim to land it before v4? |
I'll probably close it in the weekend |
ef06f53
to
4559b28
Compare
Codecov Report
@@ Coverage Diff @@
## main #742 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 24 24
Lines 1904 1918 +14
=========================================
+ Hits 1904 1918 +14
Continue to review full report at Codecov.
|
|
||
\* The `UND_ERR_RESPONSE_CONTENT_LENGTH_MISMATCH` is returned when the received body is less than the value in the received header `Content-Length`. | ||
If the server try to send a body larger than `Content-Length`, Undici closes the connection when the length is received and return an `UND_ERR_INFO`, to prevent a cache poisoning attack. |
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.
Please note that at https://github.com/nodejs/undici/blob/e2a9ddc07d9e0ff6be25d243523b53d1d71a3e9e/deps/llhttp/include/llhttp.h
If we want to allow body larger than content-length, we probably should expose this option
2121e93
to
9d17b67
Compare
lib/core/util.js
Outdated
keepAlive: null, | ||
trailers: null, | ||
contentLength: 0 | ||
} |
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.
I'm a little worried this extra object has perf implications?
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.
I don't see much difference between instancing 3 vars instead of 1 object, btw the destructuration of the result is not necessary, so I removed
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.
I'm ok to move back without this function
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.
It would be better if we could avoid it.
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.
removed
test/util.js
Outdated
Buffer.from('timeout=5'), | ||
Buffer.from('Content-Length'), | ||
Buffer.from('2') | ||
], |
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.
Maybe easier with a map(x => Buffer.from(x))
?
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.
removed
6e92b64
to
218a28e
Compare
218a28e
to
ffc97e8
Compare
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.
I don't think keep-alive test is working here as intended. Unless lhttp errors you need to check bodySize
in onBody
or onMessageComplete
.
@@ -450,6 +451,8 @@ class Parser { | |||
this.headersSize = 0 | |||
this.headersMaxSize = client[kMaxHeadersSize] | |||
this.shouldKeepAlive = false | |||
this.bodySize = 0 | |||
this.contentLength = 0 |
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.
this.contentLength = 0 | |
this.contentLength = null |
} else if (!trailers && key.length === 7 && key.toString().toLowerCase() === 'trailer') { | ||
trailers = val | ||
looking = !keepAlive | ||
looking++ | ||
} else if (!this.contentLength && key.length === 14 && key.toString().toLowerCase() === 'content-length') { |
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.
} else if (!this.contentLength && key.length === 14 && key.toString().toLowerCase() === 'content-length') { | |
} else if (this.contentLength == null && key.length === 14 && key.toString().toLowerCase() === 'content-length') { |
t.teardown(server.close.bind(server)) | ||
server.listen(0, () => { | ||
const client = new Client(`http://localhost:${server.address().port}`, { | ||
pipelining: 0 |
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.
you need to use pipelining > 0
or keep-alive won't work. This doesn't actually test keep-alive as it is now.
const server = createServer((req, res) => { | ||
res.writeHead(200, { | ||
'content-length': 10, | ||
'keep-alive': 'timeout=2s', |
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.
Node already does this so you don't need to set it.
Already tried, is not working as expected. I'm closing the PR |
Make request related errors explicit in name in case we want to add errors on response side later. Refs: #742
Make request related errors explicit in name in case we want to add errors on response side later. Refs: #742
Make request related errors explicit in name in case we want to add errors on response side later. Refs: #742
Make request related errors explicit in name in case we want to add errors on response side later. Refs: nodejs#742
see #429
@ronag @mcollina this fix the issue but I'm not very happy with the solution, looks a little bit "patchy" to me, please have a look, then I'll go on on the "@todo"
I also expect to send the same error if the body received is smaller then the "content-length", now it send "UND_ERR_INFO"
Also, I think we should/could refactor
client.js
code to improve readability, for example moving theParser
class to its own file and rename properties and methods where it is not clear they are referring to request or response phase - in another PR of course.