From 8f727be2996479de35fadcd27cedd2c149a2b8f1 Mon Sep 17 00:00:00 2001 From: achingbrain Date: Wed, 4 Sep 2024 11:18:27 +0100 Subject: [PATCH] fix: revert WebRTC message size increase go-libp2p and rust-libp2p close incoming streams when the message size is greater than 16KiB so reset to the previous limit. --- packages/transport-webrtc/package.json | 1 + packages/transport-webrtc/src/stream.ts | 39 ++++++++++++++----- packages/transport-webrtc/test/stream.spec.ts | 8 +++- 3 files changed, 36 insertions(+), 12 deletions(-) diff --git a/packages/transport-webrtc/package.json b/packages/transport-webrtc/package.json index c5acb44444..95eea506c9 100644 --- a/packages/transport-webrtc/package.json +++ b/packages/transport-webrtc/package.json @@ -72,6 +72,7 @@ "protons-runtime": "^5.4.0", "race-signal": "^1.0.2", "react-native-webrtc": "^118.0.7", + "uint8-varint": "^2.0.4", "uint8arraylist": "^2.4.8", "uint8arrays": "^5.1.0" }, diff --git a/packages/transport-webrtc/src/stream.ts b/packages/transport-webrtc/src/stream.ts index f6ec3c87fa..e7433255ab 100644 --- a/packages/transport-webrtc/src/stream.ts +++ b/packages/transport-webrtc/src/stream.ts @@ -6,6 +6,7 @@ import pDefer from 'p-defer' import { pEvent, TimeoutError } from 'p-event' import pTimeout from 'p-timeout' import { raceSignal } from 'race-signal' +import { encodingLength } from 'uint8-varint' import { Uint8ArrayList } from 'uint8arraylist' import { Message } from './pb/message.js' import type { DataChannelOptions } from './index.js' @@ -35,22 +36,40 @@ export const MAX_BUFFERED_AMOUNT = 2 * 1024 * 1024 export const BUFFERED_AMOUNT_LOW_TIMEOUT = 30 * 1000 /** - * protobuf field definition overhead + length encoding prefix length + * Max message size that can be sent to the DataChannel. In browsers this is + * 256KiB but go-libp2p and rust-libp2p only support 16KiB at the time of + * writing. + * + * @see https://blog.mozilla.org/webrtc/large-data-channel-messages/ + * @see https://issues.webrtc.org/issues/40644524 */ -export const PROTOBUF_OVERHEAD = 7 +export const MAX_MESSAGE_SIZE = 16 * 1024 /** - * Length of varint, in bytes + * max protobuf overhead: + * + * ``` + * [message-length][flag-field-id+type][flag-field-length][flag-field][message-field-id+type][message-field-length][message-field] + * ``` */ -export const VARINT_LENGTH = 2 +function calculateProtobufOverhead (maxMessageSize = MAX_MESSAGE_SIZE): number { + // these have a fixed size + const messageLength = encodingLength(maxMessageSize - encodingLength(maxMessageSize)) + const flagField = 1 + encodingLength(Object.keys(Message.Flag).length - 1) // id+type/value + const messageFieldIdType = 1 // id+type + const available = maxMessageSize - messageLength - flagField - messageFieldIdType + + // let message-length/message-data fill the rest of the message + const messageFieldLengthLength = encodingLength(available) + + return messageLength + flagField + messageFieldIdType + messageFieldLengthLength +} /** - * Max message size that can be sent to the DataChannel - * - * @see https://blog.mozilla.org/webrtc/large-data-channel-messages/ - * @see https://issues.webrtc.org/issues/40644524 + * The protobuf message overhead includes the maximum amount of all bytes in the + * protobuf that aren't message field bytes */ -export const MAX_MESSAGE_SIZE = 256 * 1024 +export const PROTOBUF_OVERHEAD = calculateProtobufOverhead() /** * When closing streams we send a FIN then wait for the remote to @@ -132,7 +151,7 @@ export class WebRTCStream extends AbstractStream { this.incomingData = pushable() this.bufferedAmountLowEventTimeout = init.bufferedAmountLowEventTimeout ?? BUFFERED_AMOUNT_LOW_TIMEOUT this.maxBufferedAmount = init.maxBufferedAmount ?? MAX_BUFFERED_AMOUNT - this.maxMessageSize = (init.maxMessageSize ?? MAX_MESSAGE_SIZE) - PROTOBUF_OVERHEAD - VARINT_LENGTH + this.maxMessageSize = (init.maxMessageSize ?? MAX_MESSAGE_SIZE) - PROTOBUF_OVERHEAD this.receiveFinAck = pDefer() this.finAckTimeout = init.closeTimeout ?? FIN_ACK_TIMEOUT this.openTimeout = init.openTimeout ?? OPEN_TIMEOUT diff --git a/packages/transport-webrtc/test/stream.spec.ts b/packages/transport-webrtc/test/stream.spec.ts index cb40e8912e..7460cfeef6 100644 --- a/packages/transport-webrtc/test/stream.spec.ts +++ b/packages/transport-webrtc/test/stream.spec.ts @@ -26,8 +26,12 @@ describe('Max message size', () => { } }) - // Make sure that the data that ought to be sent will result in a message with exactly MAX_MESSAGE_SIZE - const messageLengthEncoded = lengthPrefixed.encode.single(Message.encode({ message: data })) + // Make sure that a message with all fields will be exactly MAX_MESSAGE_SIZE + const messageLengthEncoded = lengthPrefixed.encode.single(Message.encode({ + flag: Message.Flag.STOP_SENDING, + message: data + })) + expect(messageLengthEncoded.length).eq(MAX_MESSAGE_SIZE) const webrtcStream = createStream({ channel,