media/mtransport/test/sctp_unittest.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:e1089caf1359
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 // Original author: ekr@rtfm.com
8
9 #include <iostream>
10 #include <string>
11 #include <map>
12
13 #include "sigslot.h"
14
15 #include "logging.h"
16 #include "nsNetCID.h"
17 #include "nsITimer.h"
18 #include "nsComponentManagerUtils.h"
19 #include "nsThreadUtils.h"
20 #include "nsXPCOM.h"
21
22 #include "transportflow.h"
23 #include "transportlayer.h"
24 #include "transportlayerloopback.h"
25
26 #include "mtransport_test_utils.h"
27 #include "runnable_utils.h"
28 #include "usrsctp.h"
29
30 #define GTEST_HAS_RTTI 0
31 #include "gtest/gtest.h"
32 #include "gtest_utils.h"
33
34
35 using namespace mozilla;
36
37 MtransportTestUtils *test_utils;
38 static bool sctp_logging = false;
39 static int port_number = 5000;
40
41 namespace {
42
43 class TransportTestPeer;
44
45 class SendPeriodic : public nsITimerCallback {
46 public:
47 SendPeriodic(TransportTestPeer *peer, int to_send) :
48 peer_(peer),
49 to_send_(to_send) {}
50 virtual ~SendPeriodic() {}
51
52 NS_DECL_THREADSAFE_ISUPPORTS
53 NS_DECL_NSITIMERCALLBACK
54
55 protected:
56 TransportTestPeer *peer_;
57 int to_send_;
58 };
59
60 NS_IMPL_ISUPPORTS(SendPeriodic, nsITimerCallback)
61
62
63 class TransportTestPeer : public sigslot::has_slots<> {
64 public:
65 TransportTestPeer(std::string name, int local_port, int remote_port)
66 : name_(name), connected_(false),
67 sent_(0), received_(0),
68 flow_(new TransportFlow()),
69 loopback_(new TransportLayerLoopback()),
70 sctp_(usrsctp_socket(AF_CONN, SOCK_STREAM, IPPROTO_SCTP, receive_cb, nullptr, 0, nullptr)),
71 timer_(do_CreateInstance(NS_TIMER_CONTRACTID)),
72 periodic_(nullptr) {
73 std::cerr << "Creating TransportTestPeer; flow=" <<
74 static_cast<void *>(flow_.get()) <<
75 " local=" << local_port <<
76 " remote=" << remote_port << std::endl;
77
78 usrsctp_register_address(static_cast<void *>(this));
79 int r = usrsctp_set_non_blocking(sctp_, 1);
80 EXPECT_GE(r, 0);
81
82 struct linger l;
83 l.l_onoff = 1;
84 l.l_linger = 0;
85 r = usrsctp_setsockopt(sctp_, SOL_SOCKET, SO_LINGER, &l,
86 (socklen_t)sizeof(l));
87 EXPECT_GE(r, 0);
88
89 struct sctp_event subscription;
90 memset(&subscription, 0, sizeof(subscription));
91 subscription.se_assoc_id = SCTP_ALL_ASSOC;
92 subscription.se_on = 1;
93 subscription.se_type = SCTP_ASSOC_CHANGE;
94 r = usrsctp_setsockopt(sctp_, IPPROTO_SCTP, SCTP_EVENT, &subscription,
95 sizeof(subscription));
96 EXPECT_GE(r, 0);
97
98 memset(&local_addr_, 0, sizeof(local_addr_));
99 local_addr_.sconn_family = AF_CONN;
100 #if !defined(__Userspace_os_Linux) && !defined(__Userspace_os_Windows) && !defined(__Userspace_os_Android)
101 local_addr_.sconn_len = sizeof(struct sockaddr_conn);
102 #endif
103 local_addr_.sconn_port = htons(local_port);
104 local_addr_.sconn_addr = static_cast<void *>(this);
105
106
107 memset(&remote_addr_, 0, sizeof(remote_addr_));
108 remote_addr_.sconn_family = AF_CONN;
109 #if !defined(__Userspace_os_Linux) && !defined(__Userspace_os_Windows) && !defined(__Userspace_os_Android)
110 remote_addr_.sconn_len = sizeof(struct sockaddr_conn);
111 #endif
112 remote_addr_.sconn_port = htons(remote_port);
113 remote_addr_.sconn_addr = static_cast<void *>(this);
114
115 nsresult res;
116 res = loopback_->Init();
117 EXPECT_EQ((nsresult)NS_OK, res);
118 }
119
120 ~TransportTestPeer() {
121 std::cerr << "Destroying sctp connection flow=" <<
122 static_cast<void *>(flow_.get()) << std::endl;
123 usrsctp_close(sctp_);
124 usrsctp_deregister_address(static_cast<void *>(this));
125
126 test_utils->sts_target()->Dispatch(WrapRunnable(this,
127 &TransportTestPeer::Disconnect_s),
128 NS_DISPATCH_SYNC);
129
130 std::cerr << "~TransportTestPeer() completed" << std::endl;
131 }
132
133 void ConnectSocket(TransportTestPeer *peer) {
134 test_utils->sts_target()->Dispatch(WrapRunnable(
135 this, &TransportTestPeer::ConnectSocket_s, peer),
136 NS_DISPATCH_SYNC);
137 }
138
139 void ConnectSocket_s(TransportTestPeer *peer) {
140 loopback_->Connect(peer->loopback_);
141
142 ASSERT_EQ((nsresult)NS_OK, flow_->PushLayer(loopback_));
143
144 flow_->SignalPacketReceived.connect(this, &TransportTestPeer::PacketReceived);
145
146 // SCTP here!
147 ASSERT_TRUE(sctp_);
148 std::cerr << "Calling usrsctp_bind()" << std::endl;
149 int r = usrsctp_bind(sctp_, reinterpret_cast<struct sockaddr *>(
150 &local_addr_), sizeof(local_addr_));
151 ASSERT_GE(0, r);
152
153 std::cerr << "Calling usrsctp_connect()" << std::endl;
154 r = usrsctp_connect(sctp_, reinterpret_cast<struct sockaddr *>(
155 &remote_addr_), sizeof(remote_addr_));
156 ASSERT_GE(0, r);
157 }
158
159 void Disconnect_s() {
160 if (flow_) {
161 flow_ = nullptr;
162 }
163 }
164
165 void Disconnect() {
166 loopback_->Disconnect();
167 }
168
169
170 void StartTransfer(size_t to_send) {
171 periodic_ = new SendPeriodic(this, to_send);
172 timer_->SetTarget(test_utils->sts_target());
173 timer_->InitWithCallback(periodic_, 10, nsITimer::TYPE_REPEATING_SLACK);
174 }
175
176 void SendOne() {
177 unsigned char buf[100];
178 memset(buf, sent_ & 0xff, sizeof(buf));
179
180 struct sctp_sndinfo info;
181 info.snd_sid = 1;
182 info.snd_flags = 0;
183 info.snd_ppid = 50; // What the heck is this?
184 info.snd_context = 0;
185 info.snd_assoc_id = 0;
186
187 int r = usrsctp_sendv(sctp_, buf, sizeof(buf), nullptr, 0,
188 static_cast<void *>(&info),
189 sizeof(info), SCTP_SENDV_SNDINFO, 0);
190 ASSERT_TRUE(r >= 0);
191 ASSERT_EQ(sizeof(buf), (size_t)r);
192
193 ++sent_;
194 }
195
196 int sent() const { return sent_; }
197 int received() const { return received_; }
198 bool connected() const { return connected_; }
199
200 static TransportResult SendPacket_s(const unsigned char* data, size_t len,
201 const mozilla::RefPtr<TransportFlow>& flow) {
202 TransportResult res = flow->SendPacket(data, len);
203 delete data; // we always allocate
204 return res;
205 }
206
207 TransportResult SendPacket(const unsigned char* data, size_t len) {
208 unsigned char *buffer = new unsigned char[len];
209 memcpy(buffer, data, len);
210
211 // Uses DISPATCH_NORMAL to avoid possible deadlocks when we're called
212 // from MainThread especially during shutdown (same as DataChannels).
213 // RUN_ON_THREAD short-circuits if already on the STS thread, which is
214 // normal for most transfers outside of connect() and close(). Passes
215 // a refptr to flow_ to avoid any async deletion issues (since we can't
216 // make 'this' into a refptr as it isn't refcounted)
217 RUN_ON_THREAD(test_utils->sts_target(), WrapRunnableNM(
218 &TransportTestPeer::SendPacket_s, buffer, len, flow_),
219 NS_DISPATCH_NORMAL);
220
221 return 0;
222 }
223
224 void PacketReceived(TransportFlow * flow, const unsigned char* data,
225 size_t len) {
226 std::cerr << "Received " << len << " bytes" << std::endl;
227
228 // Pass the data to SCTP
229
230 usrsctp_conninput(static_cast<void *>(this), data, len, 0);
231 }
232
233 // Process SCTP notification
234 void Notification(union sctp_notification *msg, size_t len) {
235 ASSERT_EQ(msg->sn_header.sn_length, len);
236
237 if (msg->sn_header.sn_type == SCTP_ASSOC_CHANGE) {
238 struct sctp_assoc_change *change = &msg->sn_assoc_change;
239
240 if (change->sac_state == SCTP_COMM_UP) {
241 std::cerr << "Connection up" << std::endl;
242 SetConnected(true);
243 } else {
244 std::cerr << "Connection down" << std::endl;
245 SetConnected(false);
246 }
247 }
248 }
249
250 void SetConnected(bool state) {
251 connected_ = state;
252 }
253
254 static int conn_output(void *addr, void *buffer, size_t length, uint8_t tos, uint8_t set_df) {
255 TransportTestPeer *peer = static_cast<TransportTestPeer *>(addr);
256
257 peer->SendPacket(static_cast<unsigned char *>(buffer), length);
258
259 return 0;
260 }
261
262 static int receive_cb(struct socket* sock, union sctp_sockstore addr,
263 void *data, size_t datalen,
264 struct sctp_rcvinfo rcv, int flags, void *ulp_info) {
265 TransportTestPeer *me = static_cast<TransportTestPeer *>(
266 addr.sconn.sconn_addr);
267 MOZ_ASSERT(me);
268
269 if (flags & MSG_NOTIFICATION) {
270 union sctp_notification *notif =
271 static_cast<union sctp_notification *>(data);
272
273 me->Notification(notif, datalen);
274 return 0;
275 }
276
277 me->received_ += datalen;
278
279 std::cerr << "receive_cb: sock " << sock << " data " << data << "(" << datalen << ") total received bytes = " << me->received_ << std::endl;
280
281 return 0;
282 }
283
284
285 private:
286 std::string name_;
287 bool connected_;
288 size_t sent_;
289 size_t received_;
290 mozilla::RefPtr<TransportFlow> flow_;
291 TransportLayerLoopback *loopback_;
292
293 struct sockaddr_conn local_addr_;
294 struct sockaddr_conn remote_addr_;
295 struct socket *sctp_;
296 nsCOMPtr<nsITimer> timer_;
297 nsRefPtr<SendPeriodic> periodic_;
298 };
299
300
301 // Implemented here because it calls a method of TransportTestPeer
302 NS_IMETHODIMP SendPeriodic::Notify(nsITimer *timer) {
303 peer_->SendOne();
304 --to_send_;
305 if (!to_send_) {
306 timer->Cancel();
307 }
308 return NS_OK;
309 }
310
311 class TransportTest : public ::testing::Test {
312 public:
313 TransportTest() {
314 }
315
316 ~TransportTest() {
317 if (p1_)
318 p1_->Disconnect();
319 if (p2_)
320 p2_->Disconnect();
321 delete p1_;
322 delete p2_;
323 }
324
325 static void debug_printf(const char *format, ...) {
326 va_list ap;
327
328 va_start(ap, format);
329 vprintf(format, ap);
330 va_end(ap);
331 }
332
333
334 static void SetUpTestCase() {
335 if (sctp_logging) {
336 usrsctp_init(0, &TransportTestPeer::conn_output, debug_printf);
337 usrsctp_sysctl_set_sctp_debug_on(0xffffffff);
338 } else {
339 usrsctp_init(0, &TransportTestPeer::conn_output, nullptr);
340 }
341 }
342
343 void SetUp() {
344 }
345
346 void ConnectSocket(int p1port = 0, int p2port = 0) {
347 if (!p1port)
348 p1port = port_number++;
349 if (!p2port)
350 p2port = port_number++;
351
352 p1_ = new TransportTestPeer("P1", p1port, p2port);
353 p2_ = new TransportTestPeer("P2", p2port, p1port);
354
355 p1_->ConnectSocket(p2_);
356 p2_->ConnectSocket(p1_);
357 ASSERT_TRUE_WAIT(p1_->connected(), 2000);
358 ASSERT_TRUE_WAIT(p2_->connected(), 2000);
359 }
360
361 void TestTransfer(int expected = 1) {
362 std::cerr << "Starting trasnsfer test" << std::endl;
363 p1_->StartTransfer(expected);
364 ASSERT_TRUE_WAIT(p1_->sent() == expected, 10000);
365 ASSERT_TRUE_WAIT(p2_->received() == (expected * 100), 10000);
366 std::cerr << "P2 received " << p2_->received() << std::endl;
367 }
368
369 protected:
370 TransportTestPeer *p1_;
371 TransportTestPeer *p2_;
372 };
373
374 TEST_F(TransportTest, TestConnect) {
375 ConnectSocket();
376 }
377
378 TEST_F(TransportTest, TestConnectSymmetricalPorts) {
379 ConnectSocket(5002,5002);
380 }
381
382 TEST_F(TransportTest, TestTransfer) {
383 ConnectSocket();
384 TestTransfer(50);
385 }
386
387
388 } // end namespace
389
390 int main(int argc, char **argv)
391 {
392 test_utils = new MtransportTestUtils();
393 // Start the tests
394 ::testing::InitGoogleTest(&argc, argv);
395
396 for(int i=0; i<argc; i++) {
397 if (!strcmp(argv[i],"-v")) {
398 sctp_logging = true;
399 }
400 }
401
402 int rv = RUN_ALL_TESTS();
403 delete test_utils;
404 return rv;
405 }

mercurial