/src/mozilla-central/netwerk/sctp/datachannel/DataChannel.h
Line | Count | Source (jump to first uncovered line) |
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 "mozilla/Mutex.h" |
25 | | #include "DataChannelProtocol.h" |
26 | | #include "DataChannelListener.h" |
27 | | #include "mozilla/net/NeckoTargetHolder.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 | | #endif |
34 | | |
35 | | #ifndef DATACHANNEL_LOG |
36 | | #define DATACHANNEL_LOG(args) |
37 | | #endif |
38 | | |
39 | | #ifndef EALREADY |
40 | | #define EALREADY WSAEALREADY |
41 | | #endif |
42 | | |
43 | | extern "C" { |
44 | | struct socket; |
45 | | struct sctp_rcvinfo; |
46 | | } |
47 | | |
48 | | namespace mozilla { |
49 | | |
50 | | class DataChannelConnection; |
51 | | class DataChannel; |
52 | | class DataChannelOnMessageAvailable; |
53 | | |
54 | | // For sending outgoing messages. |
55 | | // This class only holds a reference to the data and the info structure but does |
56 | | // not copy it. |
57 | | class OutgoingMsg |
58 | | { |
59 | | public: |
60 | | OutgoingMsg(struct sctp_sendv_spa &info, const uint8_t *data, |
61 | | size_t length); |
62 | | ~OutgoingMsg() = default;; |
63 | | void Advance(size_t offset); |
64 | 0 | struct sctp_sendv_spa &GetInfo() { return *mInfo; }; |
65 | 0 | size_t GetLength() { return mLength; }; |
66 | 0 | size_t GetLeft() { return mLength - mPos; }; |
67 | 0 | const uint8_t *GetData() { return (const uint8_t *)(mData + mPos); }; |
68 | | |
69 | | protected: |
70 | | OutgoingMsg() // Use this for inheritance only |
71 | | : mLength(0) |
72 | | , mData(nullptr) |
73 | | , mInfo(nullptr) |
74 | | , mPos(0) |
75 | 0 | {}; |
76 | | size_t mLength; |
77 | | const uint8_t *mData; |
78 | | struct sctp_sendv_spa *mInfo; |
79 | | size_t mPos; |
80 | | }; |
81 | | |
82 | | // For queuing outgoing messages |
83 | | // This class copies data of an outgoing message. |
84 | | class BufferedOutgoingMsg : public OutgoingMsg |
85 | | { |
86 | | public: |
87 | | explicit BufferedOutgoingMsg(OutgoingMsg &message); |
88 | | ~BufferedOutgoingMsg(); |
89 | | }; |
90 | | |
91 | | // for queuing incoming data messages before the Open or |
92 | | // external negotiation is indicated to us |
93 | | class QueuedDataMessage |
94 | | { |
95 | | public: |
96 | | QueuedDataMessage(uint16_t stream, uint32_t ppid, int flags, |
97 | | const void *data, uint32_t length) |
98 | | : mStream(stream) |
99 | | , mPpid(ppid) |
100 | | , mFlags(flags) |
101 | | , mLength(length) |
102 | 0 | { |
103 | 0 | mData = static_cast<uint8_t *>(moz_xmalloc((size_t)length)); // infallible |
104 | 0 | memcpy(mData, data, (size_t)length); |
105 | 0 | } |
106 | | |
107 | | ~QueuedDataMessage() |
108 | 0 | { |
109 | 0 | free(mData); |
110 | 0 | } |
111 | | |
112 | | uint16_t mStream; |
113 | | uint32_t mPpid; |
114 | | int mFlags; |
115 | | uint32_t mLength; |
116 | | uint8_t *mData; |
117 | | }; |
118 | | |
119 | | // One per PeerConnection |
120 | | class DataChannelConnection final |
121 | | : public net::NeckoTargetHolder |
122 | | #ifdef SCTP_DTLS_SUPPORTED |
123 | | , public sigslot::has_slots<> |
124 | | #endif |
125 | | { |
126 | | virtual ~DataChannelConnection(); |
127 | | |
128 | | public: |
129 | | enum { |
130 | | PENDING_NONE = 0U, // No outgoing messages are pending |
131 | | PENDING_DCEP = 1U, // Outgoing DCEP messages are pending |
132 | | PENDING_DATA = 2U, // Outgoing data channel messages are pending |
133 | | }; |
134 | | |
135 | | NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DataChannelConnection) |
136 | | |
137 | | class DataConnectionListener : public SupportsWeakPtr<DataConnectionListener> |
138 | | { |
139 | | public: |
140 | | MOZ_DECLARE_WEAKREFERENCE_TYPENAME(DataChannelConnection::DataConnectionListener) |
141 | | virtual ~DataConnectionListener() = default; |
142 | | |
143 | | // Called when a new DataChannel has been opened by the other side. |
144 | | virtual void NotifyDataChannel(already_AddRefed<DataChannel> channel) = 0; |
145 | | }; |
146 | | |
147 | | DataChannelConnection(DataConnectionListener *listener, |
148 | | nsIEventTarget *aTarget); |
149 | | |
150 | | bool Init(unsigned short aPort, uint16_t aNumStreams, bool aMaxMessageSizeSet, |
151 | | uint64_t aMaxMessageSize); |
152 | | |
153 | | void Destroy(); // So we can spawn refs tied to runnables in shutdown |
154 | | // Finish Destroy on STS to avoid SCTP race condition with ABORT from far end |
155 | | void DestroyOnSTS(struct socket *aMasterSocket, |
156 | | struct socket *aSocket); |
157 | | void DestroyOnSTSFinal(); |
158 | | |
159 | | void SetMaxMessageSize(bool aMaxMessageSizeSet, uint64_t aMaxMessageSize); |
160 | | uint64_t GetMaxMessageSize(); |
161 | | |
162 | | #ifdef ALLOW_DIRECT_SCTP_LISTEN_CONNECT |
163 | | // These block; they require something to decide on listener/connector |
164 | | // (though you can do simultaneous Connect()). Do not call these from |
165 | | // the main thread! |
166 | | bool Listen(unsigned short port); |
167 | | bool Connect(const char *addr, unsigned short port); |
168 | | #endif |
169 | | |
170 | | #ifdef SCTP_DTLS_SUPPORTED |
171 | | // Connect using a TransportFlow (DTLS) channel |
172 | | void SetEvenOdd(); |
173 | | bool ConnectViaTransportFlow(TransportFlow *aFlow, uint16_t localport, uint16_t remoteport); |
174 | | void CompleteConnect(TransportLayer *layer, TransportLayer::State state); |
175 | | void SetSignals(); |
176 | | #endif |
177 | | |
178 | | typedef enum { |
179 | | RELIABLE=0, |
180 | | PARTIAL_RELIABLE_REXMIT = 1, |
181 | | PARTIAL_RELIABLE_TIMED = 2 |
182 | | } Type; |
183 | | |
184 | | MOZ_MUST_USE |
185 | | already_AddRefed<DataChannel> Open(const nsACString& label, |
186 | | const nsACString& protocol, |
187 | | Type type, bool inOrder, |
188 | | uint32_t prValue, |
189 | | DataChannelListener *aListener, |
190 | | nsISupports *aContext, |
191 | | bool aExternalNegotiated, |
192 | | uint16_t aStream); |
193 | | |
194 | | void Stop(); |
195 | | void Close(DataChannel *aChannel); |
196 | | // CloseInt() must be called with mLock held |
197 | | void CloseInt(DataChannel *aChannel); |
198 | | void CloseAll(); |
199 | | |
200 | | // Returns a POSIX error code. |
201 | | int SendMsg(uint16_t stream, const nsACString &aMsg) |
202 | 0 | { |
203 | 0 | return SendDataMsgCommon(stream, aMsg, false); |
204 | 0 | } |
205 | | |
206 | | // Returns a POSIX error code. |
207 | | int SendBinaryMsg(uint16_t stream, const nsACString &aMsg) |
208 | 0 | { |
209 | 0 | return SendDataMsgCommon(stream, aMsg, true); |
210 | 0 | } |
211 | | |
212 | | // Returns a POSIX error code. |
213 | | int SendBlob(uint16_t stream, nsIInputStream *aBlob); |
214 | | |
215 | | // Called on data reception from the SCTP library |
216 | | // must(?) be public so my c->c++ trampoline can call it |
217 | | int ReceiveCallback(struct socket* sock, void *data, size_t datalen, |
218 | | struct sctp_rcvinfo rcv, int flags); |
219 | | |
220 | | // Find out state |
221 | | enum { |
222 | | CONNECTING = 0U, |
223 | | OPEN = 1U, |
224 | | CLOSING = 2U, |
225 | | CLOSED = 3U |
226 | | }; |
227 | 0 | uint16_t GetReadyState() { MutexAutoLock lock(mLock); return mState; } |
228 | | |
229 | | friend class DataChannel; |
230 | | Mutex mLock; |
231 | | |
232 | | void ReadBlob(already_AddRefed<DataChannelConnection> aThis, uint16_t aStream, nsIInputStream* aBlob); |
233 | | |
234 | | void GetStreamIds(std::vector<uint16_t>* aStreamList); |
235 | | |
236 | | bool SendDeferredMessages(); |
237 | | |
238 | | protected: |
239 | | friend class DataChannelOnMessageAvailable; |
240 | | // Avoid cycles with PeerConnectionImpl |
241 | | // Use from main thread only as WeakPtr is not threadsafe |
242 | | WeakPtr<DataConnectionListener> mListener; |
243 | | |
244 | | private: |
245 | | friend class DataChannelConnectRunnable; |
246 | | |
247 | | #ifdef SCTP_DTLS_SUPPORTED |
248 | | static void DTLSConnectThread(void *data); |
249 | | int SendPacket(nsAutoPtr<MediaPacket> packet); |
250 | | void SctpDtlsInput(TransportLayer *layer, MediaPacket& packet); |
251 | | static int SctpDtlsOutput(void *addr, void *buffer, size_t length, uint8_t tos, uint8_t set_df); |
252 | | #endif |
253 | | DataChannel* FindChannelByStream(uint16_t stream); |
254 | | uint16_t FindFreeStream(); |
255 | | bool RequestMoreStreams(int32_t aNeeded = 16); |
256 | | uint32_t UpdateCurrentStreamIndex(); |
257 | | uint32_t GetCurrentStreamIndex(); |
258 | | int SendControlMessage(const uint8_t *data, uint32_t len, uint16_t stream); |
259 | | int SendOpenAckMessage(uint16_t stream); |
260 | | int SendOpenRequestMessage(const nsACString& label, const nsACString& protocol, uint16_t stream, |
261 | | bool unordered, uint16_t prPolicy, uint32_t prValue); |
262 | | bool SendBufferedMessages(nsTArray<nsAutoPtr<BufferedOutgoingMsg>> &buffer); |
263 | | int SendMsgInternal(OutgoingMsg &msg); |
264 | | int SendMsgInternalOrBuffer(nsTArray<nsAutoPtr<BufferedOutgoingMsg>> &buffer, OutgoingMsg &msg, |
265 | | bool &buffered); |
266 | | int SendDataMsgInternalOrBuffer(DataChannel &channel, const uint8_t *data, size_t len, |
267 | | uint32_t ppid); |
268 | | int SendDataMsg(DataChannel &channel, const uint8_t *data, size_t len, uint32_t ppidPartial, |
269 | | uint32_t ppidFinal); |
270 | | int SendDataMsgCommon(uint16_t stream, const nsACString &aMsg, bool isBinary); |
271 | | |
272 | | void DeliverQueuedData(uint16_t stream); |
273 | | |
274 | | already_AddRefed<DataChannel> OpenFinish(already_AddRefed<DataChannel>&& aChannel); |
275 | | |
276 | | void ProcessQueuedOpens(); |
277 | | void ClearResets(); |
278 | | void SendOutgoingStreamReset(); |
279 | | void ResetOutgoingStream(uint16_t stream); |
280 | | void HandleOpenRequestMessage(const struct rtcweb_datachannel_open_request *req, |
281 | | uint32_t length, uint16_t stream); |
282 | | void HandleOpenAckMessage(const struct rtcweb_datachannel_ack *ack, |
283 | | uint32_t length, uint16_t stream); |
284 | | void HandleUnknownMessage(uint32_t ppid, uint32_t length, uint16_t stream); |
285 | | uint8_t BufferMessage(nsACString& recvBuffer, const void *data, uint32_t length, uint32_t ppid, |
286 | | int flags); |
287 | | void HandleDataMessage(const void *buffer, size_t length, uint32_t ppid, uint16_t stream, |
288 | | int flags); |
289 | | void HandleDCEPMessage(const void *buffer, size_t length, uint32_t ppid, uint16_t stream, |
290 | | int flags); |
291 | | void HandleMessage(const void *buffer, size_t length, uint32_t ppid, uint16_t stream, int flags); |
292 | | void HandleAssociationChangeEvent(const struct sctp_assoc_change *sac); |
293 | | void HandlePeerAddressChangeEvent(const struct sctp_paddr_change *spc); |
294 | | void HandleRemoteErrorEvent(const struct sctp_remote_error *sre); |
295 | | void HandleShutdownEvent(const struct sctp_shutdown_event *sse); |
296 | | void HandleAdaptationIndication(const struct sctp_adaptation_event *sai); |
297 | | void HandlePartialDeliveryEvent(const struct sctp_pdapi_event *spde); |
298 | | void HandleSendFailedEvent(const struct sctp_send_failed_event *ssfe); |
299 | | void HandleStreamResetEvent(const struct sctp_stream_reset_event *strrst); |
300 | | void HandleStreamChangeEvent(const struct sctp_stream_change_event *strchg); |
301 | | void HandleNotification(const union sctp_notification *notif, size_t n); |
302 | | |
303 | | #ifdef SCTP_DTLS_SUPPORTED |
304 | 0 | bool IsSTSThread() { |
305 | 0 | bool on = false; |
306 | 0 | if (mSTS) { |
307 | 0 | mSTS->IsOnCurrentThread(&on); |
308 | 0 | } |
309 | 0 | return on; |
310 | 0 | } |
311 | | #endif |
312 | | |
313 | | // Exists solely for proxying release of the TransportFlow to the STS thread |
314 | 0 | static void ReleaseTransportFlow(const RefPtr<TransportFlow>& aFlow) {} |
315 | | |
316 | | bool mSendInterleaved; |
317 | | bool mPpidFragmentation; |
318 | | bool mMaxMessageSizeSet; |
319 | | uint64_t mMaxMessageSize; |
320 | | |
321 | | // Data: |
322 | | // NOTE: while this array will auto-expand, increases in the number of |
323 | | // channels available from the stack must be negotiated! |
324 | | bool mAllocateEven; |
325 | | AutoTArray<RefPtr<DataChannel>,16> mStreams; |
326 | | uint32_t mCurrentStream; |
327 | | nsDeque mPending; // Holds addref'ed DataChannel's -- careful! |
328 | | // holds data that's come in before a channel is open |
329 | | nsTArray<nsAutoPtr<QueuedDataMessage>> mQueuedData; |
330 | | // holds outgoing control messages |
331 | | nsTArray<nsAutoPtr<BufferedOutgoingMsg>> mBufferedControl; // GUARDED_BY(mConnection->mLock) |
332 | | |
333 | | // Streams pending reset |
334 | | AutoTArray<uint16_t,4> mStreamsResetting; |
335 | | |
336 | | struct socket *mMasterSocket; // accessed from STS thread |
337 | | struct socket *mSocket; // cloned from mMasterSocket on successful Connect on STS thread |
338 | | uint16_t mState; // Protected with mLock |
339 | | |
340 | | #ifdef SCTP_DTLS_SUPPORTED |
341 | | RefPtr<TransportFlow> mTransportFlow; |
342 | | TransportLayerDtls* mDtls; |
343 | | nsCOMPtr<nsIEventTarget> mSTS; |
344 | | #endif |
345 | | uint16_t mLocalPort; // Accessed from connect thread |
346 | | uint16_t mRemotePort; |
347 | | |
348 | | nsCOMPtr<nsIThread> mInternalIOThread; |
349 | | uint8_t mPendingType; |
350 | | nsCString mRecvBuffer; |
351 | | |
352 | | #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED |
353 | | bool mShutdown; |
354 | | #endif |
355 | | }; |
356 | | |
357 | | #define ENSURE_DATACONNECTION \ |
358 | 0 | do { MOZ_ASSERT(mConnection); if (!mConnection) { return; } } while (0) |
359 | | |
360 | | class DataChannel { |
361 | | public: |
362 | | enum { |
363 | | CONNECTING = 0U, |
364 | | OPEN = 1U, |
365 | | CLOSING = 2U, |
366 | | CLOSED = 3U, |
367 | | WAITING_TO_OPEN = 4U |
368 | | }; |
369 | | |
370 | | DataChannel(DataChannelConnection *connection, |
371 | | uint16_t stream, |
372 | | uint16_t state, |
373 | | const nsACString& label, |
374 | | const nsACString& protocol, |
375 | | uint16_t policy, uint32_t value, |
376 | | uint32_t flags, |
377 | | DataChannelListener *aListener, |
378 | | nsISupports *aContext) |
379 | | : mListenerLock("netwerk::sctp::DataChannel") |
380 | | , mListener(aListener) |
381 | | , mContext(aContext) |
382 | | , mConnection(connection) |
383 | | , mLabel(label) |
384 | | , mProtocol(protocol) |
385 | | , mState(state) |
386 | | , mStream(stream) |
387 | | , mPrPolicy(policy) |
388 | | , mPrValue(value) |
389 | | , mFlags(flags) |
390 | | , mId(0) |
391 | | , mIsRecvBinary(false) |
392 | | , mBufferedThreshold(0) // default from spec |
393 | | , mMainThreadEventTarget(connection->GetNeckoTarget()) |
394 | 0 | { |
395 | 0 | NS_ASSERTION(mConnection,"NULL connection"); |
396 | 0 | } |
397 | | |
398 | | private: |
399 | | ~DataChannel(); |
400 | | |
401 | | public: |
402 | | NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DataChannel) |
403 | | |
404 | | // when we disconnect from the connection after stream RESET |
405 | | void StreamClosedLocked(); |
406 | | |
407 | | // Complete dropping of the link between DataChannel and the connection. |
408 | | // After this, except for a few methods below listed to be safe, you can't |
409 | | // call into DataChannel. |
410 | | void ReleaseConnection(); |
411 | | |
412 | | // Close this DataChannel. Can be called multiple times. MUST be called |
413 | | // before destroying the DataChannel (state must be CLOSED or CLOSING). |
414 | | void Close(); |
415 | | |
416 | | // Set the listener (especially for channels created from the other side) |
417 | | void SetListener(DataChannelListener *aListener, nsISupports *aContext); |
418 | | |
419 | | // Helper for send methods that converts POSIX error codes to an ErrorResult. |
420 | | static void SendErrnoToErrorResult(int error, ErrorResult& aRv); |
421 | | |
422 | | // Send a string |
423 | | void SendMsg(const nsACString &aMsg, ErrorResult& aRv); |
424 | | |
425 | | // Send a binary message (TypedArray) |
426 | | void SendBinaryMsg(const nsACString &aMsg, ErrorResult& aRv); |
427 | | |
428 | | // Send a binary blob |
429 | | void SendBinaryStream(nsIInputStream *aBlob, ErrorResult& aRv); |
430 | | |
431 | 0 | uint16_t GetType() { return mPrPolicy; } |
432 | | |
433 | | dom::Nullable<uint16_t> GetMaxPacketLifeTime() const; |
434 | | |
435 | | dom::Nullable<uint16_t> GetMaxRetransmits() const; |
436 | | |
437 | 0 | bool GetOrdered() { return !(mFlags & DATA_CHANNEL_FLAGS_OUT_OF_ORDER_ALLOWED); } |
438 | | |
439 | | // Amount of data buffered to send |
440 | | uint32_t GetBufferedAmount() |
441 | 0 | { |
442 | 0 | if (!mConnection) { |
443 | 0 | return 0; |
444 | 0 | } |
445 | 0 | |
446 | 0 | MutexAutoLock lock(mConnection->mLock); |
447 | 0 | size_t buffered = GetBufferedAmountLocked(); |
448 | 0 |
|
449 | 0 | #if (SIZE_MAX > UINT32_MAX) |
450 | 0 | if (buffered > UINT32_MAX) { // paranoia - >4GB buffered is very very unlikely |
451 | 0 | buffered = UINT32_MAX; |
452 | 0 | } |
453 | 0 | #endif |
454 | 0 |
|
455 | 0 | return buffered; |
456 | 0 | } |
457 | | |
458 | | |
459 | | // Trigger amount for generating BufferedAmountLow events |
460 | | uint32_t GetBufferedAmountLowThreshold(); |
461 | | void SetBufferedAmountLowThreshold(uint32_t aThreshold); |
462 | | |
463 | | // Find out state |
464 | | uint16_t GetReadyState() |
465 | 0 | { |
466 | 0 | if (mConnection) { |
467 | 0 | MutexAutoLock lock(mConnection->mLock); |
468 | 0 | if (mState == WAITING_TO_OPEN) |
469 | 0 | return CONNECTING; |
470 | 0 | return mState; |
471 | 0 | } |
472 | 0 | return CLOSED; |
473 | 0 | } |
474 | | |
475 | 0 | void GetLabel(nsAString& aLabel) { CopyUTF8toUTF16(mLabel, aLabel); } |
476 | 0 | void GetProtocol(nsAString& aProtocol) { CopyUTF8toUTF16(mProtocol, aProtocol); } |
477 | 0 | uint16_t GetStream() { return mStream; } |
478 | | |
479 | | void AppReady(); |
480 | | |
481 | | void SendOrQueue(DataChannelOnMessageAvailable *aMessage); |
482 | | |
483 | | protected: |
484 | | Mutex mListenerLock; // protects mListener and mContext |
485 | | DataChannelListener *mListener; |
486 | | nsCOMPtr<nsISupports> mContext; |
487 | | |
488 | | private: |
489 | | friend class DataChannelOnMessageAvailable; |
490 | | friend class DataChannelConnection; |
491 | | |
492 | | nsresult AddDataToBinaryMsg(const char *data, uint32_t size); |
493 | | size_t GetBufferedAmountLocked() const; |
494 | | bool EnsureValidStream(ErrorResult& aRv); |
495 | | |
496 | | RefPtr<DataChannelConnection> mConnection; |
497 | | nsCString mLabel; |
498 | | nsCString mProtocol; |
499 | | uint16_t mState; |
500 | | uint16_t mStream; |
501 | | uint16_t mPrPolicy; |
502 | | uint32_t mPrValue; |
503 | | uint32_t mFlags; |
504 | | uint32_t mId; |
505 | | bool mIsRecvBinary; |
506 | | size_t mBufferedThreshold; |
507 | | nsCString mRecvBuffer; |
508 | | nsTArray<nsAutoPtr<BufferedOutgoingMsg>> mBufferedData; // GUARDED_BY(mConnection->mLock) |
509 | | nsTArray<nsCOMPtr<nsIRunnable>> mQueuedMessages; |
510 | | nsCOMPtr<nsIEventTarget> mMainThreadEventTarget; |
511 | | }; |
512 | | |
513 | | // used to dispatch notifications of incoming data to the main thread |
514 | | // Patterned on CallOnMessageAvailable in WebSockets |
515 | | // Also used to proxy other items to MainThread |
516 | | class DataChannelOnMessageAvailable : public Runnable |
517 | | { |
518 | | public: |
519 | | enum { |
520 | | ON_CONNECTION, |
521 | | ON_DISCONNECTED, |
522 | | ON_CHANNEL_CREATED, |
523 | | ON_CHANNEL_OPEN, |
524 | | ON_CHANNEL_CLOSED, |
525 | | ON_DATA_STRING, |
526 | | ON_DATA_BINARY, |
527 | | BUFFER_LOW_THRESHOLD, |
528 | | NO_LONGER_BUFFERED, |
529 | | }; /* types */ |
530 | | |
531 | | DataChannelOnMessageAvailable( |
532 | | int32_t aType, |
533 | | DataChannelConnection* aConnection, |
534 | | DataChannel* aChannel, |
535 | | nsCString& aData) // XXX this causes inefficiency |
536 | | : Runnable("DataChannelOnMessageAvailable") |
537 | | , mType(aType) |
538 | | , mChannel(aChannel) |
539 | | , mConnection(aConnection) |
540 | | , mData(aData) |
541 | 0 | { |
542 | 0 | } |
543 | | |
544 | | DataChannelOnMessageAvailable(int32_t aType, DataChannel* aChannel) |
545 | | : Runnable("DataChannelOnMessageAvailable") |
546 | | , mType(aType) |
547 | | , mChannel(aChannel) |
548 | 0 | { |
549 | 0 | } |
550 | | // XXX is it safe to leave mData uninitialized? This should only be |
551 | | // used for notifications that don't use them, but I'd like more |
552 | | // bulletproof compile-time checking. |
553 | | |
554 | | DataChannelOnMessageAvailable(int32_t aType, |
555 | | DataChannelConnection* aConnection, |
556 | | DataChannel* aChannel) |
557 | | : Runnable("DataChannelOnMessageAvailable") |
558 | | , mType(aType) |
559 | | , mChannel(aChannel) |
560 | | , mConnection(aConnection) |
561 | 0 | { |
562 | 0 | } |
563 | | |
564 | | // for ON_CONNECTION/ON_DISCONNECTED |
565 | | DataChannelOnMessageAvailable(int32_t aType, |
566 | | DataChannelConnection* aConnection) |
567 | | : Runnable("DataChannelOnMessageAvailable") |
568 | | , mType(aType) |
569 | | , mConnection(aConnection) |
570 | 0 | { |
571 | 0 | } |
572 | | |
573 | | NS_IMETHOD Run() override |
574 | 0 | { |
575 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
576 | 0 |
|
577 | 0 | // Note: calling the listeners can indirectly cause the listeners to be |
578 | 0 | // made available for GC (by removing event listeners), especially for |
579 | 0 | // OnChannelClosed(). We hold a ref to the Channel and the listener |
580 | 0 | // while calling this. |
581 | 0 | switch (mType) { |
582 | 0 | case ON_DATA_STRING: |
583 | 0 | case ON_DATA_BINARY: |
584 | 0 | case ON_CHANNEL_OPEN: |
585 | 0 | case ON_CHANNEL_CLOSED: |
586 | 0 | case BUFFER_LOW_THRESHOLD: |
587 | 0 | case NO_LONGER_BUFFERED: |
588 | 0 | { |
589 | 0 | MutexAutoLock lock(mChannel->mListenerLock); |
590 | 0 | if (!mChannel->mListener) { |
591 | 0 | DATACHANNEL_LOG(("DataChannelOnMessageAvailable (%d) with null Listener!",mType)); |
592 | 0 | return NS_OK; |
593 | 0 | } |
594 | 0 |
|
595 | 0 | switch (mType) { |
596 | 0 | case ON_DATA_STRING: |
597 | 0 | mChannel->mListener->OnMessageAvailable(mChannel->mContext, mData); |
598 | 0 | break; |
599 | 0 | case ON_DATA_BINARY: |
600 | 0 | mChannel->mListener->OnBinaryMessageAvailable(mChannel->mContext, mData); |
601 | 0 | break; |
602 | 0 | case ON_CHANNEL_OPEN: |
603 | 0 | mChannel->mListener->OnChannelConnected(mChannel->mContext); |
604 | 0 | break; |
605 | 0 | case ON_CHANNEL_CLOSED: |
606 | 0 | mChannel->mListener->OnChannelClosed(mChannel->mContext); |
607 | 0 | break; |
608 | 0 | case BUFFER_LOW_THRESHOLD: |
609 | 0 | mChannel->mListener->OnBufferLow(mChannel->mContext); |
610 | 0 | break; |
611 | 0 | case NO_LONGER_BUFFERED: |
612 | 0 | mChannel->mListener->NotBuffered(mChannel->mContext); |
613 | 0 | break; |
614 | 0 | } |
615 | 0 | break; |
616 | 0 | } |
617 | 0 | case ON_DISCONNECTED: |
618 | 0 | // If we've disconnected, make sure we close all the streams - from mainthread! |
619 | 0 | mConnection->CloseAll(); |
620 | 0 | MOZ_FALLTHROUGH; |
621 | 0 | case ON_CHANNEL_CREATED: |
622 | 0 | case ON_CONNECTION: |
623 | 0 | // WeakPtr - only used/modified/nulled from MainThread so we can use a WeakPtr here |
624 | 0 | if (!mConnection->mListener) { |
625 | 0 | DATACHANNEL_LOG(("DataChannelOnMessageAvailable (%d) with null Listener",mType)); |
626 | 0 | return NS_OK; |
627 | 0 | } |
628 | 0 | switch (mType) { |
629 | 0 | case ON_CHANNEL_CREATED: |
630 | 0 | // important to give it an already_AddRefed pointer! |
631 | 0 | mConnection->mListener->NotifyDataChannel(mChannel.forget()); |
632 | 0 | break; |
633 | 0 | default: |
634 | 0 | break; |
635 | 0 | } |
636 | 0 | break; |
637 | 0 | } |
638 | 0 | return NS_OK; |
639 | 0 | } |
640 | | |
641 | | private: |
642 | 0 | ~DataChannelOnMessageAvailable() = default; |
643 | | |
644 | | int32_t mType; |
645 | | // XXX should use union |
646 | | RefPtr<DataChannel> mChannel; |
647 | | RefPtr<DataChannelConnection> mConnection; |
648 | | nsCString mData; |
649 | | }; |
650 | | |
651 | | } |
652 | | |
653 | | #endif // NETWERK_SCTP_DATACHANNEL_DATACHANNEL_H_ |