Many application developers who utilise WebRTC data channel know that there is a message size limitation. Some have experimentally tried to find the maximum message size. However, it seems that noone has ever looked deep enough to find out why that message size limitation exists in the first place and why sending messages from Firefox to Chromium in particular dictates a bizarrely low message size. So, I've read through most of Firefox's and Chromium's SCTP data channel implementations. And here are my results.
Chromium unintentionally fragments messages. Rather than implementing
fragmentation/reassembly on the (now deprecated) SCTP PPID (payload protocol
identifier) level Chromium simply passes the data as is to the
usrsctp (the SCTP implementation used by both Chromium and Firefox)
stack. Now, behaviour depends on usrsctp: When a message is greater than 256
KiB (which is usrsctp's default maximum buffer size), Chromium simply closes
the data channel, because usrsctp returns
EMSGSIZE when calling
usrsctp_sendv which the Chromium code is not able to handle. It simply closes
the data channel in that case. Messages up to 256 KiB are split up into very
small packets by usrsctp. These packets are then encrypted and sent on the DTLS
layer to the recipient.
On the receiver side, usrsctp buffers the small chunks until the buffer reaches the partial delivery point (which is ~64 KiB at default). It then passes the still incomplete message to the browser. Chromium is supposed to buffer the message and wait for the EOR (end of record) flag which indicates that a message is complete. However, Chromium ignores that flag and simply delivers the incomplete message to the user application, violating the message-oriented principle.
Furthermore, Chromium does not check the return code of
EAGAIN which means that sending a data channel message via Chromium can
potentially lead to arbitrarily dropped packets when the internal buffer of
usrsctp is full. Theoretically this means that you do not know how much data
you can reliably send. However, I've not encountered this particular type of
error so far, so there's probably enough time for usrsctp between send calls to
deliver buffered data and clear the buffers. Still, be aware that this might be
Current Firefox versions have two modes for sending data:
If the data channel is reliable and ordered, Firefox uses a deprecated fragmentation/reassembly on SCTP PPID level. In this mode, Firefox can send an unlimited amount of data to another Firefox browser (or someone who has also implemented that deprecated fragmentation and reassembly mechanism). This has an unintended side-effect for Chromium browsers explained in the following section.
If the data channel is not reliable or not ordered, Firefox behaves the same as Chromium, including the EOR bug. However, Firefox does not close the data channel on
EMSGSIZEand also handles
EAGAIN. Older Firefox versions simply discard messages on
EMSGSIZEwithout raising an error event at all (ouch!). Read the Chromium section above for details on the EOR bug.
Demystifying the 16 KiB Firefox to Chromium Message Size Limit
First of all, this only applies to ordered and reliable channels! For unordered or unreliable channels, the limit remains 64 KiB.
To demistify why Firefox cannot send more than 16 KiB to Chromium as stated
here, you'll have to understand that Chromium does not
implement the deprecated fragmentation/reassembly implemented by Firefox for
ordered and reliable channels.
As Firefox fragments the messages into 16 KiB chunks, it sends the first chunks
with a different SCTP PPID (
52 for Binary Partial or
54 for String
Partial) than the last one (
53 for Binary or
51 for String).
However, Chromium uses the same handling mechanism for packets with PPID
51, so it receives the packets one by one as sent by
Firefox. Thus, the application gets 16 KiB messages because that's the chunk
size Firefox uses to fragment the message. Chromium just does not reassemble it.
Can We Fix It?
Well, yes, but we'll have to be patient. The following subsections indicate what browser vendors have to do to get rid of the size limitation or at least increase the limit.
> 16 KiB
Either Firefox needs to drop support for the already deprecated fragmentation/reassembly at SCTP PPID level or Chromium needs to implement it. This is particularly annoying because it's not as easy to fix as the latter one but remains the lowest common denominator for Firefox and Chromium interoperation until it has been resolved.
> 64 KiB
Both Firefox and Chromium have to obey the EOR flag to receive messages as they were sent. This is an easy fix.
> 256 KiB
While the browsers could simply increase the buffer of usrsctp, this is not a
good idea. A better solution would be to use the EOR flag when passing messages
to usrsctp via a call to
usrsctp_sendv. Again, should be an easy fix.
But there's another problem and this is most probably the reason why it hasn't been done yet: A very big data channel message would lead to head-of-line blocking for messages sent by other data channels. To solve that problem, the SCTP ndata specification has been created. Now, browser vendors wait until it is stable before it's being activated in the browsers. This will take a bit of time.
Thanks to Michael Tüxen for answering my questions regarding usrsctp.
TL;DR, Gimme Instructions!
My tests and the code of both Chromium and Firefox indicate that it's safe to send 64 KiB of data between current versions of Firefox and Chromium unless you're sending from Firefox to Chromium on an ordered and reliable data channel where 16 KiB is the maximum message size. You'll have to fragment and reassemble on application level.
For full support for both reliable and unreliable, ordered and unordered
channels, use the SaltyRTC's chunking specification
Java. Set the chunk size to
16384 if you want to be safe.
Otherwise, use a chunk size of
65536 in case you're sure that there'll never
be a Firefox browser talking to a Chromium browser. You could also implement a
detection mechanism for that. If you're doing that, the maximum message size
needs to be distributed to the other peer because the problem occurs on the
And while you're at it, consider using SaltyRTC for seriously secure signalling to protect your WebRTC peer-to-peer connection. :)