Skip to content
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

Conversation

simone-sanfratello
Copy link
Contributor

@simone-sanfratello simone-sanfratello commented Apr 15, 2021

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 the Parser 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.

@simone-sanfratello simone-sanfratello marked this pull request as draft April 15, 2021 07:03
@mcollina
Copy link
Member

Why are you not happy with the solution?

this[kClient][kBodySize] !== this[kClient][kResponseContentLength]) {
util.destroy(this, new ResponseContentLengthMismatchError('end'))
return
}
Copy link
Member

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

Copy link
Member

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

Copy link
Contributor Author

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.

Copy link
Contributor Author

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

Copy link
Member

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.

Copy link
Member

@ronag ronag Apr 19, 2021

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.

Copy link
Member

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, sorry

/* Enables/disables lenient handling of `Connection: close` and HTTP/1.0

Copy link
Contributor Author

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@simone-sanfratello
Copy link
Contributor Author

simone-sanfratello commented Apr 15, 2021

Why are you not happy with the solution?

because I added this[kResponseContentLength] = 0 to client class, is there a better way to do that?

@mcollina
Copy link
Member

Why are you not happy with the solution?

because I added this[kResponseContentLength] = 0 to client class, is there a better way to do that?

I don't think there is any different way to do that.

lib/client.js Outdated Show resolved Hide resolved
@simone-sanfratello
Copy link
Contributor Author

thanks @mcollina @ronag for the instant review 😃

I'll go on

lib/client.js Outdated Show resolved Hide resolved
@dnlup
Copy link
Contributor

dnlup commented Apr 15, 2021

+1 to move the parser into its own file

@ronag
Copy link
Member

ronag commented Apr 15, 2021

@simone-sanfratello What's your timeline on this? Should we aim to land it before v4?

@simone-sanfratello
Copy link
Contributor Author

I'll probably close it in the weekend

@codecov-commenter
Copy link

codecov-commenter commented Apr 18, 2021

Codecov Report

Merging #742 (ffc97e8) into main (9f6bf86) will not change coverage.
The diff coverage is 100.00%.

Impacted file tree graph

@@            Coverage Diff            @@
##              main      #742   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           24        24           
  Lines         1904      1918   +14     
=========================================
+ Hits          1904      1918   +14     
Impacted Files Coverage Δ
lib/client.js 100.00% <100.00%> (ø)
lib/core/errors.js 100.00% <100.00%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 9f6bf86...ffc97e8. Read the comment docs.

@simone-sanfratello simone-sanfratello marked this pull request as ready for review April 18, 2021 07:35

\* 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.
Copy link
Contributor Author

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

lib/core/errors.js Outdated Show resolved Hide resolved
docs/api/Errors.md Outdated Show resolved Hide resolved
lib/core/util.js Outdated
keepAlive: null,
trailers: null,
contentLength: 0
}
Copy link
Member

@ronag ronag Apr 19, 2021

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?

Copy link
Contributor Author

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

Copy link
Contributor Author

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

Copy link
Member

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.

Copy link
Contributor Author

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')
],
Copy link
Member

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))?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

@simone-sanfratello simone-sanfratello force-pushed the bug/content-length-429 branch 2 times, most recently from 6e92b64 to 218a28e Compare April 24, 2021 06:16
Copy link
Member

@ronag ronag left a 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
} 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
Copy link
Member

@ronag ronag Apr 24, 2021

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',
Copy link
Member

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.

@ronag ronag added this to the 4.0 milestone Apr 24, 2021
@simone-sanfratello
Copy link
Contributor Author

I don't think keep-alive test is working here as intended. Unless lhttp errors you need to check bodySize in onBody or onMessageComplete.

Already tried, is not working as expected. I'm closing the PR

ronag added a commit that referenced this pull request Apr 26, 2021
Make request related errors explicit in name in case we want to
add errors on response side later.

Refs: #742
ronag added a commit that referenced this pull request Apr 26, 2021
Make request related errors explicit in name in case we want to
add errors on response side later.

Refs: #742
ronag added a commit that referenced this pull request Apr 26, 2021
Make request related errors explicit in name in case we want to
add errors on response side later.

Refs: #742
crysmags pushed a commit to crysmags/undici that referenced this pull request Feb 27, 2024
Make request related errors explicit in name in case we want to
add errors on response side later.

Refs: nodejs#742
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants