Florent Castelli | e3b74f8 | 2022-05-02 22:24:15 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2022 The WebRTC project authors. All Rights Reserved. |
| 3 | * |
| 4 | * Use of this source code is governed by a BSD-style license |
| 5 | * that can be found in the LICENSE file in the root of the source |
| 6 | * tree. An additional intellectual property rights grant can be found |
| 7 | * in the file PATENTS. All contributing project authors may |
| 8 | * be found in the AUTHORS file in the root of the source tree. |
| 9 | */ |
| 10 | |
| 11 | #include "media/sctp/dcsctp_transport.h" |
| 12 | |
| 13 | #include <memory> |
| 14 | #include <utility> |
| 15 | |
| 16 | #include "net/dcsctp/public/mock_dcsctp_socket.h" |
| 17 | #include "net/dcsctp/public/mock_dcsctp_socket_factory.h" |
| 18 | #include "p2p/base/fake_packet_transport.h" |
| 19 | #include "test/gtest.h" |
| 20 | |
Florent Castelli | dbc2ba2 | 2022-08-22 17:46:39 | [diff] [blame] | 21 | using ::testing::_; |
Florent Castelli | e3b74f8 | 2022-05-02 22:24:15 | [diff] [blame] | 22 | using ::testing::ByMove; |
| 23 | using ::testing::DoAll; |
| 24 | using ::testing::ElementsAre; |
| 25 | using ::testing::InSequence; |
| 26 | using ::testing::Invoke; |
| 27 | using ::testing::NiceMock; |
| 28 | using ::testing::Return; |
Florent Castelli | dbc2ba2 | 2022-08-22 17:46:39 | [diff] [blame] | 29 | using ::testing::ReturnPointee; |
Florent Castelli | e3b74f8 | 2022-05-02 22:24:15 | [diff] [blame] | 30 | |
| 31 | namespace webrtc { |
| 32 | |
| 33 | namespace { |
Florent Castelli | 691d4a0 | 2023-03-13 14:17:17 | [diff] [blame] | 34 | class MockDataChannelSink : public DataChannelSink { |
Florent Castelli | e3b74f8 | 2022-05-02 22:24:15 | [diff] [blame] | 35 | public: |
Fredrik Solenberg | 5cb3a90 | 2022-08-22 09:34:29 | [diff] [blame] | 36 | MOCK_METHOD(void, OnConnected, ()); |
| 37 | |
| 38 | // DataChannelSink |
| 39 | MOCK_METHOD(void, |
| 40 | OnDataReceived, |
| 41 | (int, DataMessageType, const rtc::CopyOnWriteBuffer&)); |
| 42 | MOCK_METHOD(void, OnChannelClosing, (int)); |
| 43 | MOCK_METHOD(void, OnChannelClosed, (int)); |
| 44 | MOCK_METHOD(void, OnReadyToSend, ()); |
| 45 | MOCK_METHOD(void, OnTransportClosed, (RTCError)); |
Florent Castelli | e3b74f8 | 2022-05-02 22:24:15 | [diff] [blame] | 46 | }; |
| 47 | |
Florent Castelli | 691d4a0 | 2023-03-13 14:17:17 | [diff] [blame] | 48 | static_assert(!std::is_abstract_v<MockDataChannelSink>); |
| 49 | |
Florent Castelli | e3b74f8 | 2022-05-02 22:24:15 | [diff] [blame] | 50 | class Peer { |
| 51 | public: |
| 52 | Peer() : fake_packet_transport_("transport"), simulated_clock_(1000) { |
| 53 | auto socket_ptr = std::make_unique<dcsctp::MockDcSctpSocket>(); |
| 54 | socket_ = socket_ptr.get(); |
| 55 | |
| 56 | auto mock_dcsctp_socket_factory = |
| 57 | std::make_unique<dcsctp::MockDcSctpSocketFactory>(); |
| 58 | EXPECT_CALL(*mock_dcsctp_socket_factory, Create) |
| 59 | .Times(1) |
| 60 | .WillOnce(Return(ByMove(std::move(socket_ptr)))); |
| 61 | |
| 62 | sctp_transport_ = std::make_unique<webrtc::DcSctpTransport>( |
| 63 | rtc::Thread::Current(), &fake_packet_transport_, &simulated_clock_, |
| 64 | std::move(mock_dcsctp_socket_factory)); |
Florent Castelli | 691d4a0 | 2023-03-13 14:17:17 | [diff] [blame] | 65 | sctp_transport_->SetDataChannelSink(&sink_); |
| 66 | sctp_transport_->SetOnConnectedCallback([this]() { sink_.OnConnected(); }); |
Florent Castelli | e3b74f8 | 2022-05-02 22:24:15 | [diff] [blame] | 67 | } |
| 68 | |
| 69 | rtc::FakePacketTransport fake_packet_transport_; |
| 70 | webrtc::SimulatedClock simulated_clock_; |
| 71 | dcsctp::MockDcSctpSocket* socket_; |
| 72 | std::unique_ptr<webrtc::DcSctpTransport> sctp_transport_; |
Florent Castelli | 691d4a0 | 2023-03-13 14:17:17 | [diff] [blame] | 73 | NiceMock<MockDataChannelSink> sink_; |
Florent Castelli | e3b74f8 | 2022-05-02 22:24:15 | [diff] [blame] | 74 | }; |
| 75 | } // namespace |
| 76 | |
| 77 | TEST(DcSctpTransportTest, OpenSequence) { |
Niels Möller | 83830f3 | 2022-05-20 07:12:57 | [diff] [blame] | 78 | rtc::AutoThread main_thread; |
Florent Castelli | e3b74f8 | 2022-05-02 22:24:15 | [diff] [blame] | 79 | Peer peer_a; |
| 80 | peer_a.fake_packet_transport_.SetWritable(true); |
| 81 | |
| 82 | EXPECT_CALL(*peer_a.socket_, Connect) |
| 83 | .Times(1) |
| 84 | .WillOnce(Invoke(peer_a.sctp_transport_.get(), |
| 85 | &dcsctp::DcSctpSocketCallbacks::OnConnected)); |
Florent Castelli | 691d4a0 | 2023-03-13 14:17:17 | [diff] [blame] | 86 | EXPECT_CALL(peer_a.sink_, OnReadyToSend); |
| 87 | EXPECT_CALL(peer_a.sink_, OnConnected); |
Florent Castelli | e3b74f8 | 2022-05-02 22:24:15 | [diff] [blame] | 88 | |
| 89 | peer_a.sctp_transport_->Start(5000, 5000, 256 * 1024); |
| 90 | } |
| 91 | |
Florent Castelli | 8f04c7c | 2022-05-05 21:43:44 | [diff] [blame] | 92 | // Tests that the close sequence invoked from one end results in the stream to |
| 93 | // be reset from both ends and all the proper signals are sent. |
Florent Castelli | e3b74f8 | 2022-05-02 22:24:15 | [diff] [blame] | 94 | TEST(DcSctpTransportTest, CloseSequence) { |
Niels Möller | 83830f3 | 2022-05-20 07:12:57 | [diff] [blame] | 95 | rtc::AutoThread main_thread; |
Florent Castelli | e3b74f8 | 2022-05-02 22:24:15 | [diff] [blame] | 96 | Peer peer_a; |
| 97 | Peer peer_b; |
| 98 | peer_a.fake_packet_transport_.SetDestination(&peer_b.fake_packet_transport_, |
| 99 | false); |
| 100 | { |
| 101 | InSequence sequence; |
| 102 | |
| 103 | EXPECT_CALL(*peer_a.socket_, ResetStreams(ElementsAre(dcsctp::StreamID(1)))) |
Florent Castelli | 8f04c7c | 2022-05-05 21:43:44 | [diff] [blame] | 104 | .WillOnce(Return(dcsctp::ResetStreamsStatus::kPerformed)); |
Florent Castelli | e3b74f8 | 2022-05-02 22:24:15 | [diff] [blame] | 105 | |
| 106 | EXPECT_CALL(*peer_b.socket_, ResetStreams(ElementsAre(dcsctp::StreamID(1)))) |
Florent Castelli | 8f04c7c | 2022-05-05 21:43:44 | [diff] [blame] | 107 | .WillOnce(Return(dcsctp::ResetStreamsStatus::kPerformed)); |
Florent Castelli | e3b74f8 | 2022-05-02 22:24:15 | [diff] [blame] | 108 | |
Florent Castelli | 691d4a0 | 2023-03-13 14:17:17 | [diff] [blame] | 109 | EXPECT_CALL(peer_a.sink_, OnChannelClosing(1)).Times(0); |
| 110 | EXPECT_CALL(peer_b.sink_, OnChannelClosing(1)); |
| 111 | EXPECT_CALL(peer_a.sink_, OnChannelClosed(1)); |
| 112 | EXPECT_CALL(peer_b.sink_, OnChannelClosed(1)); |
Florent Castelli | e3b74f8 | 2022-05-02 22:24:15 | [diff] [blame] | 113 | } |
| 114 | |
| 115 | peer_a.sctp_transport_->Start(5000, 5000, 256 * 1024); |
| 116 | peer_b.sctp_transport_->Start(5000, 5000, 256 * 1024); |
| 117 | peer_a.sctp_transport_->OpenStream(1); |
Florent Castelli | dbc2ba2 | 2022-08-22 17:46:39 | [diff] [blame] | 118 | peer_b.sctp_transport_->OpenStream(1); |
Florent Castelli | e3b74f8 | 2022-05-02 22:24:15 | [diff] [blame] | 119 | peer_a.sctp_transport_->ResetStream(1); |
Florent Castelli | 8f04c7c | 2022-05-05 21:43:44 | [diff] [blame] | 120 | |
| 121 | // Simulate the callbacks from the stream resets |
| 122 | dcsctp::StreamID streams[1] = {dcsctp::StreamID(1)}; |
| 123 | static_cast<dcsctp::DcSctpSocketCallbacks*>(peer_a.sctp_transport_.get()) |
| 124 | ->OnStreamsResetPerformed(streams); |
| 125 | static_cast<dcsctp::DcSctpSocketCallbacks*>(peer_b.sctp_transport_.get()) |
| 126 | ->OnIncomingStreamsReset(streams); |
| 127 | static_cast<dcsctp::DcSctpSocketCallbacks*>(peer_a.sctp_transport_.get()) |
| 128 | ->OnIncomingStreamsReset(streams); |
| 129 | static_cast<dcsctp::DcSctpSocketCallbacks*>(peer_b.sctp_transport_.get()) |
| 130 | ->OnStreamsResetPerformed(streams); |
| 131 | } |
| 132 | |
| 133 | // Tests that the close sequence initiated from both peers at the same time |
| 134 | // terminates properly. Both peers will think they initiated it, so no |
Fredrik Solenberg | 5cb3a90 | 2022-08-22 09:34:29 | [diff] [blame] | 135 | // OnClosingProcedureStartedRemotely should be called. |
Florent Castelli | 8f04c7c | 2022-05-05 21:43:44 | [diff] [blame] | 136 | TEST(DcSctpTransportTest, CloseSequenceSimultaneous) { |
Niels Möller | 83830f3 | 2022-05-20 07:12:57 | [diff] [blame] | 137 | rtc::AutoThread main_thread; |
Florent Castelli | 8f04c7c | 2022-05-05 21:43:44 | [diff] [blame] | 138 | Peer peer_a; |
| 139 | Peer peer_b; |
| 140 | peer_a.fake_packet_transport_.SetDestination(&peer_b.fake_packet_transport_, |
| 141 | false); |
| 142 | { |
| 143 | InSequence sequence; |
| 144 | |
| 145 | EXPECT_CALL(*peer_a.socket_, ResetStreams(ElementsAre(dcsctp::StreamID(1)))) |
| 146 | .WillOnce(Return(dcsctp::ResetStreamsStatus::kPerformed)); |
| 147 | |
| 148 | EXPECT_CALL(*peer_b.socket_, ResetStreams(ElementsAre(dcsctp::StreamID(1)))) |
| 149 | .WillOnce(Return(dcsctp::ResetStreamsStatus::kPerformed)); |
| 150 | |
Florent Castelli | 691d4a0 | 2023-03-13 14:17:17 | [diff] [blame] | 151 | EXPECT_CALL(peer_a.sink_, OnChannelClosing(1)).Times(0); |
| 152 | EXPECT_CALL(peer_b.sink_, OnChannelClosing(1)).Times(0); |
| 153 | EXPECT_CALL(peer_a.sink_, OnChannelClosed(1)); |
| 154 | EXPECT_CALL(peer_b.sink_, OnChannelClosed(1)); |
Florent Castelli | 8f04c7c | 2022-05-05 21:43:44 | [diff] [blame] | 155 | } |
| 156 | |
| 157 | peer_a.sctp_transport_->Start(5000, 5000, 256 * 1024); |
| 158 | peer_b.sctp_transport_->Start(5000, 5000, 256 * 1024); |
| 159 | peer_a.sctp_transport_->OpenStream(1); |
Florent Castelli | dbc2ba2 | 2022-08-22 17:46:39 | [diff] [blame] | 160 | peer_b.sctp_transport_->OpenStream(1); |
Florent Castelli | 8f04c7c | 2022-05-05 21:43:44 | [diff] [blame] | 161 | peer_a.sctp_transport_->ResetStream(1); |
| 162 | peer_b.sctp_transport_->ResetStream(1); |
| 163 | |
| 164 | // Simulate the callbacks from the stream resets |
| 165 | dcsctp::StreamID streams[1] = {dcsctp::StreamID(1)}; |
| 166 | static_cast<dcsctp::DcSctpSocketCallbacks*>(peer_a.sctp_transport_.get()) |
| 167 | ->OnStreamsResetPerformed(streams); |
| 168 | static_cast<dcsctp::DcSctpSocketCallbacks*>(peer_b.sctp_transport_.get()) |
| 169 | ->OnStreamsResetPerformed(streams); |
| 170 | static_cast<dcsctp::DcSctpSocketCallbacks*>(peer_a.sctp_transport_.get()) |
| 171 | ->OnIncomingStreamsReset(streams); |
| 172 | static_cast<dcsctp::DcSctpSocketCallbacks*>(peer_b.sctp_transport_.get()) |
| 173 | ->OnIncomingStreamsReset(streams); |
Florent Castelli | e3b74f8 | 2022-05-02 22:24:15 | [diff] [blame] | 174 | } |
| 175 | |
Florent Castelli | dbc2ba2 | 2022-08-22 17:46:39 | [diff] [blame] | 176 | TEST(DcSctpTransportTest, DiscardMessageClosedChannel) { |
| 177 | rtc::AutoThread main_thread; |
| 178 | Peer peer_a; |
| 179 | |
| 180 | EXPECT_CALL(*peer_a.socket_, Send(_, _)).Times(0); |
| 181 | |
| 182 | peer_a.sctp_transport_->Start(5000, 5000, 256 * 1024); |
| 183 | |
Florent Castelli | dbc2ba2 | 2022-08-22 17:46:39 | [diff] [blame] | 184 | SendDataParams params; |
| 185 | rtc::CopyOnWriteBuffer payload; |
Tommi | 1fabbac | 2023-03-21 13:48:51 | [diff] [blame] | 186 | EXPECT_EQ(peer_a.sctp_transport_->SendData(1, params, payload).type(), |
| 187 | RTCErrorType::INVALID_STATE); |
Florent Castelli | dbc2ba2 | 2022-08-22 17:46:39 | [diff] [blame] | 188 | } |
| 189 | |
| 190 | TEST(DcSctpTransportTest, DiscardMessageClosingChannel) { |
| 191 | rtc::AutoThread main_thread; |
| 192 | Peer peer_a; |
| 193 | |
| 194 | EXPECT_CALL(*peer_a.socket_, Send(_, _)).Times(0); |
| 195 | |
| 196 | peer_a.sctp_transport_->OpenStream(1); |
| 197 | peer_a.sctp_transport_->Start(5000, 5000, 256 * 1024); |
| 198 | peer_a.sctp_transport_->ResetStream(1); |
| 199 | |
Florent Castelli | dbc2ba2 | 2022-08-22 17:46:39 | [diff] [blame] | 200 | SendDataParams params; |
| 201 | rtc::CopyOnWriteBuffer payload; |
Tommi | 1fabbac | 2023-03-21 13:48:51 | [diff] [blame] | 202 | EXPECT_EQ(peer_a.sctp_transport_->SendData(1, params, payload).type(), |
| 203 | RTCErrorType::INVALID_STATE); |
Florent Castelli | dbc2ba2 | 2022-08-22 17:46:39 | [diff] [blame] | 204 | } |
| 205 | |
| 206 | TEST(DcSctpTransportTest, SendDataOpenChannel) { |
| 207 | rtc::AutoThread main_thread; |
| 208 | Peer peer_a; |
| 209 | dcsctp::DcSctpOptions options; |
| 210 | |
| 211 | EXPECT_CALL(*peer_a.socket_, Send(_, _)).Times(1); |
| 212 | EXPECT_CALL(*peer_a.socket_, options()).WillOnce(ReturnPointee(&options)); |
| 213 | |
| 214 | peer_a.sctp_transport_->OpenStream(1); |
| 215 | peer_a.sctp_transport_->Start(5000, 5000, 256 * 1024); |
| 216 | |
Florent Castelli | dbc2ba2 | 2022-08-22 17:46:39 | [diff] [blame] | 217 | SendDataParams params; |
| 218 | rtc::CopyOnWriteBuffer payload; |
Tommi | 1fabbac | 2023-03-21 13:48:51 | [diff] [blame] | 219 | EXPECT_TRUE(peer_a.sctp_transport_->SendData(1, params, payload).ok()); |
Florent Castelli | dbc2ba2 | 2022-08-22 17:46:39 | [diff] [blame] | 220 | } |
| 221 | |
Florent Castelli | 691d4a0 | 2023-03-13 14:17:17 | [diff] [blame] | 222 | TEST(DcSctpTransportTest, DeliversMessage) { |
| 223 | rtc::AutoThread main_thread; |
| 224 | Peer peer_a; |
| 225 | |
| 226 | EXPECT_CALL(peer_a.sink_, |
| 227 | OnDataReceived(1, webrtc::DataMessageType::kBinary, _)) |
| 228 | .Times(1); |
| 229 | |
| 230 | peer_a.sctp_transport_->OpenStream(1); |
| 231 | peer_a.sctp_transport_->Start(5000, 5000, 256 * 1024); |
| 232 | |
| 233 | static_cast<dcsctp::DcSctpSocketCallbacks*>(peer_a.sctp_transport_.get()) |
| 234 | ->OnMessageReceived( |
| 235 | dcsctp::DcSctpMessage(dcsctp::StreamID(1), dcsctp::PPID(53), {0})); |
| 236 | } |
| 237 | |
| 238 | TEST(DcSctpTransportTest, DropMessageWithUnknownPpid) { |
| 239 | rtc::AutoThread main_thread; |
| 240 | Peer peer_a; |
| 241 | |
| 242 | EXPECT_CALL(peer_a.sink_, OnDataReceived(_, _, _)).Times(0); |
| 243 | |
| 244 | peer_a.sctp_transport_->OpenStream(1); |
| 245 | peer_a.sctp_transport_->Start(5000, 5000, 256 * 1024); |
| 246 | |
| 247 | static_cast<dcsctp::DcSctpSocketCallbacks*>(peer_a.sctp_transport_.get()) |
| 248 | ->OnMessageReceived( |
| 249 | dcsctp::DcSctpMessage(dcsctp::StreamID(1), dcsctp::PPID(1337), {0})); |
| 250 | } |
Florent Castelli | e3b74f8 | 2022-05-02 22:24:15 | [diff] [blame] | 251 | } // namespace webrtc |