-
Notifications
You must be signed in to change notification settings - Fork 501
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
websocket: fix ByteParser.run and parseCloseBody #3080
Changes from 1 commit
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 |
---|---|---|
|
@@ -108,7 +108,7 @@ class ByteParser extends Writable { | |
// sending a Close frame in response, the endpoint typically echos the | ||
// status code it received.) | ||
let body = emptyBuffer | ||
if (this.#info.closeInfo.code) { | ||
if (this.#info.closeInfo?.code) { | ||
body = Buffer.allocUnsafe(2) | ||
body.writeUInt16BE(this.#info.closeInfo.code, 0) | ||
} | ||
|
@@ -298,26 +298,30 @@ class ByteParser extends Writable { | |
// defined as the status code (Section 7.4) contained in the first Close | ||
// control frame received by the application | ||
code = data.readUInt16BE(0) | ||
if (!isValidStatusCode(code)) { | ||
return null | ||
} | ||
} else { | ||
return null | ||
} | ||
|
||
// https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.6 | ||
/** @type {Buffer} */ | ||
let reason = data.subarray(2) | ||
/** @type {number} */ | ||
let reasonOffset = 2 | ||
|
||
// Remove BOM | ||
if (reason[0] === 0xEF && reason[1] === 0xBB && reason[2] === 0xBF) { | ||
reason = reason.subarray(3) | ||
// Detect BOM and ignore it | ||
if (data.length >= 5 && data[2] === 0xEF && data[3] === 0xBB && data[4] === 0xBF) { | ||
reasonOffset = 5 | ||
} | ||
|
||
if (code !== undefined && !isValidStatusCode(code)) { | ||
return null | ||
if (data.length - reasonOffset < 1) { | ||
return { code, reason: '' } | ||
} | ||
|
||
let reason = '' | ||
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. There is a misunderstanding here. Returning 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 seems to be a false conclusion. We got a valid code, so we wont need to set the code 1005 or 1006. The message is somewhat corrupt. So. We need to prefer the valid code and discard the message. |
||
try { | ||
reason = utf8Decode(reason) | ||
} catch { | ||
return null | ||
} | ||
reason = utf8Decode(data.subarray(reasonOffset)) | ||
} catch { } | ||
|
||
return { code, reason } | ||
} | ||
|
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.
A close frame doesn't need to contain a reason or code,
data
(the body) can be empty here. You could assert the data length isn't 1 here, but we do check for its length elsewhere so it'd just be for safety.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 thing is:
https://app.codecov.io/gh/nodejs/undici/pull/3080/blob/lib/web/websocket/receiver.js
This line is covered by test. So it is possible that the size of the data is 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 know, I said that. The buffer here is not the entire frame (which has to have a minimum length of 2), but the body of a frame. If there is no code and no reason, it is still a valid close frame, and will be empty here. Returning
null
means that the frame body is invalid, but that's not correct.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.
On the other hand, the body can be empty or 2+ bytes, but it cannot be 1 byte, which is why I mentioned earlier that for safety we could assert that the body isn't a single byte. We already check elsewhere, but as I mentioned earlier, would be for safety.
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.
Well but it is happening:
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 understand what that has to do with what I said?
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 means, that your claim, that we already handled all cases with data.length < 2 is invalid. Either data.length === 0 or data.length === 1 or both are not covered by the calling function.