Re: DataChannels API

On 08/31/2012 11:05 PM, Randell Jesup wrote:
> So, on a totally different topic:
>
> We discussed the DataChannels API at the Stockholm Interim.  I'd like to
> move this part of the spec forward, as Mozilla has an implementation in
> testing.
>
> The API, per the decisions at the Interim and mailing list is based
> closely on the WebSockets interface.  For example, you are supposed to
> wait for onopen to fire before calling send(), at both ends. (The
> low-level IETF protocol supports sending before onopen, but WebSockets
> doesn't.)  Currently I have no limit on send(DOMString); WebSockets
> limits DOMStrings to 123 characters.
>
> I should note that the immediately-following IETF Interim appeared to
> have the opposite conclusion on onopen and send(), and so the IETF
> protocol design allows sending immediately, while the API I propose for
> WebRTC DataChannels here does not.
>
> Supported channel types are reliable (and and out of order) channels,
> unreliable and partly-reliable channels.  (See the dictionary below.)
>
> I'd *REALLY* like to move forward on resolving the open issues listed
> below, as we're preparing to expose a preliminary DataChannels
> implementation.   Thanks.
>
> Open Issues:
> ==========
>
> When can createDataChannel() be called?
> ----------------------------------------------------------
>
> Currently pc.createDataChannel() must be called after onconnected
> fires.  This was a point of discussion at the meeting and on the rtcweb
> list, since DataChannels need some RTTs to establish; allowing the
> application to pre-request data channels would allow them to be included
> in the signaling (and thus save 1 RTT off the setup time).

I think this makes sense.

>
> In order to allow pc.createDataChannel() to be called earlier the only
> difference at the offerer's JS API level would be whether you can do:
>
>      pc = new PeerConnection(...);
>      pc.addStream(...);
>      pc.createDataChannel("foo",{});
>      pc.createOffer(...);

or just (if you're only going to do data):

     pc = new PeerConnection(...);
     pc.createDataChannel("foo",{});
  === "negotiationneeded" fires ===
     pc.createOffer(...);

I don't see a reason why you would need to create a PeerConnection, wait 
for onopen, and then createDataChannel.




>
> etc.  On the receive side, there's no way to reject a DataChannel (just
> as there's no way to reject one before it's created after the
> PeerConnection is connected).  You can avoid setting onDataChannel or
> respond to onDataChannel by closing it if you don't want it.  The
> receiver would be able to call pc.createDataChannel() before
> createAnswer().  In all cases the channel isn't usable until onopen fires.
>
>
> Do we need a DataConnection object?
> -----------------------------------------------------
>
> It was suggested that we have a DataConnection object, and hang
> createDataChannel off of that.  (see the thread "DataConnection objects"
> from June.)  Doing so would minorly reduce the number of methods on
> PeerConnection (by moving onconnection/onclosedconnection/ondatachannel
> to it, and maybe an attribute for initial number of streams).
>
> Pros:
> * It gives you a clear way to easily drop all DataChannels.
> * It gives you an easy point to query the association (mostly for
> maxstreams I'd guess, perhaps for total bytes queued across all
> DataChannels).
> * A spec for a DataConnection object might be useful in some other
> context in the future - but it always could be added later if needed.
> (You could even indicate that PeerConnection implements DataConnection
> if you wanted.)
> * It does give you a clear way to say "Don't bother to open an
> association" if you don't need one.
> * You could add more DataConnections to a PeerConnections (but what does
> that gain you?)
> * Allows future extension to allow other protocols to be used over the
> connection/association.
> * Gives you something to hang an application identifier off of - but the
> application can do that in other ways.
>
> Cons:
> * More objects, more code required to set up a call with little benefit
> to the current usecases.
> * If you want to add a DataConnection later, it will require a renegotiation
> * Answering requires a few extra steps to see if the offerer offered a
> DataConnection.
>
> Overall, I'm very mildly in favor of adding the DataConnection object,
> because it might be useful in some other contexts in the future.  I do
> think it complicates PeerConnection slightly, but only slightly  (less
> methods, but PeerConnection now needs to trigger renegotiation when
> createDataConnection is called.)

I don't have a strong view.

>
>
> Label glare:
> ---------------
> A previous discussion ("Data API symmetry", in April/May) covered this.
> This is the "what happens if both sides call createDataChannel("foo",{})
> at the same time" question.  You can:
>
> 1) Create two channels labelled "foo".  Each side would get an onopen to
> their createDataChannel, and each would get an onDataChannel and onopen
> for the one created by the other side. Handling the glare would be the
> application's domain.
> 2) Fail both (or find some agreed-on tiebreaker that lets you have one
> fail and the other succeed).
> 3) Create one channel labelled "foo", and each side would believe they
> created it.  Both would simply get "onopen", and it might even come
> faster than normal.  NOTE: if the two sides select different options for
> the channel, then you still may need to return errors! (or create
> channels with the same label)
>
> #3 is mildly appealing, until you think about how you handle errors with
> disagreement on reliability.  So I think I end up preferring #1.  #3
> also implies you should have a unique label for each channel, to avoid
> confusions with opening multiple channels with the same name at the same
> time when the other side tries to as well.

+1, I also think #1 makes most sense.

>
>
> Attributes:
> -------------
> Is the single "reliable" attribute enough?  Do we need to expose the
> dict entries?  Do we need a FindDataChannel(label) to get a reference to
> an existing channel?

To me, the single "reliable" attribute seems enough for a first version, 
but if others want more it should be added (after all, a reason for 
using a dictionary was to enable the addition of more things as we see 
need).

>
> (My opinion: Probably no to all of these, though I can see exposing the
> dict entries.)
>
>
>
> This is the dictionary for RTCPeerConnection's createDataChannel()
> method: (xpidl)
>
> /* If either maxRetransmitTime or maxRetransmitNum are set, it's
>      unreliable, else it's a reliable channel.  If both are set it's an
>      error.  outOfOrderAllowed can be used with any type of channel.  The
>      equivalent of UDP is { outOfOrderAllowed: true, maxRetransmitNum: 0 }.
>      The TCP equivalent is {}. */
>
> dictionary DataChannelInit {
>     boolean outOfOrderAllowed;
>     unsigned short maxRetransmitTime; // in ms
>     unsigned short maxRetransmitNum;
> };
>
>
> And in PeerConnection: (xpidl)
>
>     /* Data channel */
>     nsIDOMDataChannel createDataChannel([optional] in ACString label,
>                                         /* DataChannelInit */ [optional] in jsval options);
>     attribute RTCPeerConnectionCallbackVoid onConnection;
>     attribute RTCPeerConnectionCallbackVoid onClosedConnection;
>     attribute RTCPeerConnectionCallback onDataChannel;
>
>
> This is the webidl for DataChannel I have currently (note: it might have
> errors as we use xpidl internally, though we're switching to webidl).
>
> interface DataChannel : EventTarget {
>     [Infallible]
>     readonly attribute DOMString label;
>
>     [Infallible]
>     readonly attribute boolean reliable;
>
>     // ready state
>     const unsigned short CONNECTING = 0;
>     const unsigned short OPEN = 1;
>     const unsigned short CLOSING = 2;
>     const unsigned short CLOSED = 3;
>
>     [Infallible]
>     readonly attribute unsigned short readyState;
>
>     [Infallible]
>     readonly attribute unsigned long bufferedAmount;
>
>     [TreatNonCallableAsNull, GetterInfallible]
>     attribute Function? onopen;
>
>     [TreatNonCallableAsNull, GetterInfallible]
>     attribute Function? onerror;
>
>     [TreatNonCallableAsNull, GetterInfallible]
>     attribute Function? onclose;
>
>     [Infallible]
>     readonly attribute DOMString extensions;
>
>     [Infallible]
>     readonly attribute DOMString protocol;
>
>     void close();
>
>     // messaging
>     [TreatNonCallableAsNull, GetterInfallible]
>     attribute Function? onmessage;
>
>     [GetterInfallible]
>     attribute DOMString binaryType;
>
>     void send(DOMString data);
>     void send(Blob data);
>     void send(ArrayBuffer data);
> };
>
>

Received on Monday, 3 September 2012 13:27:54 UTC