mediasoup 3.20.2 → 3.20.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/npm-scripts.mjs +26 -0
  2. package/package.json +4 -3
  3. package/worker/include/RTC/SCTP/association/Association.hpp +16 -2
  4. package/worker/include/RTC/SCTP/association/HeartbeatHandler.hpp +3 -3
  5. package/worker/include/RTC/SCTP/association/StreamResetHandler.hpp +3 -3
  6. package/worker/include/RTC/SCTP/association/TransmissionControlBlock.hpp +19 -4
  7. package/worker/include/RTC/SCTP/association/TransmissionControlBlockContextInterface.hpp +19 -2
  8. package/worker/include/RTC/SCTP/public/SctpTypes.hpp +2 -0
  9. package/worker/include/RTC/SCTP/rx/ReassemblyQueue.hpp +7 -0
  10. package/worker/include/RTC/SCTP/tx/RetransmissionErrorCounter.hpp +3 -4
  11. package/worker/meson.build +3 -0
  12. package/worker/mocks/include/RTC/SCTP/association/MockAssociationListener.hpp +68 -7
  13. package/worker/mocks/include/handles/MockBackoffTimerHandle.hpp +10 -4
  14. package/worker/mocks/src/handles/MockBackoffTimerHandle.cpp +7 -0
  15. package/worker/src/RTC/SCTP/association/Association.cpp +52 -12
  16. package/worker/src/RTC/SCTP/association/HeartbeatHandler.cpp +48 -18
  17. package/worker/src/RTC/SCTP/association/StreamResetHandler.cpp +26 -17
  18. package/worker/src/RTC/SCTP/association/TransmissionControlBlock.cpp +48 -15
  19. package/worker/src/RTC/SCTP/rx/ReassemblyQueue.cpp +17 -8
  20. package/worker/src/RTC/Transport.cpp +1 -1
  21. package/worker/test/src/RTC/SCTP/association/TestAssociation.cpp +1755 -0
  22. package/worker/test/src/RTC/SCTP/association/TestHeartbeatHandler.cpp +4 -1
  23. package/worker/test/src/RTC/SCTP/association/TestPacketSender.cpp +121 -0
  24. package/worker/test/src/RTC/SCTP/association/TestStreamResetHandler.cpp +369 -0
  25. package/worker/test/src/RTC/SCTP/association/TestTransmissionControlBlock.cpp +16 -2
  26. package/worker/test/src/RTC/SCTP/rx/TestReassemblyQueue.cpp +24 -0
package/npm-scripts.mjs CHANGED
@@ -232,6 +232,12 @@ async function run() {
232
232
  break;
233
233
  }
234
234
 
235
+ case 'publish:dry-run': {
236
+ publishDryRun();
237
+
238
+ break;
239
+ }
240
+
235
241
  case 'release:check': {
236
242
  await checkRelease();
237
243
 
@@ -511,6 +517,23 @@ function installNodeDeps() {
511
517
  executeCmd('npm audit --prefix worker/scripts');
512
518
  }
513
519
 
520
+ function publishDryRun() {
521
+ logInfo('publishDryRun()');
522
+
523
+ // NOTE: We use `npm pack --dry-run` rather than `npm publish --dry-run`
524
+ // because the latter contacts the registry and fails with "You cannot
525
+ // publish over the previously published versions" whenever the version in
526
+ // package.json is already published (which is the usual state between
527
+ // releases), making it useless in CI.
528
+ //
529
+ // `npm pack --dry-run` still runs the `prepare` script (flatbuffers
530
+ // generation and TypeScript build) and assembles the tarball exactly as a
531
+ // real publish would, reporting its contents without writing any file or
532
+ // contacting the registry. Useful to validate the `files` list in
533
+ // package.json and that the package builds before tagging a release.
534
+ executeCmd('npm pack --dry-run');
535
+ }
536
+
514
537
  async function checkRelease() {
515
538
  logInfo('checkRelease()');
516
539
 
@@ -522,6 +545,9 @@ async function checkRelease() {
522
545
  lintWorker();
523
546
  testNode();
524
547
  testWorker();
548
+ // Validate packaging (the `files` list in package.json) before the
549
+ // irreversible release steps (git push, GitHub release, npm publish).
550
+ publishDryRun();
525
551
  }
526
552
 
527
553
  async function release() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mediasoup",
3
- "version": "3.20.2",
3
+ "version": "3.20.4",
4
4
  "description": "Cutting Edge WebRTC Video Conferencing",
5
5
  "contributors": [
6
6
  "Iñaki Baz Castillo <ibc@aliax.net> (https://inakibaz.me)",
@@ -96,6 +96,7 @@
96
96
  "test:worker": "node npm-scripts.mjs test:worker",
97
97
  "coverage": "node npm-scripts.mjs coverage:node",
98
98
  "coverage:node": "node npm-scripts.mjs coverage:node",
99
+ "publish:dry-run": "node npm-scripts.mjs publish:dry-run",
99
100
  "release:check": "node npm-scripts.mjs release:check",
100
101
  "release": "node npm-scripts.mjs release"
101
102
  },
@@ -121,8 +122,8 @@
121
122
  "globals": "^17.6.0",
122
123
  "ini": "^6.0.0",
123
124
  "jest": "^30.4.2",
124
- "knip": "^6.15.0",
125
- "marked": "^18.0.4",
125
+ "knip": "^6.16.1",
126
+ "marked": "^18.0.5",
126
127
  "open-cli": "^9.0.0",
127
128
  "pick-port": "^2.2.0",
128
129
  "prettier": "^3.8.3",
@@ -53,7 +53,8 @@ namespace RTC
53
53
  */
54
54
  class Association : public AssociationInterface,
55
55
  public PacketSender::Listener,
56
- public BackoffTimerHandleInterface::Listener
56
+ public BackoffTimerHandleInterface::Listener,
57
+ public TransmissionControlBlockContextInterface::Listener
57
58
  {
58
59
  public:
59
60
  /**
@@ -172,7 +173,13 @@ namespace RTC
172
173
  const SctpOptions& sctpOptions,
173
174
  AssociationListenerInterface* listener,
174
175
  SharedInterface* shared,
175
- bool isDataChannel);
176
+ bool isDataChannel,
177
+ // Whether `MayConnect()` should be called when SCTP data is received.
178
+ // This is always true in production (the association auto-initiates the
179
+ // connection as soon as the transport is ready or data arrives). It's
180
+ // only set to false in tests that need a purely passive peer to mimic
181
+ // dcsctp's asymmetric handshake.
182
+ bool mayConnectOnReceivedSctpData);
176
183
 
177
184
  ~Association() override;
178
185
 
@@ -494,6 +501,10 @@ namespace RTC
494
501
  void OnBackoffTimer(
495
502
  BackoffTimerHandleInterface* backoffTimer, uint64_t& baseTimeoutMs, bool& stop) override;
496
503
 
504
+ /* Pure virtual methods inherited from TransmissionControlBlockContextInterface::Listener. */
505
+ public:
506
+ void OnTransmissionControlBlockTooManyTxErrors() override;
507
+
497
508
  private:
498
509
  // SCTP options given in the constructor.
499
510
  SctpOptions sctpOptions;
@@ -526,6 +537,9 @@ namespace RTC
526
537
  const size_t maxPacketLength;
527
538
  // Whether this is DataChannel based SCTP.
528
539
  bool isDataChannel;
540
+ // Whether `MayConnect()` should be called when SCTP data is received.
541
+ // See the constructor for details.
542
+ bool mayConnectOnReceivedSctpData;
529
543
  };
530
544
  } // namespace SCTP
531
545
  } // namespace RTC
@@ -3,10 +3,10 @@
3
3
 
4
4
  #include "common.hpp"
5
5
  #include "handles/BackoffTimerHandleInterface.hpp"
6
+ #include "RTC/SCTP/association/AssociationListenerDeferrer.hpp"
6
7
  #include "RTC/SCTP/association/TransmissionControlBlockContextInterface.hpp"
7
8
  #include "RTC/SCTP/packet/chunks/HeartbeatAckChunk.hpp"
8
9
  #include "RTC/SCTP/packet/chunks/HeartbeatRequestChunk.hpp"
9
- #include "RTC/SCTP/public/AssociationListenerInterface.hpp"
10
10
  #include "RTC/SCTP/public/SctpOptions.hpp"
11
11
  #include "SharedInterface.hpp"
12
12
 
@@ -26,7 +26,7 @@ namespace RTC
26
26
  {
27
27
  public:
28
28
  HeartbeatHandler(
29
- AssociationListenerInterface& associationListener,
29
+ AssociationListenerDeferrer& associationListenerDeferrer,
30
30
  const SctpOptions& sctpOptions,
31
31
  SharedInterface* shared,
32
32
  TransmissionControlBlockContextInterface* tcbContext);
@@ -63,7 +63,7 @@ namespace RTC
63
63
  BackoffTimerHandleInterface* backoffTimer, uint64_t& baseTimeoutMs, bool& stop) override;
64
64
 
65
65
  private:
66
- AssociationListenerInterface& associationListener;
66
+ AssociationListenerDeferrer& associationListenerDeferrer;
67
67
  const SctpOptions sctpOptions;
68
68
  SharedInterface* shared;
69
69
  TransmissionControlBlockContextInterface* tcbContext;
@@ -4,13 +4,13 @@
4
4
  #include "common.hpp"
5
5
  #include "Utils/UnwrappedSequenceNumber.hpp"
6
6
  #include "handles/BackoffTimerHandleInterface.hpp"
7
+ #include "RTC/SCTP/association/AssociationListenerDeferrer.hpp"
7
8
  #include "RTC/SCTP/association/TransmissionControlBlockContextInterface.hpp"
8
9
  #include "RTC/SCTP/packet/Packet.hpp"
9
10
  #include "RTC/SCTP/packet/chunks/ReConfigChunk.hpp"
10
11
  #include "RTC/SCTP/packet/parameters/IncomingSsnResetRequestParameter.hpp"
11
12
  #include "RTC/SCTP/packet/parameters/OutgoingSsnResetRequestParameter.hpp"
12
13
  #include "RTC/SCTP/packet/parameters/ReconfigurationResponseParameter.hpp"
13
- #include "RTC/SCTP/public/AssociationListenerInterface.hpp"
14
14
  #include "RTC/SCTP/rx/DataTracker.hpp"
15
15
  #include "RTC/SCTP/rx/ReassemblyQueue.hpp"
16
16
  #include "RTC/SCTP/tx/RetransmissionQueue.hpp"
@@ -168,7 +168,7 @@ namespace RTC
168
168
 
169
169
  public:
170
170
  StreamResetHandler(
171
- AssociationListenerInterface& associationListener,
171
+ AssociationListenerDeferrer& associationListenerDeferrer,
172
172
  SharedInterface* shared,
173
173
  TransmissionControlBlockContextInterface* tcbContext,
174
174
  DataTracker* dataTracker,
@@ -263,7 +263,7 @@ namespace RTC
263
263
  BackoffTimerHandleInterface* backoffTimer, uint64_t& baseTimeoutMs, bool& stop) override;
264
264
 
265
265
  private:
266
- AssociationListenerInterface& associationListener;
266
+ AssociationListenerDeferrer& associationListenerDeferrer;
267
267
  SharedInterface* shared;
268
268
  TransmissionControlBlockContextInterface* tcbContext;
269
269
  DataTracker* dataTracker;
@@ -3,13 +3,13 @@
3
3
 
4
4
  #include "common.hpp"
5
5
  #include "handles/BackoffTimerHandleInterface.hpp"
6
+ #include "RTC/SCTP/association/AssociationListenerDeferrer.hpp"
6
7
  #include "RTC/SCTP/association/HeartbeatHandler.hpp"
7
8
  #include "RTC/SCTP/association/NegotiatedCapabilities.hpp"
8
9
  #include "RTC/SCTP/association/PacketSender.hpp"
9
10
  #include "RTC/SCTP/association/StreamResetHandler.hpp"
10
11
  #include "RTC/SCTP/association/TransmissionControlBlockContextInterface.hpp"
11
12
  #include "RTC/SCTP/packet/Packet.hpp"
12
- #include "RTC/SCTP/public/AssociationListenerInterface.hpp"
13
13
  #include "RTC/SCTP/public/SctpOptions.hpp"
14
14
  #include "RTC/SCTP/rx/DataTracker.hpp"
15
15
  #include "RTC/SCTP/rx/ReassemblyQueue.hpp"
@@ -37,7 +37,8 @@ namespace RTC
37
37
  {
38
38
  public:
39
39
  TransmissionControlBlock(
40
- AssociationListenerInterface& associationListener,
40
+ TransmissionControlBlockContextInterface::Listener* listener,
41
+ AssociationListenerDeferrer& associationListenerDeferrer,
41
42
  const SctpOptions& sctpOptions,
42
43
  SharedInterface* shared,
43
44
  SendQueueInterface& sendQueue,
@@ -250,7 +251,20 @@ namespace RTC
250
251
  */
251
252
  bool IncrementTxErrorCounter(std::string_view reason) override
252
253
  {
253
- return this->txErrorCounter.Increment(reason);
254
+ const bool withinLimit = this->txErrorCounter.Increment(reason);
255
+
256
+ if (!withinLimit)
257
+ {
258
+ // NOTE: This closes (and destroys) this TCB synchronously. It's safe to
259
+ // do so from within a timer handler because the handler sets the
260
+ // BackoffTimerHandle `stop` flag and doesn't touch any member
261
+ // afterwards, so the (now destroyed) firing timer won't be accessed.
262
+ this->listener->OnTransmissionControlBlockTooManyTxErrors();
263
+ }
264
+
265
+ // NOTE: `withinLimit` is a local, so this is safe even if `this` was
266
+ // destroyed above.
267
+ return withinLimit;
254
268
  }
255
269
 
256
270
  /**
@@ -288,7 +302,8 @@ namespace RTC
288
302
  BackoffTimerHandleInterface* backoffTimer, uint64_t& baseTimeoutMs, bool& stop) override;
289
303
 
290
304
  private:
291
- AssociationListenerInterface& associationListener;
305
+ TransmissionControlBlockContextInterface::Listener* listener;
306
+ AssociationListenerDeferrer& associationListenerDeferrer;
292
307
  const SctpOptions sctpOptions;
293
308
  SharedInterface* shared;
294
309
  PacketSender& packetSender;
@@ -11,6 +11,23 @@ namespace RTC
11
11
  {
12
12
  class TransmissionControlBlockContextInterface
13
13
  {
14
+ public:
15
+ class Listener
16
+ {
17
+ public:
18
+ virtual ~Listener() = default;
19
+
20
+ public:
21
+ /**
22
+ * Called when the transmission error counter has exceeded its limit and
23
+ * the association must therefore be closed.
24
+ *
25
+ * @remarks
26
+ * - It mirrors dcsctp's `CloseConnectionBecauseOfTooManyTransmissionErrors()`.
27
+ */
28
+ virtual void OnTransmissionControlBlockTooManyTxErrors() = 0;
29
+ };
30
+
14
31
  public:
15
32
  virtual ~TransmissionControlBlockContextInterface() = default;
16
33
 
@@ -44,8 +61,8 @@ namespace RTC
44
61
 
45
62
  /**
46
63
  * Increments the transmission error counter, given a human readable
47
- * reason. Returns `true` if the maximum error count has been reached,
48
- * `false` will be returned.
64
+ * reason. Returns `false` if the maximum error count has been reached,
65
+ * `true` otherwise.
49
66
  */
50
67
  virtual bool IncrementTxErrorCounter(std::string_view reason) = 0;
51
68
 
@@ -331,6 +331,8 @@ namespace RTC
331
331
  {
332
332
  return "ERROR_SHUTTING_DOWN";
333
333
  }
334
+
335
+ NO_DEFAULT_GCC();
334
336
  }
335
337
  }
336
338
 
@@ -178,6 +178,13 @@ namespace RTC
178
178
  ReassemblyStreamsInterface::OnAssembledMessage onAssembledMessage,
179
179
  bool useMessageInterleaving);
180
180
 
181
+ /**
182
+ * Treat Forward-TSN message as payload. size is calculated based on wire
183
+ * size rather than used memory size: 32bit for TSN + 16+16 bits for each
184
+ * skipped stream entry.
185
+ */
186
+ size_t ForwardTsnCost(size_t numStreams);
187
+
181
188
  void AddReassembledMessage(std::span<const Types::UnwrappedTsn> tsns, Message message);
182
189
 
183
190
  void AssertIsConsistent() const;
@@ -18,7 +18,7 @@ namespace RTC
18
18
  class RetransmissionErrorCounter
19
19
  {
20
20
  public:
21
- RetransmissionErrorCounter(const SctpOptions& sctpOptions);
21
+ explicit RetransmissionErrorCounter(const SctpOptions& sctpOptions);
22
22
 
23
23
  ~RetransmissionErrorCounter();
24
24
 
@@ -26,14 +26,13 @@ namespace RTC
26
26
  void Dump(int indentation = 0) const;
27
27
 
28
28
  /**
29
- * Increments the retransmission timer. Returns `true` if the maximum
30
- * error count has been reached, `false` will be returned.
29
+ * Increments the retransmission timer. Returns `false` if the maximum
30
+ * error count has been reached, `true` otherwise.
31
31
  */
32
32
  bool Increment(std::string_view reason);
33
33
 
34
34
  /**
35
35
  * Whether maximum error count has been reached.
36
- * @return [description]
37
36
  */
38
37
  bool IsExhausted() const
39
38
  {
@@ -494,9 +494,12 @@ test_sources = [
494
494
  'test/src/RTC/RTCP/TestPacket.cpp',
495
495
  'test/src/RTC/RTCP/TestXr.cpp',
496
496
  'test/src/RTC/SCTP/sctpCommon.cpp',
497
+ 'test/src/RTC/SCTP/association/TestAssociation.cpp',
497
498
  'test/src/RTC/SCTP/association/TestHeartbeatHandler.cpp',
498
499
  'test/src/RTC/SCTP/association/TestNegotiatedCapabilities.cpp',
500
+ 'test/src/RTC/SCTP/association/TestPacketSender.cpp',
499
501
  'test/src/RTC/SCTP/association/TestStateCookie.cpp',
502
+ 'test/src/RTC/SCTP/association/TestStreamResetHandler.cpp',
500
503
  'test/src/RTC/SCTP/association/TestTransmissionControlBlock.cpp',
501
504
  'test/src/RTC/SCTP/rx/TestDataTracker.cpp',
502
505
  'test/src/RTC/SCTP/rx/TestInterleavedReassemblyStreams.cpp',
@@ -21,7 +21,7 @@ namespace mocks
21
21
  {
22
22
  this->sentPackets.emplace_back(data, data + len);
23
23
 
24
- return true;
24
+ return this->sendDataResult;
25
25
  }
26
26
 
27
27
  void OnAssociationConnecting() override
@@ -75,20 +75,35 @@ namespace mocks
75
75
  this->receivedMessages.emplace_back(std::move(message));
76
76
  }
77
77
 
78
- void OnAssociationStreamsResetPerformed(std::span<const uint16_t> /*outboundStreamIds*/) override
78
+ void OnAssociationStreamsResetPerformed(std::span<const uint16_t> outboundStreamIds) override
79
79
  {
80
- // TODO: Do something here for tests.
80
+ ++this->onStreamsResetPerformedCalls;
81
+
82
+ for (const auto streamId : outboundStreamIds)
83
+ {
84
+ this->streamsResetPerformed.insert(streamId);
85
+ }
81
86
  }
82
87
 
83
88
  void OnAssociationStreamsResetFailed(
84
- std::span<const uint16_t> /*outboundStreamIds*/, std::string_view /*errorMessage*/) override
89
+ std::span<const uint16_t> outboundStreamIds, std::string_view /*errorMessage*/) override
85
90
  {
86
- // TODO: Do something here for tests.
91
+ ++this->onStreamsResetFailedCalls;
92
+
93
+ for (const auto streamId : outboundStreamIds)
94
+ {
95
+ this->streamsResetFailed.insert(streamId);
96
+ }
87
97
  }
88
98
 
89
- void OnAssociationInboundStreamsReset(std::span<const uint16_t> /*inboundStreamIds*/) override
99
+ void OnAssociationInboundStreamsReset(std::span<const uint16_t> inboundStreamIds) override
90
100
  {
91
- // TODO: Do something here for tests.
101
+ ++this->onInboundStreamsResetCalls;
102
+
103
+ for (const auto streamId : inboundStreamIds)
104
+ {
105
+ this->inboundStreamsReset.insert(streamId);
106
+ }
92
107
  }
93
108
 
94
109
  void OnAssociationStreamBufferedAmountLow(uint16_t streamId) override
@@ -208,6 +223,45 @@ namespace mocks
208
223
  return this->onTotalBufferedAmountLowCalls;
209
224
  }
210
225
 
226
+ size_t CountOnStreamsResetPerformedCalls() const
227
+ {
228
+ return this->onStreamsResetPerformedCalls;
229
+ }
230
+
231
+ bool HasStreamsResetPerformedForStreamId(uint16_t streamId) const
232
+ {
233
+ return this->streamsResetPerformed.contains(streamId);
234
+ }
235
+
236
+ size_t CountOnStreamsResetFailedCalls() const
237
+ {
238
+ return this->onStreamsResetFailedCalls;
239
+ }
240
+
241
+ bool HasStreamsResetFailedForStreamId(uint16_t streamId) const
242
+ {
243
+ return this->streamsResetFailed.contains(streamId);
244
+ }
245
+
246
+ size_t CountOnInboundStreamsResetCalls() const
247
+ {
248
+ return this->onInboundStreamsResetCalls;
249
+ }
250
+
251
+ bool HasInboundStreamsResetForStreamId(uint16_t streamId) const
252
+ {
253
+ return this->inboundStreamsReset.contains(streamId);
254
+ }
255
+
256
+ /**
257
+ * Sets the value that `OnAssociationSendData()` will return, allowing
258
+ * tests to simulate a failed send.
259
+ */
260
+ void SetSendDataResult(bool sendDataResult)
261
+ {
262
+ this->sendDataResult = sendDataResult;
263
+ }
264
+
211
265
  bool HasSentPackets() const
212
266
  {
213
267
  return !this->sentPackets.empty();
@@ -293,6 +347,13 @@ namespace mocks
293
347
  std::string erroredErrorMessage;
294
348
  std::map<uint16_t /*streamId*/, size_t /*count*/> onStreamBufferedAmountLowCalls;
295
349
  size_t onTotalBufferedAmountLowCalls{ 0 };
350
+ size_t onStreamsResetPerformedCalls{ 0 };
351
+ std::set<uint16_t /*streamId*/> streamsResetPerformed;
352
+ size_t onStreamsResetFailedCalls{ 0 };
353
+ std::set<uint16_t /*streamId*/> streamsResetFailed;
354
+ size_t onInboundStreamsResetCalls{ 0 };
355
+ std::set<uint16_t /*streamId*/> inboundStreamsReset;
356
+ bool sendDataResult{ true };
296
357
  std::deque<std::vector<uint8_t>> sentPackets;
297
358
  std::deque<::RTC::SCTP::Message> receivedMessages;
298
359
  bool transportReady{ true };
@@ -36,14 +36,20 @@ namespace mocks
36
36
 
37
37
  void Start() override
38
38
  {
39
- this->running = true;
40
- this->expiresAtMs = this->getTimeMs() + ComputeNextTimeoutMs();
39
+ this->running = true;
40
+ // NOTE: Reset the expiration count, just like the real BackoffTimerHandle
41
+ // does, so that the backoff starts over from the base timeout.
42
+ this->expirationCount = 0;
43
+ this->expiresAtMs = this->getTimeMs() + ComputeNextTimeoutMs();
41
44
  }
42
45
 
43
46
  void Stop() override
44
47
  {
45
- this->running = false;
46
- this->expiresAtMs = std::numeric_limits<uint64_t>::max();
48
+ this->running = false;
49
+ // NOTE: Reset the expiration count, just like the real BackoffTimerHandle
50
+ // does.
51
+ this->expirationCount = 0;
52
+ this->expiresAtMs = std::numeric_limits<uint64_t>::max();
47
53
  }
48
54
 
49
55
  /**
@@ -123,5 +123,12 @@ namespace mocks
123
123
  {
124
124
  this->expiresAtMs = this->getTimeMs() + ComputeNextTimeoutMs();
125
125
  }
126
+ // Once the timer is no longer running (e.g. max restarts reached), the real
127
+ // BackoffTimerHandle doesn't restart the underlying timer, so it won't fire
128
+ // again. Mirror that here so a stopped timer doesn't keep expiring.
129
+ else
130
+ {
131
+ this->expiresAtMs = std::numeric_limits<uint64_t>::max();
132
+ }
126
133
  }
127
134
  } // namespace mocks
@@ -40,7 +40,8 @@ namespace RTC
40
40
  const SctpOptions& sctpOptions,
41
41
  AssociationListenerInterface* listener,
42
42
  SharedInterface* shared,
43
- bool isDataChannel)
43
+ bool isDataChannel,
44
+ bool mayConnectOnReceivedSctpData)
44
45
  : sctpOptions(sctpOptions),
45
46
  // Our `listener` member is a `AssociationListenerDeferrer` which takes
46
47
  // `listener` argument as constructor argument.
@@ -78,7 +79,8 @@ namespace RTC
78
79
  .maxBackoffTimeoutMs = sctpOptions.timerMaxBackoffTimeoutMs,
79
80
  .maxRestarts = sctpOptions.maxRetransmissions })),
80
81
  maxPacketLength(Utils::Byte::PadDownTo4Bytes(this->sctpOptions.mtu)),
81
- isDataChannel(isDataChannel)
82
+ isDataChannel(isDataChannel),
83
+ mayConnectOnReceivedSctpData(mayConnectOnReceivedSctpData)
82
84
  {
83
85
  MS_TRACE();
84
86
  }
@@ -542,7 +544,13 @@ namespace RTC
542
544
 
543
545
  // If we are received SCTP data from the remote peer it means that we may
544
546
  // initiate the SCTP association (if not already connected).
545
- MayConnect();
547
+ //
548
+ // NOTE: This is disabled in tests that need a purely passive peer to
549
+ // mimic dcsctp's asymmetric handshake.
550
+ if (this->mayConnectOnReceivedSctpData)
551
+ {
552
+ MayConnect();
553
+ }
546
554
 
547
555
  // NOTE: It's important to create the deferrer here, otherwise it may
548
556
  // happen that MayConnect() ends calling to Connect() so we end with two
@@ -749,6 +757,7 @@ namespace RTC
749
757
  MS_TRACE();
750
758
 
751
759
  this->tcb = std::make_unique<TransmissionControlBlock>(
760
+ this,
752
761
  this->associationListenerDeferrer,
753
762
  this->sctpOptions,
754
763
  this->shared,
@@ -2425,6 +2434,15 @@ namespace RTC
2425
2434
  {
2426
2435
  MS_TRACE();
2427
2436
 
2437
+ const auto maxRestarts = this->t1InitTimer->GetMaxRestarts();
2438
+
2439
+ MS_DEBUG_TAG(
2440
+ sctp,
2441
+ "%s timer has expired [expirations:%zu, maxRestarts:%s]",
2442
+ this->t1InitTimer->GetLabel().c_str(),
2443
+ this->t1InitTimer->GetExpirationCount(),
2444
+ maxRestarts ? std::to_string(maxRestarts.value()).c_str() : "Infinite");
2445
+
2428
2446
  const AssociationListenerDeferrer::ScopedDeferrer deferrer(this->associationListenerDeferrer);
2429
2447
 
2430
2448
  AssertState(State::COOKIE_WAIT);
@@ -2445,6 +2463,15 @@ namespace RTC
2445
2463
  {
2446
2464
  MS_TRACE();
2447
2465
 
2466
+ const auto maxRestarts = this->t1CookieTimer->GetMaxRestarts();
2467
+
2468
+ MS_DEBUG_TAG(
2469
+ sctp,
2470
+ "%s timer has expired [expirations:%zu, maxRestarts:%s]",
2471
+ this->t1CookieTimer->GetLabel().c_str(),
2472
+ this->t1CookieTimer->GetExpirationCount(),
2473
+ maxRestarts ? std::to_string(maxRestarts.value()).c_str() : "Infinite");
2474
+
2448
2475
  const AssociationListenerDeferrer::ScopedDeferrer deferrer(this->associationListenerDeferrer);
2449
2476
 
2450
2477
  AssertState(State::COOKIE_ECHOED);
@@ -2467,6 +2494,15 @@ namespace RTC
2467
2494
  {
2468
2495
  MS_TRACE();
2469
2496
 
2497
+ const auto maxRestarts = this->t2ShutdownTimer->GetMaxRestarts();
2498
+
2499
+ MS_DEBUG_TAG(
2500
+ sctp,
2501
+ "%s timer has expired [expirations:%zu, maxRestarts:%s]",
2502
+ this->t2ShutdownTimer->GetLabel().c_str(),
2503
+ this->t2ShutdownTimer->GetExpirationCount(),
2504
+ maxRestarts ? std::to_string(maxRestarts.value()).c_str() : "Infinite");
2505
+
2470
2506
  AssertState(State::SHUTDOWN_SENT, State::SHUTDOWN_ACK_SENT);
2471
2507
  AssertHasTcb();
2472
2508
 
@@ -2811,15 +2847,6 @@ namespace RTC
2811
2847
  {
2812
2848
  MS_TRACE();
2813
2849
 
2814
- const auto maxRestarts = backoffTimer->GetMaxRestarts();
2815
-
2816
- MS_DEBUG_TAG(
2817
- sctp,
2818
- "%s timer has expired [expìrations:%zu/%s]",
2819
- backoffTimer->GetLabel().c_str(),
2820
- backoffTimer->GetExpirationCount(),
2821
- maxRestarts ? std::to_string(maxRestarts.value()).c_str() : "Infinite");
2822
-
2823
2850
  if (backoffTimer == this->t1InitTimer.get())
2824
2851
  {
2825
2852
  OnT1InitTimer(baseTimeoutMs, stop);
@@ -2833,5 +2860,18 @@ namespace RTC
2833
2860
  OnT2ShutdownTimer(baseTimeoutMs, stop);
2834
2861
  }
2835
2862
  }
2863
+
2864
+ void Association::OnTransmissionControlBlockTooManyTxErrors()
2865
+ {
2866
+ MS_TRACE();
2867
+
2868
+ // NOTE: This is invoked from within a TCB timer handler (t3-rtx, heartbeat
2869
+ // or RE-CONFIG timeout). `InternalClose()` destroys the TCB synchronously,
2870
+ // which is safe because the calling timer handler sets the BackoffTimerHandle
2871
+ // `stop` flag and doesn't touch any member afterwards. `InternalClose()` does
2872
+ // not establish its own deferrer scope, so it relies on the active one set up
2873
+ // by the timer handler.
2874
+ InternalClose(Types::ErrorKind::TOO_MANY_RETRIES, "too many transmission errors");
2875
+ }
2836
2876
  } // namespace SCTP
2837
2877
  } // namespace RTC