Repository navigation
Node: Fix the buffer builder being left unusable #1999
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
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 |
|---|---|---|
|
|
@@ -191,8 +191,16 @@ export class Channel extends EnhancedEventEmitter { | |
|
|
||
| /** | ||
| * flatbuffer builder. | ||
| * | ||
| * NOTE: It must be retrieved once per message, right before building | ||
| * its body. | ||
| * It's reset here so a new message always starts with a clean builder, | ||
| * which also recovers it if a previous build threw in the middle of the | ||
| * serialization (which would otherwise break every future message). | ||
| */ | ||
| get bufferBuilder(): flatbuffers.Builder { | ||
| this.#bufferBuilder.clear(); | ||
|
|
||
| return this.#bufferBuilder; | ||
| } | ||
|
|
||
|
|
@@ -243,6 +251,12 @@ export class Channel extends EnhancedEventEmitter { | |
| ); | ||
| } | ||
|
|
||
| // If there is no body then the buffer builder holds nothing we need, so | ||
|
Member
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 is also post-defensive approach that we are supposed to hate. |
||
| // reset it in case a previous message left content in it. | ||
| if (!bodyType || !bodyOffset) { | ||
| this.#bufferBuilder.clear(); | ||
| } | ||
|
|
||
| const handlerIdOffset = this.#bufferBuilder.createString(handlerId ?? ''); | ||
|
|
||
| let notificationOffset: number; | ||
|
|
@@ -278,9 +292,6 @@ export class Channel extends EnhancedEventEmitter { | |
| // do not point to the builder buffer overriding others info. | ||
| const buffer = new Uint8Array(this.#bufferBuilder.asUint8Array()); | ||
|
|
||
| // Clear the buffer builder so it's reused for the next request. | ||
| this.#bufferBuilder.clear(); | ||
|
|
||
| if (buffer.byteLength > MESSAGE_MAX_LEN) { | ||
| logger.error(`notify() | notification too big [event:${Event[event]}]`); | ||
|
|
||
|
|
@@ -318,6 +329,13 @@ export class Channel extends EnhancedEventEmitter { | |
| } | ||
|
|
||
| const id = this.#nextId; | ||
|
|
||
| // If there is no body then the buffer builder holds nothing we need, so | ||
| // reset it in case a previous message left content in it. | ||
| if (!bodyType || !bodyOffset) { | ||
| this.#bufferBuilder.clear(); | ||
| } | ||
|
|
||
| const handlerIdOffset = this.#bufferBuilder.createString(handlerId ?? ''); | ||
|
|
||
| let requestOffset: number; | ||
|
|
@@ -355,9 +373,6 @@ export class Channel extends EnhancedEventEmitter { | |
| // do not point to the builder buffer overriding others info. | ||
| const buffer = new Uint8Array(this.#bufferBuilder.asUint8Array()); | ||
|
|
||
| // Clear the buffer builder so it's reused for the next request. | ||
| this.#bufferBuilder.clear(); | ||
|
|
||
| if (buffer.byteLength > MESSAGE_MAX_LEN) { | ||
| throw new Error(`request too big [method:${Method[method]}]`); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -37,6 +37,10 @@ const DynamicPayloadTypes = [ | |
| 99, | ||
| ]; | ||
|
|
||
| // Biggest value that fits in an uint64, which is what the worker expects for | ||
| // bitrate related fields. | ||
| const MaxUint64 = 2n ** 64n - 1n; | ||
|
|
||
| // TODO: Remove this if we switch to 'sendrecv' in Dependency-Descriptor header | ||
| // extension. | ||
| // | ||
|
|
@@ -1310,6 +1314,29 @@ function validateAndNormalizeRtpEncodingParameters( | |
| ) { | ||
| throw new TypeError('invalid encoding.scalabilityMode'); | ||
| } | ||
|
|
||
| // maxBitrate is optional, it can be a number or a bigint. | ||
| const maxBitrate: number | bigint | undefined = encoding.maxBitrate; | ||
|
|
||
| if (!maxBitrate) { | ||
|
Member
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 is not valid since 0 is a valid value but it enters this condition. It works by accident but this solution is not valid for other similar cases such as transport.setMaxIncomingBitrate(bitrate) where 0 means "no limit". |
||
| delete encoding.maxBitrate; | ||
| } else if (typeof maxBitrate === 'bigint') { | ||
| if (maxBitrate < 0n || maxBitrate > MaxUint64) { | ||
| throw new TypeError('invalid encoding.maxBitrate'); | ||
| } | ||
| } else if (typeof maxBitrate === 'number') { | ||
| // NOTE: Compare as bigint. MaxUint64 rounds up to 2^64 if converted to a | ||
| // number, so an out of range 2^64 would wrongly be accepted. | ||
| if ( | ||
| !Number.isInteger(maxBitrate) || | ||
| maxBitrate < 0 || | ||
| BigInt(maxBitrate) > MaxUint64 | ||
| ) { | ||
| throw new TypeError('invalid encoding.maxBitrate'); | ||
| } | ||
| } else { | ||
| throw new TypeError('invalid encoding.maxBitrate'); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -194,7 +194,7 @@ export function serializeRtpEncodingParameters( | |
| } | ||
|
|
||
| // Add max bitrate. | ||
| if (encoding.maxBitrate !== undefined) { | ||
| if (encoding.maxBitrate) { | ||
|
Member
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. Again this condition breaks the valid case of value 0. |
||
| FbsRtpEncodingParameters.addMaxBitrate( | ||
| builder, | ||
| BigInt(encoding.maxBitrate) | ||
|
|
||
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 love this solution because it basically means that, in case of corrupted buffer, we leave it that way until the next time we need to use the buffer.