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.
- package/npm-scripts.mjs +26 -0
- package/package.json +4 -3
- package/worker/include/RTC/SCTP/association/Association.hpp +16 -2
- package/worker/include/RTC/SCTP/association/HeartbeatHandler.hpp +3 -3
- package/worker/include/RTC/SCTP/association/StreamResetHandler.hpp +3 -3
- package/worker/include/RTC/SCTP/association/TransmissionControlBlock.hpp +19 -4
- package/worker/include/RTC/SCTP/association/TransmissionControlBlockContextInterface.hpp +19 -2
- package/worker/include/RTC/SCTP/public/SctpTypes.hpp +2 -0
- package/worker/include/RTC/SCTP/rx/ReassemblyQueue.hpp +7 -0
- package/worker/include/RTC/SCTP/tx/RetransmissionErrorCounter.hpp +3 -4
- package/worker/meson.build +3 -0
- package/worker/mocks/include/RTC/SCTP/association/MockAssociationListener.hpp +68 -7
- package/worker/mocks/include/handles/MockBackoffTimerHandle.hpp +10 -4
- package/worker/mocks/src/handles/MockBackoffTimerHandle.cpp +7 -0
- package/worker/src/RTC/SCTP/association/Association.cpp +52 -12
- package/worker/src/RTC/SCTP/association/HeartbeatHandler.cpp +48 -18
- package/worker/src/RTC/SCTP/association/StreamResetHandler.cpp +26 -17
- package/worker/src/RTC/SCTP/association/TransmissionControlBlock.cpp +48 -15
- package/worker/src/RTC/SCTP/rx/ReassemblyQueue.cpp +17 -8
- package/worker/src/RTC/Transport.cpp +1 -1
- package/worker/test/src/RTC/SCTP/association/TestAssociation.cpp +1755 -0
- package/worker/test/src/RTC/SCTP/association/TestHeartbeatHandler.cpp +4 -1
- package/worker/test/src/RTC/SCTP/association/TestPacketSender.cpp +121 -0
- package/worker/test/src/RTC/SCTP/association/TestStreamResetHandler.cpp +369 -0
- package/worker/test/src/RTC/SCTP/association/TestTransmissionControlBlock.cpp +16 -2
- package/worker/test/src/RTC/SCTP/rx/TestReassemblyQueue.cpp +24 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#include "common.hpp"
|
|
2
|
+
#include "RTC/SCTP/association/AssociationListenerDeferrer.hpp"
|
|
2
3
|
#include "RTC/SCTP/association/HeartbeatHandler.hpp"
|
|
3
4
|
#include "RTC/SCTP/packet/Packet.hpp"
|
|
4
5
|
#include "RTC/SCTP/packet/chunks/HeartbeatAckChunk.hpp"
|
|
@@ -38,7 +39,7 @@ SCENARIO("SCTP HeartbeatHandler", "[sctp][heartbeathandler]")
|
|
|
38
39
|
return this->nowMs;
|
|
39
40
|
}),
|
|
40
41
|
heartbeatHandler(
|
|
41
|
-
this->
|
|
42
|
+
this->associationListenerDeferrer,
|
|
42
43
|
this->sctpOptions,
|
|
43
44
|
std::addressof(this->shared),
|
|
44
45
|
std::addressof(this->tcbContext))
|
|
@@ -60,6 +61,8 @@ SCENARIO("SCTP HeartbeatHandler", "[sctp][heartbeathandler]")
|
|
|
60
61
|
public:
|
|
61
62
|
RTC::SCTP::SctpOptions sctpOptions;
|
|
62
63
|
mocks::RTC::SCTP::MockAssociationListener associationListener;
|
|
64
|
+
RTC::SCTP::AssociationListenerDeferrer associationListenerDeferrer{ std::addressof(
|
|
65
|
+
this->associationListener) };
|
|
63
66
|
mocks::RTC::SCTP::MockTransmissionControlBlockContext tcbContext;
|
|
64
67
|
mocks::MockShared shared;
|
|
65
68
|
RTC::SCTP::HeartbeatHandler heartbeatHandler;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
#include "common.hpp"
|
|
2
|
+
#include "RTC/SCTP/association/PacketSender.hpp"
|
|
3
|
+
#include "RTC/SCTP/packet/Packet.hpp"
|
|
4
|
+
#include "RTC/SCTP/packet/chunks/CookieAckChunk.hpp"
|
|
5
|
+
#include "RTC/SCTP/public/SctpOptions.hpp"
|
|
6
|
+
#include "mocks/include/RTC/SCTP/association/MockAssociationListener.hpp"
|
|
7
|
+
#include <catch2/catch_test_macros.hpp>
|
|
8
|
+
#include <vector>
|
|
9
|
+
|
|
10
|
+
namespace
|
|
11
|
+
{
|
|
12
|
+
constexpr uint32_t VerificationTag{ 123 };
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A PacketSender::Listener that records the last `OnPacketSenderPacketSent()`
|
|
16
|
+
* call.
|
|
17
|
+
*/
|
|
18
|
+
class MockPacketSenderListener : public RTC::SCTP::PacketSender::Listener
|
|
19
|
+
{
|
|
20
|
+
public:
|
|
21
|
+
void OnPacketSenderPacketSent(
|
|
22
|
+
RTC::SCTP::PacketSender* /*packetSender*/, const RTC::SCTP::Packet* /*packet*/, bool sent) override
|
|
23
|
+
{
|
|
24
|
+
++this->onPacketSentCalls;
|
|
25
|
+
this->lastSent = sent;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public:
|
|
29
|
+
size_t onPacketSentCalls{ 0 };
|
|
30
|
+
bool lastSent{ false };
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Builds an SCTP packet carrying a single COOKIE-ACK chunk into `buffer`
|
|
35
|
+
* (which must outlive the returned packet).
|
|
36
|
+
*/
|
|
37
|
+
std::unique_ptr<RTC::SCTP::Packet> buildPacketWithCookieAck(std::vector<uint8_t>& buffer)
|
|
38
|
+
{
|
|
39
|
+
std::unique_ptr<RTC::SCTP::Packet> packet(
|
|
40
|
+
RTC::SCTP::Packet::Factory(buffer.data(), buffer.size()));
|
|
41
|
+
|
|
42
|
+
packet->SetVerificationTag(VerificationTag);
|
|
43
|
+
|
|
44
|
+
auto* cookieAckChunk = packet->BuildChunkInPlace<RTC::SCTP::CookieAckChunk>();
|
|
45
|
+
|
|
46
|
+
cookieAckChunk->Consolidate();
|
|
47
|
+
|
|
48
|
+
return packet;
|
|
49
|
+
}
|
|
50
|
+
} // namespace
|
|
51
|
+
|
|
52
|
+
SCENARIO("SCTP PacketSender", "[sctp][packetsender]")
|
|
53
|
+
{
|
|
54
|
+
const RTC::SCTP::SctpOptions sctpOptions;
|
|
55
|
+
|
|
56
|
+
MockPacketSenderListener listener;
|
|
57
|
+
mocks::RTC::SCTP::MockAssociationListener associationListener;
|
|
58
|
+
RTC::SCTP::PacketSender packetSender(std::addressof(listener), associationListener);
|
|
59
|
+
|
|
60
|
+
SECTION("sends a packet and notifies the listener")
|
|
61
|
+
{
|
|
62
|
+
std::vector<uint8_t> buffer(sctpOptions.mtu);
|
|
63
|
+
const auto packet = buildPacketWithCookieAck(buffer);
|
|
64
|
+
|
|
65
|
+
REQUIRE(packetSender.SendPacket(packet.get()) == true);
|
|
66
|
+
REQUIRE(listener.onPacketSentCalls == 1);
|
|
67
|
+
REQUIRE(listener.lastSent == true);
|
|
68
|
+
REQUIRE(associationListener.HasSentPackets() == true);
|
|
69
|
+
|
|
70
|
+
// The checksum was written.
|
|
71
|
+
const auto sentBuffer = associationListener.ConsumeFirstSentPacket();
|
|
72
|
+
const std::unique_ptr<RTC::SCTP::Packet> sentPacket{ RTC::SCTP::Packet::Parse(
|
|
73
|
+
sentBuffer.data(), sentBuffer.size()) };
|
|
74
|
+
|
|
75
|
+
REQUIRE(sentPacket);
|
|
76
|
+
REQUIRE(sentPacket->ValidateCRC32cChecksum() == true);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
SECTION("reports a failed send to the listener")
|
|
80
|
+
{
|
|
81
|
+
// Simulate that the transport fails to send the packet.
|
|
82
|
+
associationListener.SetSendDataResult(false);
|
|
83
|
+
|
|
84
|
+
std::vector<uint8_t> buffer(sctpOptions.mtu);
|
|
85
|
+
const auto packet = buildPacketWithCookieAck(buffer);
|
|
86
|
+
|
|
87
|
+
REQUIRE(packetSender.SendPacket(packet.get()) == false);
|
|
88
|
+
REQUIRE(listener.onPacketSentCalls == 1);
|
|
89
|
+
REQUIRE(listener.lastSent == false);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
SECTION("doesn't send a packet without chunks")
|
|
93
|
+
{
|
|
94
|
+
std::vector<uint8_t> buffer(sctpOptions.mtu);
|
|
95
|
+
const std::unique_ptr<RTC::SCTP::Packet> packet(
|
|
96
|
+
RTC::SCTP::Packet::Factory(buffer.data(), buffer.size()));
|
|
97
|
+
|
|
98
|
+
packet->SetVerificationTag(VerificationTag);
|
|
99
|
+
|
|
100
|
+
REQUIRE(packetSender.SendPacket(packet.get()) == false);
|
|
101
|
+
REQUIRE(listener.onPacketSentCalls == 0);
|
|
102
|
+
REQUIRE(associationListener.HasSentPackets() == false);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
SECTION("doesn't write the checksum when not requested")
|
|
106
|
+
{
|
|
107
|
+
std::vector<uint8_t> buffer(sctpOptions.mtu);
|
|
108
|
+
const auto packet = buildPacketWithCookieAck(buffer);
|
|
109
|
+
|
|
110
|
+
packet->SetChecksum(0);
|
|
111
|
+
|
|
112
|
+
REQUIRE(packetSender.SendPacket(packet.get(), /*writeChecksum*/ false) == true);
|
|
113
|
+
|
|
114
|
+
const auto sentBuffer = associationListener.ConsumeFirstSentPacket();
|
|
115
|
+
const std::unique_ptr<RTC::SCTP::Packet> sentPacket{ RTC::SCTP::Packet::Parse(
|
|
116
|
+
sentBuffer.data(), sentBuffer.size()) };
|
|
117
|
+
|
|
118
|
+
REQUIRE(sentPacket);
|
|
119
|
+
REQUIRE(sentPacket->GetChecksum() == 0);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
#include "common.hpp"
|
|
2
|
+
#include "handles/BackoffTimerHandleInterface.hpp"
|
|
3
|
+
#include "RTC/SCTP/association/AssociationListenerDeferrer.hpp"
|
|
4
|
+
#include "RTC/SCTP/association/StreamResetHandler.hpp"
|
|
5
|
+
#include "RTC/SCTP/packet/Packet.hpp"
|
|
6
|
+
#include "RTC/SCTP/packet/chunks/ReConfigChunk.hpp"
|
|
7
|
+
#include "RTC/SCTP/packet/parameters/IncomingSsnResetRequestParameter.hpp"
|
|
8
|
+
#include "RTC/SCTP/packet/parameters/OutgoingSsnResetRequestParameter.hpp"
|
|
9
|
+
#include "RTC/SCTP/packet/parameters/ReconfigurationResponseParameter.hpp"
|
|
10
|
+
#include "RTC/SCTP/public/SctpOptions.hpp"
|
|
11
|
+
#include "RTC/SCTP/rx/DataTracker.hpp"
|
|
12
|
+
#include "RTC/SCTP/rx/ReassemblyQueue.hpp"
|
|
13
|
+
#include "RTC/SCTP/tx/RetransmissionQueue.hpp"
|
|
14
|
+
#include "RTC/SCTP/tx/RoundRobinSendQueue.hpp"
|
|
15
|
+
#include "mocks/include/MockShared.hpp"
|
|
16
|
+
#include "mocks/include/RTC/SCTP/association/MockAssociationListener.hpp"
|
|
17
|
+
#include "mocks/include/RTC/SCTP/association/MockTransmissionControlBlockContext.hpp"
|
|
18
|
+
#include <catch2/catch_test_macros.hpp>
|
|
19
|
+
#include <vector>
|
|
20
|
+
|
|
21
|
+
namespace
|
|
22
|
+
{
|
|
23
|
+
constexpr uint32_t Arwnd{ 131072 };
|
|
24
|
+
constexpr uint32_t LocalInitialTsn{ 0 };
|
|
25
|
+
constexpr uint32_t RemoteInitialTsn{ 0 };
|
|
26
|
+
constexpr uint64_t InitialNowMs{ 10000 };
|
|
27
|
+
constexpr uint64_t RtoMs{ 250 };
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* A RTC::SCTP::StreamResetHandler under test, together with all the (real)
|
|
31
|
+
* components it depends on, its simulated clock and listener.
|
|
32
|
+
*/
|
|
33
|
+
class TestStreamResetHandler
|
|
34
|
+
{
|
|
35
|
+
public:
|
|
36
|
+
TestStreamResetHandler()
|
|
37
|
+
// NOTE: The order in which these members are initialized is **critical**.
|
|
38
|
+
: shared(/*getTimeMs*/
|
|
39
|
+
[this]()
|
|
40
|
+
{
|
|
41
|
+
return this->nowMs;
|
|
42
|
+
}),
|
|
43
|
+
tcbContext(this->associationListener, this->sctpOptions),
|
|
44
|
+
delayedAckTimer(this->shared.CreateBackoffTimer(
|
|
45
|
+
BackoffTimerHandleInterface::BackoffTimerHandleOptions{
|
|
46
|
+
.listener = std::addressof(this->backoffTimerListener),
|
|
47
|
+
.label = "mock-sctp-delayed-ack",
|
|
48
|
+
.baseTimeoutMs = this->sctpOptions.initialRtoMs,
|
|
49
|
+
.backoffAlgorithm = BackoffTimerHandleInterface::BackoffAlgorithm::FIXED })),
|
|
50
|
+
t3RtxTimer(this->shared.CreateBackoffTimer(
|
|
51
|
+
BackoffTimerHandleInterface::BackoffTimerHandleOptions{
|
|
52
|
+
.listener = std::addressof(this->backoffTimerListener),
|
|
53
|
+
.label = "mock-sctp-t3-rtx",
|
|
54
|
+
.baseTimeoutMs = this->sctpOptions.initialRtoMs,
|
|
55
|
+
.backoffAlgorithm = BackoffTimerHandleInterface::BackoffAlgorithm::EXPONENTIAL })),
|
|
56
|
+
sendQueue(
|
|
57
|
+
this->associationListener,
|
|
58
|
+
this->sctpOptions.mtu,
|
|
59
|
+
this->sctpOptions.defaultStreamPriority,
|
|
60
|
+
this->sctpOptions.totalBufferedAmountLowThreshold),
|
|
61
|
+
dataTracker(this->delayedAckTimer.get(), RemoteInitialTsn),
|
|
62
|
+
reassemblyQueue(this->sctpOptions.maxReceiverWindowBufferSize),
|
|
63
|
+
retransmissionQueue(
|
|
64
|
+
std::addressof(this->retransmissionQueueListener),
|
|
65
|
+
this->associationListener,
|
|
66
|
+
LocalInitialTsn,
|
|
67
|
+
Arwnd,
|
|
68
|
+
this->sendQueue,
|
|
69
|
+
this->t3RtxTimer.get(),
|
|
70
|
+
this->sctpOptions,
|
|
71
|
+
/*supportsPartialReliability*/ true,
|
|
72
|
+
/*useMessageInterleaving*/ false),
|
|
73
|
+
streamResetHandler(
|
|
74
|
+
this->associationListenerDeferrer,
|
|
75
|
+
std::addressof(this->shared),
|
|
76
|
+
std::addressof(this->tcbContext),
|
|
77
|
+
std::addressof(this->dataTracker),
|
|
78
|
+
std::addressof(this->reassemblyQueue),
|
|
79
|
+
std::addressof(this->retransmissionQueue))
|
|
80
|
+
{
|
|
81
|
+
this->tcbContext.WillGetCurrentRtoMsOnce(
|
|
82
|
+
[]()
|
|
83
|
+
{
|
|
84
|
+
return RtoMs;
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
public:
|
|
89
|
+
/**
|
|
90
|
+
* Advances the simulated clock by `incrementMs`.
|
|
91
|
+
*/
|
|
92
|
+
void AdvanceTimeMs(uint64_t incrementMs)
|
|
93
|
+
{
|
|
94
|
+
this->nowMs += incrementMs;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Handles a received RE-CONFIG chunk within a deferrer scope, just like
|
|
99
|
+
* association does before dispatching to the handler.
|
|
100
|
+
*/
|
|
101
|
+
void HandleReceivedReConfigChunk(const RTC::SCTP::ReConfigChunk* receivedReConfigChunk)
|
|
102
|
+
{
|
|
103
|
+
const RTC::SCTP::AssociationListenerDeferrer::ScopedDeferrer deferrer(
|
|
104
|
+
this->associationListenerDeferrer);
|
|
105
|
+
|
|
106
|
+
this->streamResetHandler.HandleReceivedReConfigChunk(receivedReConfigChunk);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
private:
|
|
110
|
+
/**
|
|
111
|
+
* A no-op BackoffTimerHandle listener for the timers owned directly by this
|
|
112
|
+
* fixture (the ones owned by the handler are driven by the handler itself).
|
|
113
|
+
*/
|
|
114
|
+
class BackoffTimerListener : public BackoffTimerHandleInterface::Listener
|
|
115
|
+
{
|
|
116
|
+
public:
|
|
117
|
+
void OnBackoffTimer(
|
|
118
|
+
BackoffTimerHandleInterface* /*backoffTimer*/, uint64_t& /*baseTimeoutMs*/, bool& /*stop*/) override
|
|
119
|
+
{
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* A no-op RTC::SCTP::RetransmissionQueue listener.
|
|
125
|
+
*/
|
|
126
|
+
class RetransmissionQueueListener : public RTC::SCTP::RetransmissionQueue::Listener
|
|
127
|
+
{
|
|
128
|
+
public:
|
|
129
|
+
void OnRetransmissionQueueNewRttMs(uint64_t /*rttMs*/) override
|
|
130
|
+
{
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
void OnRetransmissionQueueClearRetransmissionCounter() override
|
|
134
|
+
{
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
// NOTE: Public members for testing.
|
|
139
|
+
public:
|
|
140
|
+
uint64_t nowMs{ InitialNowMs };
|
|
141
|
+
RTC::SCTP::SctpOptions sctpOptions;
|
|
142
|
+
BackoffTimerListener backoffTimerListener;
|
|
143
|
+
RetransmissionQueueListener retransmissionQueueListener;
|
|
144
|
+
mocks::RTC::SCTP::MockAssociationListener associationListener;
|
|
145
|
+
RTC::SCTP::AssociationListenerDeferrer associationListenerDeferrer{ std::addressof(
|
|
146
|
+
this->associationListener) };
|
|
147
|
+
mocks::MockShared shared;
|
|
148
|
+
mocks::RTC::SCTP::MockTransmissionControlBlockContext tcbContext;
|
|
149
|
+
const std::unique_ptr<BackoffTimerHandleInterface> delayedAckTimer;
|
|
150
|
+
const std::unique_ptr<BackoffTimerHandleInterface> t3RtxTimer;
|
|
151
|
+
RTC::SCTP::RoundRobinSendQueue sendQueue;
|
|
152
|
+
RTC::SCTP::DataTracker dataTracker;
|
|
153
|
+
RTC::SCTP::ReassemblyQueue reassemblyQueue;
|
|
154
|
+
RTC::SCTP::RetransmissionQueue retransmissionQueue;
|
|
155
|
+
RTC::SCTP::StreamResetHandler streamResetHandler;
|
|
156
|
+
};
|
|
157
|
+
} // namespace
|
|
158
|
+
|
|
159
|
+
SCENARIO("SCTP RTC::SCTP::StreamResetHandler", "[sctp][streamresethandler]")
|
|
160
|
+
{
|
|
161
|
+
SECTION("a chunk with no parameters returns an error")
|
|
162
|
+
{
|
|
163
|
+
TestStreamResetHandler test;
|
|
164
|
+
|
|
165
|
+
std::vector<uint8_t> buffer(test.sctpOptions.mtu);
|
|
166
|
+
|
|
167
|
+
const std::unique_ptr<RTC::SCTP::ReConfigChunk> reConfigChunk{ RTC::SCTP::ReConfigChunk::Factory(
|
|
168
|
+
buffer.data(), buffer.size()) };
|
|
169
|
+
|
|
170
|
+
test.HandleReceivedReConfigChunk(reConfigChunk.get());
|
|
171
|
+
|
|
172
|
+
REQUIRE(test.associationListener.HasErrored() == true);
|
|
173
|
+
REQUIRE(test.associationListener.HasSentPackets() == false);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
SECTION("a chunk with invalid parameters returns an error")
|
|
177
|
+
{
|
|
178
|
+
TestStreamResetHandler test;
|
|
179
|
+
|
|
180
|
+
std::vector<uint8_t> buffer(test.sctpOptions.mtu);
|
|
181
|
+
|
|
182
|
+
const std::unique_ptr<RTC::SCTP::ReConfigChunk> reConfigChunk{ RTC::SCTP::ReConfigChunk::Factory(
|
|
183
|
+
buffer.data(), buffer.size()) };
|
|
184
|
+
|
|
185
|
+
// Two RTC::SCTP::OutgoingSsnResetRequestParameter in a RE-CONFIG is not valid.
|
|
186
|
+
for (const uint32_t reqSeqNbr : { 1u, 2u })
|
|
187
|
+
{
|
|
188
|
+
auto* parameter =
|
|
189
|
+
reConfigChunk->BuildParameterInPlace<RTC::SCTP::OutgoingSsnResetRequestParameter>();
|
|
190
|
+
|
|
191
|
+
parameter->SetReconfigurationRequestSequenceNumber(reqSeqNbr);
|
|
192
|
+
parameter->SetReconfigurationResponseSequenceNumber(10);
|
|
193
|
+
parameter->SetSenderLastAssignedTsn(RemoteInitialTsn);
|
|
194
|
+
parameter->AddStreamId(static_cast<uint16_t>(reqSeqNbr));
|
|
195
|
+
parameter->Consolidate();
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
test.HandleReceivedReConfigChunk(reConfigChunk.get());
|
|
199
|
+
|
|
200
|
+
REQUIRE(test.associationListener.HasErrored() == true);
|
|
201
|
+
REQUIRE(test.associationListener.HasSentPackets() == false);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
SECTION("sends an outgoing request directly")
|
|
205
|
+
{
|
|
206
|
+
TestStreamResetHandler test;
|
|
207
|
+
|
|
208
|
+
const std::vector<uint16_t> streamIds{ 42 };
|
|
209
|
+
|
|
210
|
+
test.streamResetHandler.ResetStreams(streamIds);
|
|
211
|
+
|
|
212
|
+
REQUIRE(test.streamResetHandler.ShouldSendStreamResetRequest() == true);
|
|
213
|
+
|
|
214
|
+
const auto packet = test.tcbContext.CreatePacket();
|
|
215
|
+
|
|
216
|
+
test.streamResetHandler.AddStreamResetRequest(packet.get());
|
|
217
|
+
|
|
218
|
+
const auto* reConfigChunk = packet->GetFirstChunkOfType<RTC::SCTP::ReConfigChunk>();
|
|
219
|
+
|
|
220
|
+
REQUIRE(reConfigChunk);
|
|
221
|
+
|
|
222
|
+
const auto* parameter =
|
|
223
|
+
reConfigChunk->GetFirstParameterOfType<RTC::SCTP::OutgoingSsnResetRequestParameter>();
|
|
224
|
+
|
|
225
|
+
REQUIRE(parameter);
|
|
226
|
+
REQUIRE(parameter->GetStreamIds() == std::vector<uint16_t>{ 42 });
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
SECTION("resets multiple streams in one request")
|
|
230
|
+
{
|
|
231
|
+
TestStreamResetHandler test;
|
|
232
|
+
|
|
233
|
+
test.streamResetHandler.ResetStreams(std::vector<uint16_t>{ 42 });
|
|
234
|
+
test.streamResetHandler.ResetStreams(std::vector<uint16_t>{ 43, 44, 41 });
|
|
235
|
+
test.streamResetHandler.ResetStreams(std::vector<uint16_t>{ 42, 40 });
|
|
236
|
+
|
|
237
|
+
REQUIRE(test.streamResetHandler.ShouldSendStreamResetRequest() == true);
|
|
238
|
+
|
|
239
|
+
const auto packet = test.tcbContext.CreatePacket();
|
|
240
|
+
|
|
241
|
+
test.streamResetHandler.AddStreamResetRequest(packet.get());
|
|
242
|
+
|
|
243
|
+
const auto* reConfigChunk = packet->GetFirstChunkOfType<RTC::SCTP::ReConfigChunk>();
|
|
244
|
+
|
|
245
|
+
REQUIRE(reConfigChunk);
|
|
246
|
+
|
|
247
|
+
const auto* parameter =
|
|
248
|
+
reConfigChunk->GetFirstParameterOfType<RTC::SCTP::OutgoingSsnResetRequestParameter>();
|
|
249
|
+
|
|
250
|
+
REQUIRE(parameter);
|
|
251
|
+
|
|
252
|
+
auto streamIds = parameter->GetStreamIds();
|
|
253
|
+
|
|
254
|
+
std::ranges::sort(streamIds);
|
|
255
|
+
|
|
256
|
+
REQUIRE(streamIds == std::vector<uint16_t>{ 40, 41, 42, 43, 44 });
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
SECTION("commits the reset on a positive response")
|
|
260
|
+
{
|
|
261
|
+
TestStreamResetHandler test;
|
|
262
|
+
|
|
263
|
+
test.streamResetHandler.ResetStreams(std::vector<uint16_t>{ 42 });
|
|
264
|
+
|
|
265
|
+
REQUIRE(test.streamResetHandler.ShouldSendStreamResetRequest() == true);
|
|
266
|
+
|
|
267
|
+
const auto packet = test.tcbContext.CreatePacket();
|
|
268
|
+
|
|
269
|
+
test.streamResetHandler.AddStreamResetRequest(packet.get());
|
|
270
|
+
|
|
271
|
+
const auto* parameter =
|
|
272
|
+
packet->GetFirstChunkOfType<RTC::SCTP::ReConfigChunk>()
|
|
273
|
+
->GetFirstParameterOfType<RTC::SCTP::OutgoingSsnResetRequestParameter>();
|
|
274
|
+
|
|
275
|
+
REQUIRE(parameter);
|
|
276
|
+
|
|
277
|
+
const uint32_t reqSeqNbr = parameter->GetReconfigurationRequestSequenceNumber();
|
|
278
|
+
|
|
279
|
+
// Build a RE-CONFIG response with a successful result.
|
|
280
|
+
std::vector<uint8_t> responseBuffer(test.sctpOptions.mtu);
|
|
281
|
+
const std::unique_ptr<RTC::SCTP::ReConfigChunk> responseChunk{ RTC::SCTP::ReConfigChunk::Factory(
|
|
282
|
+
responseBuffer.data(), responseBuffer.size()) };
|
|
283
|
+
|
|
284
|
+
auto* response =
|
|
285
|
+
responseChunk->BuildParameterInPlace<RTC::SCTP::ReconfigurationResponseParameter>();
|
|
286
|
+
|
|
287
|
+
response->SetReconfigurationResponseSequenceNumber(reqSeqNbr);
|
|
288
|
+
response->SetResult(RTC::SCTP::ReconfigurationResponseParameter::Result::SUCCESS_PERFORMED);
|
|
289
|
+
response->Consolidate();
|
|
290
|
+
|
|
291
|
+
test.HandleReceivedReConfigChunk(responseChunk.get());
|
|
292
|
+
|
|
293
|
+
REQUIRE(test.associationListener.HasStreamsResetPerformedForStreamId(42) == true);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
SECTION("rolls back the reset on an error response")
|
|
297
|
+
{
|
|
298
|
+
TestStreamResetHandler test;
|
|
299
|
+
|
|
300
|
+
test.streamResetHandler.ResetStreams(std::vector<uint16_t>{ 42 });
|
|
301
|
+
|
|
302
|
+
REQUIRE(test.streamResetHandler.ShouldSendStreamResetRequest() == true);
|
|
303
|
+
|
|
304
|
+
const auto packet = test.tcbContext.CreatePacket();
|
|
305
|
+
|
|
306
|
+
test.streamResetHandler.AddStreamResetRequest(packet.get());
|
|
307
|
+
|
|
308
|
+
const auto* parameter =
|
|
309
|
+
packet->GetFirstChunkOfType<RTC::SCTP::ReConfigChunk>()
|
|
310
|
+
->GetFirstParameterOfType<RTC::SCTP::OutgoingSsnResetRequestParameter>();
|
|
311
|
+
|
|
312
|
+
REQUIRE(parameter);
|
|
313
|
+
|
|
314
|
+
const uint32_t reqSeqNbr = parameter->GetReconfigurationRequestSequenceNumber();
|
|
315
|
+
|
|
316
|
+
// Build a RE-CONFIG response with an error result.
|
|
317
|
+
std::vector<uint8_t> responseBuffer(test.sctpOptions.mtu);
|
|
318
|
+
const std::unique_ptr<RTC::SCTP::ReConfigChunk> responseChunk{ RTC::SCTP::ReConfigChunk::Factory(
|
|
319
|
+
responseBuffer.data(), responseBuffer.size()) };
|
|
320
|
+
|
|
321
|
+
auto* response =
|
|
322
|
+
responseChunk->BuildParameterInPlace<RTC::SCTP::ReconfigurationResponseParameter>();
|
|
323
|
+
|
|
324
|
+
response->SetReconfigurationResponseSequenceNumber(reqSeqNbr);
|
|
325
|
+
response->SetResult(
|
|
326
|
+
RTC::SCTP::ReconfigurationResponseParameter::Result::ERROR_BAD_SEQUENCE_NUMBER);
|
|
327
|
+
response->Consolidate();
|
|
328
|
+
|
|
329
|
+
test.HandleReceivedReConfigChunk(responseChunk.get());
|
|
330
|
+
|
|
331
|
+
REQUIRE(test.associationListener.HasStreamsResetFailedForStreamId(42) == true);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
SECTION("an incoming stream reset request returns nothing performed")
|
|
335
|
+
{
|
|
336
|
+
TestStreamResetHandler test;
|
|
337
|
+
|
|
338
|
+
std::vector<uint8_t> buffer(test.sctpOptions.mtu);
|
|
339
|
+
const std::unique_ptr<RTC::SCTP::ReConfigChunk> reConfigChunk{ RTC::SCTP::ReConfigChunk::Factory(
|
|
340
|
+
buffer.data(), buffer.size()) };
|
|
341
|
+
|
|
342
|
+
auto* parameter =
|
|
343
|
+
reConfigChunk->BuildParameterInPlace<RTC::SCTP::IncomingSsnResetRequestParameter>();
|
|
344
|
+
|
|
345
|
+
parameter->SetReconfigurationRequestSequenceNumber(RemoteInitialTsn);
|
|
346
|
+
parameter->AddStreamId(1);
|
|
347
|
+
parameter->Consolidate();
|
|
348
|
+
|
|
349
|
+
test.HandleReceivedReConfigChunk(reConfigChunk.get());
|
|
350
|
+
|
|
351
|
+
// A RE-CONFIG response is sent back.
|
|
352
|
+
const auto sentBuffer = test.associationListener.ConsumeFirstSentPacket();
|
|
353
|
+
|
|
354
|
+
REQUIRE(!sentBuffer.empty());
|
|
355
|
+
|
|
356
|
+
const std::unique_ptr<RTC::SCTP::Packet> sentPacket{ RTC::SCTP::Packet::Parse(
|
|
357
|
+
sentBuffer.data(), sentBuffer.size()) };
|
|
358
|
+
|
|
359
|
+
REQUIRE(sentPacket);
|
|
360
|
+
|
|
361
|
+
const auto* response = sentPacket->GetFirstChunkOfType<RTC::SCTP::ReConfigChunk>()
|
|
362
|
+
->GetFirstParameterOfType<RTC::SCTP::ReconfigurationResponseParameter>();
|
|
363
|
+
|
|
364
|
+
REQUIRE(response);
|
|
365
|
+
REQUIRE(
|
|
366
|
+
response->GetResult() ==
|
|
367
|
+
RTC::SCTP::ReconfigurationResponseParameter::Result::SUCCESS_NOTHING_TO_DO);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#include "common.hpp"
|
|
2
2
|
#include "DepLibUV.hpp"
|
|
3
|
+
#include "RTC/SCTP/association/AssociationListenerDeferrer.hpp"
|
|
3
4
|
#include "RTC/SCTP/association/NegotiatedCapabilities.hpp"
|
|
4
5
|
#include "RTC/SCTP/association/PacketSender.hpp"
|
|
5
6
|
#include "RTC/SCTP/association/TransmissionControlBlock.hpp"
|
|
@@ -29,9 +30,19 @@ SCENARIO("SCTP TransmissionControlBlock", "[sctp][transmissioncontrolblock]")
|
|
|
29
30
|
}
|
|
30
31
|
};
|
|
31
32
|
|
|
33
|
+
class MockTcbContextListener : public RTC::SCTP::TransmissionControlBlockContextInterface::Listener
|
|
34
|
+
{
|
|
35
|
+
public:
|
|
36
|
+
void OnTransmissionControlBlockTooManyTxErrors() override
|
|
37
|
+
{
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
32
41
|
const RTC::SCTP::SctpOptions sctpOptions;
|
|
33
42
|
|
|
34
43
|
mocks::RTC::SCTP::MockAssociationListener associationListener;
|
|
44
|
+
RTC::SCTP::AssociationListenerDeferrer associationListenerDeferrer(
|
|
45
|
+
std::addressof(associationListener));
|
|
35
46
|
mocks::MockShared shared(/*getTimeMs*/
|
|
36
47
|
[]()
|
|
37
48
|
{
|
|
@@ -41,6 +52,7 @@ SCENARIO("SCTP TransmissionControlBlock", "[sctp][transmissioncontrolblock]")
|
|
|
41
52
|
RTC::SCTP::NegotiatedCapabilities negotiatedCapabilities;
|
|
42
53
|
MockPacketSenderListener packetSenderListener;
|
|
43
54
|
RTC::SCTP::PacketSender packetSender(std::addressof(packetSenderListener), associationListener);
|
|
55
|
+
MockTcbContextListener tcbContextListener;
|
|
44
56
|
|
|
45
57
|
auto isAssociationEstablished = []()
|
|
46
58
|
{
|
|
@@ -54,7 +66,8 @@ SCENARIO("SCTP TransmissionControlBlock", "[sctp][transmissioncontrolblock]")
|
|
|
54
66
|
sendQueue.ExpectEnableMessageInterleavingCalledWith(false);
|
|
55
67
|
|
|
56
68
|
const RTC::SCTP::TransmissionControlBlock tcb(
|
|
57
|
-
|
|
69
|
+
std::addressof(tcbContextListener),
|
|
70
|
+
associationListenerDeferrer,
|
|
58
71
|
sctpOptions,
|
|
59
72
|
std::addressof(shared),
|
|
60
73
|
sendQueue,
|
|
@@ -79,7 +92,8 @@ SCENARIO("SCTP TransmissionControlBlock", "[sctp][transmissioncontrolblock]")
|
|
|
79
92
|
sendQueue.ExpectEnableMessageInterleavingCalledWith(true);
|
|
80
93
|
|
|
81
94
|
const RTC::SCTP::TransmissionControlBlock tcb(
|
|
82
|
-
|
|
95
|
+
std::addressof(tcbContextListener),
|
|
96
|
+
associationListenerDeferrer,
|
|
83
97
|
sctpOptions,
|
|
84
98
|
std::addressof(shared),
|
|
85
99
|
sendQueue,
|
|
@@ -554,4 +554,28 @@ SCENARIO("SCTP ReassemblyQueue", "[sctp][reassemblyqueue]")
|
|
|
554
554
|
REQUIRE(messages[0].GetPayloadProtocolId() == 53);
|
|
555
555
|
REQUIRE_THAT(messages[0].GetPayload(), Catch::Matchers::RangeEquals(Message2Payload));
|
|
556
556
|
}
|
|
557
|
+
|
|
558
|
+
SECTION("account Forward-TSN size in deferred reset")
|
|
559
|
+
{
|
|
560
|
+
RTC::SCTP::ReassemblyQueue reassemblyQueue(BufferLength, /*useMessageInterleaving*/ false);
|
|
561
|
+
|
|
562
|
+
reassemblyQueue.EnterDeferredReset(
|
|
563
|
+
/*senderLastAssignedTsn*/ 12, std::vector<uint16_t>{ /*streamId*/ 1 });
|
|
564
|
+
|
|
565
|
+
REQUIRE(reassemblyQueue.GetQueuedBytes() == 0);
|
|
566
|
+
|
|
567
|
+
reassemblyQueue.HandleForwardTsn(
|
|
568
|
+
13,
|
|
569
|
+
std::vector<RTC::SCTP::AnyForwardTsnChunk::SkippedStream>{
|
|
570
|
+
{ /*unordered*/ false, /*streamId*/ 1, /*mid*/ 0 }
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
REQUIRE(reassemblyQueue.GetQueuedBytes() > 0);
|
|
574
|
+
|
|
575
|
+
// When deferred Forward-TSN are processed, GetQueuedBytes() should return
|
|
576
|
+
// to the earlier value.
|
|
577
|
+
reassemblyQueue.ResetStreamsAndLeaveDeferredReset(std::vector<uint16_t>{ /*streamId*/ 1 });
|
|
578
|
+
|
|
579
|
+
REQUIRE(reassemblyQueue.GetQueuedBytes() == 0);
|
|
580
|
+
}
|
|
557
581
|
}
|