-
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -9,7 +9,8 @@ const util = require('./core/util') | |||||
const Request = require('./core/request') | ||||||
const Dispatcher = require('./dispatcher') | ||||||
const { | ||||||
ContentLengthMismatchError, | ||||||
RequestContentLengthError, | ||||||
ResponseContentLengthError, | ||||||
TrailerMismatchError, | ||||||
InvalidArgumentError, | ||||||
RequestAbortedError, | ||||||
|
@@ -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 commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
this.paused = false | ||||||
this.resume = this.resume.bind(this) | ||||||
} | ||||||
|
@@ -640,9 +643,6 @@ class Parser { | |||||
|
||||||
const request = client[kQueue][client[kRunningIdx]] | ||||||
assert(request) | ||||||
|
||||||
// TODO: Check for content-length mismatch from server? | ||||||
|
||||||
assert(!this.upgrade) | ||||||
assert(this.statusCode < 200) | ||||||
|
||||||
|
@@ -689,23 +689,28 @@ class Parser { | |||||
} | ||||||
|
||||||
assert(this.headers.length % 2 === 0) | ||||||
|
||||||
this.headers = [] | ||||||
this.headersSize = 0 | ||||||
|
||||||
let keepAlive | ||||||
let trailers | ||||||
this.contentLength = 0 | ||||||
|
||||||
let looking = true | ||||||
for (let n = 0; n < rawHeaders.length && looking; n += 2) { | ||||||
for (let n = 0, looking = 0; n < rawHeaders.length && looking < 3; n += 2) { | ||||||
const key = rawHeaders[n] | ||||||
const val = rawHeaders[n + 1] | ||||||
|
||||||
if (!keepAlive && key.length === 10 && key.toString().toLowerCase() === 'keep-alive') { | ||||||
keepAlive = val | ||||||
looking = !trailers | ||||||
looking++ | ||||||
} 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 commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
// content-length here is always valid because lhttp validate it | ||||||
this.contentLength = Number(val) | ||||||
looking++ | ||||||
} | ||||||
} | ||||||
|
||||||
|
@@ -757,6 +762,8 @@ class Parser { | |||||
return -1 | ||||||
} | ||||||
|
||||||
this.bodySize += buf.length | ||||||
|
||||||
const request = client[kQueue][client[kRunningIdx]] | ||||||
assert(request) | ||||||
|
||||||
|
@@ -918,6 +925,11 @@ function onSocketError (err) { | |||||
} | ||||||
|
||||||
function onSocketEnd () { | ||||||
if (this[kParser].bodySize > 0 && this[kParser].contentLength > 0 && | ||||||
this[kParser].bodySize !== this[kParser].contentLength) { | ||||||
util.destroy(this, new ResponseContentLengthError('other side closed')) | ||||||
return | ||||||
} | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 commentThe 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 commentThe 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:
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 commentThe reason will be displayed to describe this comment to others. Learn more. The only place the check works is in I think we can't use the There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 commentThe 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 commentThe reason will be displayed to describe this comment to others. Learn more.
I think you were intending to link to a specific line? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, sorry undici/deps/llhttp/include/llhttp.h Line 472 in e2a9ddc
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
ok, I'll work on that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ronag to me it's working fine also with keep-alive option |
||||||
util.destroy(this, new SocketError('other side closed')) | ||||||
} | ||||||
|
||||||
|
@@ -1233,12 +1245,12 @@ function write (client, request) { | |||||
|
||||||
if (request.contentLength !== null && request.contentLength !== contentLength) { | ||||||
if (client[kStrictContentLength]) { | ||||||
request.onError(new ContentLengthMismatchError()) | ||||||
request.onError(new RequestContentLengthError()) | ||||||
assert(request.aborted) | ||||||
return false | ||||||
} | ||||||
|
||||||
process.emitWarning(new ContentLengthMismatchError()) | ||||||
process.emitWarning(new RequestContentLengthError()) | ||||||
} | ||||||
|
||||||
const socket = client[kSocket] | ||||||
|
@@ -1305,7 +1317,7 @@ function write (client, request) { | |||||
|
||||||
if (!body) { | ||||||
if (contentLength === 0) { | ||||||
socket.write(`${header}content-length: ${contentLength}\r\n\r\n\r\n`, 'ascii') | ||||||
socket.write(`${header}content-length: 0\r\n\r\n\r\n`, 'ascii') | ||||||
} else { | ||||||
assert(contentLength === null, 'no body must not have content length') | ||||||
socket.write(`${header}\r\n`, 'ascii') | ||||||
|
@@ -1344,11 +1356,11 @@ function write (client, request) { | |||||
// We should defer writing chunks. | ||||||
if (contentLength !== null && bytesWritten + len > contentLength) { | ||||||
if (client[kStrictContentLength]) { | ||||||
util.destroy(socket, new ContentLengthMismatchError()) | ||||||
util.destroy(socket, new RequestContentLengthError()) | ||||||
return | ||||||
} | ||||||
|
||||||
process.emitWarning(new ContentLengthMismatchError()) | ||||||
process.emitWarning(new RequestContentLengthError()) | ||||||
} | ||||||
|
||||||
if (bytesWritten === 0) { | ||||||
|
@@ -1398,9 +1410,9 @@ function write (client, request) { | |||||
|
||||||
if (!err && contentLength !== null && bytesWritten !== contentLength) { | ||||||
if (client[kStrictContentLength]) { | ||||||
err = new ContentLengthMismatchError() | ||||||
err = new RequestContentLengthError() | ||||||
} else { | ||||||
process.emitWarning(new ContentLengthMismatchError()) | ||||||
process.emitWarning(new RequestContentLengthError()) | ||||||
} | ||||||
} | ||||||
|
||||||
|
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