- From: Randell Jesup <randell-ietf@jesup.org>
- Date: Fri, 31 Aug 2012 17:05:35 -0400
- To: "public-webrtc@w3.org >> \"public-webrtc@w3.org\"" <public-webrtc@w3.org>
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).
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(...);
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.)
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.
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?
(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);
};
--
Randell Jesup
randell-ietf@jesup.org
Received on Friday, 31 August 2012 21:06:39 UTC