|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim: set ts=2 et sw=2 tw=80: */ |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #ifndef NETWERK_SCTP_DATACHANNEL_DATACHANNEL_H_ |
|
8 #define NETWERK_SCTP_DATACHANNEL_DATACHANNEL_H_ |
|
9 |
|
10 #ifdef MOZ_WEBRTC_SIGNALING |
|
11 #define SCTP_DTLS_SUPPORTED 1 |
|
12 #endif |
|
13 |
|
14 #include <string> |
|
15 #include <errno.h> |
|
16 #include "nsISupports.h" |
|
17 #include "nsCOMPtr.h" |
|
18 #include "mozilla/WeakPtr.h" |
|
19 #include "nsString.h" |
|
20 #include "nsThreadUtils.h" |
|
21 #include "nsTArray.h" |
|
22 #include "nsDeque.h" |
|
23 #include "nsIInputStream.h" |
|
24 #include "nsITimer.h" |
|
25 #include "mozilla/Mutex.h" |
|
26 #include "DataChannelProtocol.h" |
|
27 #include "DataChannelListener.h" |
|
28 #ifdef SCTP_DTLS_SUPPORTED |
|
29 #include "mtransport/sigslot.h" |
|
30 #include "mtransport/transportflow.h" |
|
31 #include "mtransport/transportlayer.h" |
|
32 #include "mtransport/transportlayerdtls.h" |
|
33 #include "mtransport/transportlayerprsock.h" |
|
34 #endif |
|
35 |
|
36 #ifndef DATACHANNEL_LOG |
|
37 #define DATACHANNEL_LOG(args) |
|
38 #endif |
|
39 |
|
40 #ifndef EALREADY |
|
41 #define EALREADY WSAEALREADY |
|
42 #endif |
|
43 |
|
44 extern "C" { |
|
45 struct socket; |
|
46 struct sctp_rcvinfo; |
|
47 } |
|
48 |
|
49 namespace mozilla { |
|
50 |
|
51 class DTLSConnection; |
|
52 class DataChannelConnection; |
|
53 class DataChannel; |
|
54 class DataChannelOnMessageAvailable; |
|
55 |
|
56 // For queuing outgoing messages |
|
57 class BufferedMsg |
|
58 { |
|
59 public: |
|
60 BufferedMsg(struct sctp_sendv_spa &spa,const char *data, |
|
61 uint32_t length); |
|
62 ~BufferedMsg(); |
|
63 |
|
64 struct sctp_sendv_spa *mSpa; |
|
65 const char *mData; |
|
66 uint32_t mLength; |
|
67 }; |
|
68 |
|
69 // for queuing incoming data messages before the Open or |
|
70 // external negotiation is indicated to us |
|
71 class QueuedDataMessage |
|
72 { |
|
73 public: |
|
74 QueuedDataMessage(uint16_t stream, uint32_t ppid, |
|
75 const void *data, size_t length) |
|
76 : mStream(stream) |
|
77 , mPpid(ppid) |
|
78 , mLength(length) |
|
79 { |
|
80 mData = static_cast<char *>(moz_xmalloc(length)); // infallible |
|
81 memcpy(mData, data, length); |
|
82 } |
|
83 |
|
84 ~QueuedDataMessage() |
|
85 { |
|
86 moz_free(mData); |
|
87 } |
|
88 |
|
89 uint16_t mStream; |
|
90 uint32_t mPpid; |
|
91 size_t mLength; |
|
92 char *mData; |
|
93 }; |
|
94 |
|
95 // One per PeerConnection |
|
96 class DataChannelConnection: public nsITimerCallback |
|
97 #ifdef SCTP_DTLS_SUPPORTED |
|
98 , public sigslot::has_slots<> |
|
99 #endif |
|
100 { |
|
101 public: |
|
102 NS_DECL_THREADSAFE_ISUPPORTS |
|
103 NS_DECL_NSITIMERCALLBACK |
|
104 |
|
105 class DataConnectionListener : public SupportsWeakPtr<DataConnectionListener> |
|
106 { |
|
107 public: |
|
108 MOZ_DECLARE_REFCOUNTED_TYPENAME(DataChannelConnection::DataConnectionListener) |
|
109 virtual ~DataConnectionListener() {} |
|
110 |
|
111 // Called when a the connection is open |
|
112 virtual void NotifyConnection() = 0; |
|
113 |
|
114 // Called when a the connection is lost/closed |
|
115 virtual void NotifyClosedConnection() = 0; |
|
116 |
|
117 // Called when a new DataChannel has been opened by the other side. |
|
118 virtual void NotifyDataChannel(already_AddRefed<DataChannel> channel) = 0; |
|
119 }; |
|
120 |
|
121 DataChannelConnection(DataConnectionListener *listener); |
|
122 virtual ~DataChannelConnection(); |
|
123 |
|
124 bool Init(unsigned short aPort, uint16_t aNumStreams, bool aUsingDtls); |
|
125 void Destroy(); // So we can spawn refs tied to runnables in shutdown |
|
126 // Finish Destroy on STS to avoid SCTP race condition with ABORT from far end |
|
127 void DestroyOnSTS(struct socket *aMasterSocket, |
|
128 struct socket *aSocket); |
|
129 |
|
130 #ifdef ALLOW_DIRECT_SCTP_LISTEN_CONNECT |
|
131 // These block; they require something to decide on listener/connector |
|
132 // (though you can do simultaneous Connect()). Do not call these from |
|
133 // the main thread! |
|
134 bool Listen(unsigned short port); |
|
135 bool Connect(const char *addr, unsigned short port); |
|
136 #endif |
|
137 |
|
138 #ifdef SCTP_DTLS_SUPPORTED |
|
139 // Connect using a TransportFlow (DTLS) channel |
|
140 void SetEvenOdd(); |
|
141 bool ConnectViaTransportFlow(TransportFlow *aFlow, uint16_t localport, uint16_t remoteport); |
|
142 void CompleteConnect(TransportFlow *flow, TransportLayer::State state); |
|
143 void SetSignals(); |
|
144 #endif |
|
145 |
|
146 typedef enum { |
|
147 RELIABLE=0, |
|
148 PARTIAL_RELIABLE_REXMIT = 1, |
|
149 PARTIAL_RELIABLE_TIMED = 2 |
|
150 } Type; |
|
151 |
|
152 already_AddRefed<DataChannel> Open(const nsACString& label, |
|
153 const nsACString& protocol, |
|
154 Type type, bool inOrder, |
|
155 uint32_t prValue, |
|
156 DataChannelListener *aListener, |
|
157 nsISupports *aContext, |
|
158 bool aExternalNegotiated, |
|
159 uint16_t aStream) NS_WARN_UNUSED_RESULT; |
|
160 |
|
161 void Close(DataChannel *aChannel); |
|
162 // CloseInt() must be called with mLock held |
|
163 void CloseInt(DataChannel *aChannel); |
|
164 void CloseAll(); |
|
165 |
|
166 int32_t SendMsg(uint16_t stream, const nsACString &aMsg) |
|
167 { |
|
168 return SendMsgCommon(stream, aMsg, false); |
|
169 } |
|
170 int32_t SendBinaryMsg(uint16_t stream, const nsACString &aMsg) |
|
171 { |
|
172 return SendMsgCommon(stream, aMsg, true); |
|
173 } |
|
174 int32_t SendBlob(uint16_t stream, nsIInputStream *aBlob); |
|
175 |
|
176 // Called on data reception from the SCTP library |
|
177 // must(?) be public so my c->c++ trampoline can call it |
|
178 int ReceiveCallback(struct socket* sock, void *data, size_t datalen, |
|
179 struct sctp_rcvinfo rcv, int32_t flags); |
|
180 |
|
181 // Find out state |
|
182 enum { |
|
183 CONNECTING = 0U, |
|
184 OPEN = 1U, |
|
185 CLOSING = 2U, |
|
186 CLOSED = 3U |
|
187 }; |
|
188 uint16_t GetReadyState() { MutexAutoLock lock(mLock); return mState; } |
|
189 |
|
190 friend class DataChannel; |
|
191 Mutex mLock; |
|
192 |
|
193 void ReadBlob(already_AddRefed<DataChannelConnection> aThis, uint16_t aStream, nsIInputStream* aBlob); |
|
194 |
|
195 void GetStreamIds(std::vector<uint16_t>* aStreamList); |
|
196 |
|
197 protected: |
|
198 friend class DataChannelOnMessageAvailable; |
|
199 // Avoid cycles with PeerConnectionImpl |
|
200 // Use from main thread only as WeakPtr is not threadsafe |
|
201 WeakPtr<DataConnectionListener> mListener; |
|
202 |
|
203 private: |
|
204 friend class DataChannelConnectRunnable; |
|
205 |
|
206 #ifdef SCTP_DTLS_SUPPORTED |
|
207 static void DTLSConnectThread(void *data); |
|
208 int SendPacket(const unsigned char* data, size_t len, bool release); |
|
209 void SctpDtlsInput(TransportFlow *flow, const unsigned char *data, size_t len); |
|
210 static int SctpDtlsOutput(void *addr, void *buffer, size_t length, uint8_t tos, uint8_t set_df); |
|
211 #endif |
|
212 DataChannel* FindChannelByStream(uint16_t stream); |
|
213 uint16_t FindFreeStream(); |
|
214 bool RequestMoreStreams(int32_t aNeeded = 16); |
|
215 int32_t SendControlMessage(void *msg, uint32_t len, uint16_t stream); |
|
216 int32_t SendOpenRequestMessage(const nsACString& label, const nsACString& protocol, |
|
217 uint16_t stream, |
|
218 bool unordered, uint16_t prPolicy, uint32_t prValue); |
|
219 int32_t SendOpenAckMessage(uint16_t stream); |
|
220 int32_t SendMsgInternal(DataChannel *channel, const char *data, |
|
221 uint32_t length, uint32_t ppid); |
|
222 int32_t SendBinary(DataChannel *channel, const char *data, |
|
223 uint32_t len, uint32_t ppid_partial, uint32_t ppid_final); |
|
224 int32_t SendMsgCommon(uint16_t stream, const nsACString &aMsg, bool isBinary); |
|
225 |
|
226 void DeliverQueuedData(uint16_t stream); |
|
227 |
|
228 already_AddRefed<DataChannel> OpenFinish(already_AddRefed<DataChannel>&& aChannel); |
|
229 |
|
230 void StartDefer(); |
|
231 bool SendDeferredMessages(); |
|
232 void ProcessQueuedOpens(); |
|
233 void ClearResets(); |
|
234 void SendOutgoingStreamReset(); |
|
235 void ResetOutgoingStream(uint16_t stream); |
|
236 void HandleOpenRequestMessage(const struct rtcweb_datachannel_open_request *req, |
|
237 size_t length, |
|
238 uint16_t stream); |
|
239 void HandleOpenAckMessage(const struct rtcweb_datachannel_ack *ack, |
|
240 size_t length, uint16_t stream); |
|
241 void HandleUnknownMessage(uint32_t ppid, size_t length, uint16_t stream); |
|
242 void HandleDataMessage(uint32_t ppid, const void *buffer, size_t length, uint16_t stream); |
|
243 void HandleMessage(const void *buffer, size_t length, uint32_t ppid, uint16_t stream); |
|
244 void HandleAssociationChangeEvent(const struct sctp_assoc_change *sac); |
|
245 void HandlePeerAddressChangeEvent(const struct sctp_paddr_change *spc); |
|
246 void HandleRemoteErrorEvent(const struct sctp_remote_error *sre); |
|
247 void HandleShutdownEvent(const struct sctp_shutdown_event *sse); |
|
248 void HandleAdaptationIndication(const struct sctp_adaptation_event *sai); |
|
249 void HandleSendFailedEvent(const struct sctp_send_failed_event *ssfe); |
|
250 void HandleStreamResetEvent(const struct sctp_stream_reset_event *strrst); |
|
251 void HandleStreamChangeEvent(const struct sctp_stream_change_event *strchg); |
|
252 void HandleNotification(const union sctp_notification *notif, size_t n); |
|
253 |
|
254 #ifdef SCTP_DTLS_SUPPORTED |
|
255 bool IsSTSThread() { |
|
256 bool on = false; |
|
257 if (mSTS) { |
|
258 mSTS->IsOnCurrentThread(&on); |
|
259 } |
|
260 return on; |
|
261 } |
|
262 #endif |
|
263 |
|
264 // Exists solely for proxying release of the TransportFlow to the STS thread |
|
265 static void ReleaseTransportFlow(nsRefPtr<TransportFlow> aFlow) {} |
|
266 |
|
267 // Data: |
|
268 // NOTE: while this array will auto-expand, increases in the number of |
|
269 // channels available from the stack must be negotiated! |
|
270 bool mAllocateEven; |
|
271 nsAutoTArray<nsRefPtr<DataChannel>,16> mStreams; |
|
272 nsDeque mPending; // Holds already_AddRefed<DataChannel>s -- careful! |
|
273 // holds data that's come in before a channel is open |
|
274 nsTArray<nsAutoPtr<QueuedDataMessage> > mQueuedData; |
|
275 |
|
276 // Streams pending reset |
|
277 nsAutoTArray<uint16_t,4> mStreamsResetting; |
|
278 |
|
279 struct socket *mMasterSocket; // accessed from STS thread |
|
280 struct socket *mSocket; // cloned from mMasterSocket on successful Connect on STS thread |
|
281 uint16_t mState; // Protected with mLock |
|
282 |
|
283 #ifdef SCTP_DTLS_SUPPORTED |
|
284 nsRefPtr<TransportFlow> mTransportFlow; |
|
285 nsCOMPtr<nsIEventTarget> mSTS; |
|
286 #endif |
|
287 uint16_t mLocalPort; // Accessed from connect thread |
|
288 uint16_t mRemotePort; |
|
289 bool mUsingDtls; |
|
290 |
|
291 // Timer to control when we try to resend blocked messages |
|
292 nsCOMPtr<nsITimer> mDeferredTimer; |
|
293 uint32_t mDeferTimeout; // in ms |
|
294 bool mTimerRunning; |
|
295 nsCOMPtr<nsIThread> mInternalIOThread; |
|
296 }; |
|
297 |
|
298 #define ENSURE_DATACONNECTION \ |
|
299 do { if (!mConnection) { DATACHANNEL_LOG(("%s: %p no connection!",__FUNCTION__, this)); return; } } while (0) |
|
300 |
|
301 #define ENSURE_DATACONNECTION_RET(x) \ |
|
302 do { if (!mConnection) { DATACHANNEL_LOG(("%s: %p no connection!",__FUNCTION__, this)); return (x); } } while (0) |
|
303 |
|
304 class DataChannel { |
|
305 public: |
|
306 enum { |
|
307 CONNECTING = 0U, |
|
308 OPEN = 1U, |
|
309 CLOSING = 2U, |
|
310 CLOSED = 3U, |
|
311 WAITING_TO_OPEN = 4U |
|
312 }; |
|
313 |
|
314 DataChannel(DataChannelConnection *connection, |
|
315 uint16_t stream, |
|
316 uint16_t state, |
|
317 const nsACString& label, |
|
318 const nsACString& protocol, |
|
319 uint16_t policy, uint32_t value, |
|
320 uint32_t flags, |
|
321 DataChannelListener *aListener, |
|
322 nsISupports *aContext) |
|
323 : mListenerLock("netwerk::sctp::DataChannel") |
|
324 , mListener(aListener) |
|
325 , mContext(aContext) |
|
326 , mConnection(connection) |
|
327 , mLabel(label) |
|
328 , mProtocol(protocol) |
|
329 , mState(state) |
|
330 , mReady(false) |
|
331 , mStream(stream) |
|
332 , mPrPolicy(policy) |
|
333 , mPrValue(value) |
|
334 , mFlags(flags) |
|
335 , mIsRecvBinary(false) |
|
336 { |
|
337 NS_ASSERTION(mConnection,"NULL connection"); |
|
338 } |
|
339 |
|
340 ~DataChannel(); |
|
341 void Destroy(); // when we disconnect from the connection after stream RESET |
|
342 |
|
343 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DataChannel) |
|
344 |
|
345 // Close this DataChannel. Can be called multiple times. MUST be called |
|
346 // before destroying the DataChannel (state must be CLOSED or CLOSING). |
|
347 void Close(); |
|
348 |
|
349 // Set the listener (especially for channels created from the other side) |
|
350 void SetListener(DataChannelListener *aListener, nsISupports *aContext); |
|
351 |
|
352 // Send a string |
|
353 bool SendMsg(const nsACString &aMsg) |
|
354 { |
|
355 ENSURE_DATACONNECTION_RET(false); |
|
356 |
|
357 if (mStream != INVALID_STREAM) |
|
358 return (mConnection->SendMsg(mStream, aMsg) > 0); |
|
359 else |
|
360 return false; |
|
361 } |
|
362 |
|
363 // Send a binary message (TypedArray) |
|
364 bool SendBinaryMsg(const nsACString &aMsg) |
|
365 { |
|
366 ENSURE_DATACONNECTION_RET(false); |
|
367 |
|
368 if (mStream != INVALID_STREAM) |
|
369 return (mConnection->SendBinaryMsg(mStream, aMsg) > 0); |
|
370 else |
|
371 return false; |
|
372 } |
|
373 |
|
374 // Send a binary blob |
|
375 bool SendBinaryStream(nsIInputStream *aBlob, uint32_t msgLen) |
|
376 { |
|
377 ENSURE_DATACONNECTION_RET(false); |
|
378 |
|
379 if (mStream != INVALID_STREAM) |
|
380 return (mConnection->SendBlob(mStream, aBlob) > 0); |
|
381 else |
|
382 return false; |
|
383 } |
|
384 |
|
385 uint16_t GetType() { return mPrPolicy; } |
|
386 |
|
387 bool GetOrdered() { return !(mFlags & DATA_CHANNEL_FLAGS_OUT_OF_ORDER_ALLOWED); } |
|
388 |
|
389 // Amount of data buffered to send |
|
390 uint32_t GetBufferedAmount(); |
|
391 |
|
392 // Find out state |
|
393 uint16_t GetReadyState() |
|
394 { |
|
395 if (mConnection) { |
|
396 MutexAutoLock lock(mConnection->mLock); |
|
397 if (mState == WAITING_TO_OPEN) |
|
398 return CONNECTING; |
|
399 return mState; |
|
400 } |
|
401 return CLOSED; |
|
402 } |
|
403 |
|
404 void GetLabel(nsAString& aLabel) { CopyUTF8toUTF16(mLabel, aLabel); } |
|
405 void GetProtocol(nsAString& aProtocol) { CopyUTF8toUTF16(mProtocol, aProtocol); } |
|
406 uint16_t GetStream() { return mStream; } |
|
407 |
|
408 void AppReady(); |
|
409 |
|
410 void SendOrQueue(DataChannelOnMessageAvailable *aMessage); |
|
411 |
|
412 protected: |
|
413 Mutex mListenerLock; // protects mListener and mContext |
|
414 DataChannelListener *mListener; |
|
415 nsCOMPtr<nsISupports> mContext; |
|
416 |
|
417 private: |
|
418 friend class DataChannelOnMessageAvailable; |
|
419 friend class DataChannelConnection; |
|
420 |
|
421 nsresult AddDataToBinaryMsg(const char *data, uint32_t size); |
|
422 |
|
423 nsRefPtr<DataChannelConnection> mConnection; |
|
424 nsCString mLabel; |
|
425 nsCString mProtocol; |
|
426 uint16_t mState; |
|
427 bool mReady; |
|
428 uint16_t mStream; |
|
429 uint16_t mPrPolicy; |
|
430 uint32_t mPrValue; |
|
431 uint32_t mFlags; |
|
432 uint32_t mId; |
|
433 bool mIsRecvBinary; |
|
434 nsCString mRecvBuffer; |
|
435 nsTArray<nsAutoPtr<BufferedMsg> > mBufferedData; |
|
436 nsTArray<nsCOMPtr<nsIRunnable> > mQueuedMessages; |
|
437 }; |
|
438 |
|
439 // used to dispatch notifications of incoming data to the main thread |
|
440 // Patterned on CallOnMessageAvailable in WebSockets |
|
441 // Also used to proxy other items to MainThread |
|
442 class DataChannelOnMessageAvailable : public nsRunnable |
|
443 { |
|
444 public: |
|
445 enum { |
|
446 ON_CONNECTION, |
|
447 ON_DISCONNECTED, |
|
448 ON_CHANNEL_CREATED, |
|
449 ON_CHANNEL_OPEN, |
|
450 ON_CHANNEL_CLOSED, |
|
451 ON_DATA, |
|
452 START_DEFER, |
|
453 }; /* types */ |
|
454 |
|
455 DataChannelOnMessageAvailable(int32_t aType, |
|
456 DataChannelConnection *aConnection, |
|
457 DataChannel *aChannel, |
|
458 nsCString &aData, // XXX this causes inefficiency |
|
459 int32_t aLen) |
|
460 : mType(aType), |
|
461 mChannel(aChannel), |
|
462 mConnection(aConnection), |
|
463 mData(aData), |
|
464 mLen(aLen) {} |
|
465 |
|
466 DataChannelOnMessageAvailable(int32_t aType, |
|
467 DataChannel *aChannel) |
|
468 : mType(aType), |
|
469 mChannel(aChannel) {} |
|
470 // XXX is it safe to leave mData/mLen uninitialized? This should only be |
|
471 // used for notifications that don't use them, but I'd like more |
|
472 // bulletproof compile-time checking. |
|
473 |
|
474 DataChannelOnMessageAvailable(int32_t aType, |
|
475 DataChannelConnection *aConnection, |
|
476 DataChannel *aChannel) |
|
477 : mType(aType), |
|
478 mChannel(aChannel), |
|
479 mConnection(aConnection) {} |
|
480 |
|
481 // for ON_CONNECTION/ON_DISCONNECTED |
|
482 DataChannelOnMessageAvailable(int32_t aType, |
|
483 DataChannelConnection *aConnection, |
|
484 bool aResult = true) |
|
485 : mType(aType), |
|
486 mConnection(aConnection), |
|
487 mResult(aResult) {} |
|
488 |
|
489 NS_IMETHOD Run() |
|
490 { |
|
491 MOZ_ASSERT(NS_IsMainThread()); |
|
492 switch (mType) { |
|
493 case ON_DATA: |
|
494 case ON_CHANNEL_OPEN: |
|
495 case ON_CHANNEL_CLOSED: |
|
496 { |
|
497 MutexAutoLock lock(mChannel->mListenerLock); |
|
498 if (!mChannel->mListener) { |
|
499 DATACHANNEL_LOG(("DataChannelOnMessageAvailable (%d) with null Listener!",mType)); |
|
500 return NS_OK; |
|
501 } |
|
502 |
|
503 switch (mType) { |
|
504 case ON_DATA: |
|
505 if (mLen < 0) { |
|
506 mChannel->mListener->OnMessageAvailable(mChannel->mContext, mData); |
|
507 } else { |
|
508 mChannel->mListener->OnBinaryMessageAvailable(mChannel->mContext, mData); |
|
509 } |
|
510 break; |
|
511 case ON_CHANNEL_OPEN: |
|
512 mChannel->mListener->OnChannelConnected(mChannel->mContext); |
|
513 break; |
|
514 case ON_CHANNEL_CLOSED: |
|
515 mChannel->mListener->OnChannelClosed(mChannel->mContext); |
|
516 break; |
|
517 } |
|
518 break; |
|
519 } |
|
520 case ON_DISCONNECTED: |
|
521 // If we've disconnected, make sure we close all the streams - from mainthread! |
|
522 mConnection->CloseAll(); |
|
523 // fall through |
|
524 case ON_CHANNEL_CREATED: |
|
525 case ON_CONNECTION: |
|
526 // WeakPtr - only used/modified/nulled from MainThread so we can use a WeakPtr here |
|
527 if (!mConnection->mListener) { |
|
528 DATACHANNEL_LOG(("DataChannelOnMessageAvailable (%d) with null Listener",mType)); |
|
529 return NS_OK; |
|
530 } |
|
531 switch (mType) { |
|
532 case ON_CHANNEL_CREATED: |
|
533 // important to give it an already_AddRefed pointer! |
|
534 mConnection->mListener->NotifyDataChannel(mChannel.forget()); |
|
535 break; |
|
536 case ON_CONNECTION: |
|
537 if (mResult) { |
|
538 mConnection->mListener->NotifyConnection(); |
|
539 } |
|
540 // FIX - on mResult false (failure) we should do something. Needs spec work here |
|
541 break; |
|
542 case ON_DISCONNECTED: |
|
543 mConnection->mListener->NotifyClosedConnection(); |
|
544 break; |
|
545 } |
|
546 break; |
|
547 case START_DEFER: |
|
548 mConnection->StartDefer(); |
|
549 break; |
|
550 } |
|
551 return NS_OK; |
|
552 } |
|
553 |
|
554 private: |
|
555 ~DataChannelOnMessageAvailable() {} |
|
556 |
|
557 int32_t mType; |
|
558 // XXX should use union |
|
559 nsRefPtr<DataChannel> mChannel; |
|
560 nsRefPtr<DataChannelConnection> mConnection; |
|
561 nsCString mData; |
|
562 int32_t mLen; |
|
563 bool mResult; |
|
564 }; |
|
565 |
|
566 } |
|
567 |
|
568 #endif // NETWERK_SCTP_DATACHANNEL_DATACHANNEL_H_ |