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/. */
7 // Original author: ekr@rtfm.com
18 #include "nsComponentManagerUtils.h"
19 #include "nsThreadUtils.h"
22 #include "transportflow.h"
23 #include "transportlayer.h"
24 #include "transportlayerloopback.h"
26 #include "mtransport_test_utils.h"
27 #include "runnable_utils.h"
30 #define GTEST_HAS_RTTI 0
31 #include "gtest/gtest.h"
32 #include "gtest_utils.h"
35 using namespace mozilla
;
37 MtransportTestUtils
*test_utils
;
38 static bool sctp_logging
= false;
39 static int port_number
= 5000;
43 class TransportTestPeer
;
45 class SendPeriodic
: public nsITimerCallback
{
47 SendPeriodic(TransportTestPeer
*peer
, int to_send
) :
51 NS_DECL_THREADSAFE_ISUPPORTS
52 NS_DECL_NSITIMERCALLBACK
55 virtual ~SendPeriodic() {}
57 TransportTestPeer
*peer_
;
61 NS_IMPL_ISUPPORTS(SendPeriodic
, nsITimerCallback
)
64 class TransportTestPeer
: public sigslot::has_slots
<> {
66 TransportTestPeer(std::string name
, int local_port
, int remote_port
)
67 : name_(name
), connected_(false),
68 sent_(0), received_(0),
69 flow_(new TransportFlow()),
70 loopback_(new TransportLayerLoopback()),
71 sctp_(usrsctp_socket(AF_CONN
, SOCK_STREAM
, IPPROTO_SCTP
, receive_cb
, nullptr, 0, nullptr)),
72 timer_(do_CreateInstance(NS_TIMER_CONTRACTID
)),
74 std::cerr
<< "Creating TransportTestPeer; flow=" <<
75 static_cast<void *>(flow_
.get()) <<
76 " local=" << local_port
<<
77 " remote=" << remote_port
<< std::endl
;
79 usrsctp_register_address(static_cast<void *>(this));
80 int r
= usrsctp_set_non_blocking(sctp_
, 1);
86 r
= usrsctp_setsockopt(sctp_
, SOL_SOCKET
, SO_LINGER
, &l
,
87 (socklen_t
)sizeof(l
));
90 struct sctp_event subscription
;
91 memset(&subscription
, 0, sizeof(subscription
));
92 subscription
.se_assoc_id
= SCTP_ALL_ASSOC
;
93 subscription
.se_on
= 1;
94 subscription
.se_type
= SCTP_ASSOC_CHANGE
;
95 r
= usrsctp_setsockopt(sctp_
, IPPROTO_SCTP
, SCTP_EVENT
, &subscription
,
96 sizeof(subscription
));
99 memset(&local_addr_
, 0, sizeof(local_addr_
));
100 local_addr_
.sconn_family
= AF_CONN
;
101 #if !defined(__Userspace_os_Linux) && !defined(__Userspace_os_Windows) && !defined(__Userspace_os_Android)
102 local_addr_
.sconn_len
= sizeof(struct sockaddr_conn
);
104 local_addr_
.sconn_port
= htons(local_port
);
105 local_addr_
.sconn_addr
= static_cast<void *>(this);
108 memset(&remote_addr_
, 0, sizeof(remote_addr_
));
109 remote_addr_
.sconn_family
= AF_CONN
;
110 #if !defined(__Userspace_os_Linux) && !defined(__Userspace_os_Windows) && !defined(__Userspace_os_Android)
111 remote_addr_
.sconn_len
= sizeof(struct sockaddr_conn
);
113 remote_addr_
.sconn_port
= htons(remote_port
);
114 remote_addr_
.sconn_addr
= static_cast<void *>(this);
117 res
= loopback_
->Init();
118 EXPECT_EQ((nsresult
)NS_OK
, res
);
121 ~TransportTestPeer() {
122 std::cerr
<< "Destroying sctp connection flow=" <<
123 static_cast<void *>(flow_
.get()) << std::endl
;
124 usrsctp_close(sctp_
);
125 usrsctp_deregister_address(static_cast<void *>(this));
127 test_utils
->sts_target()->Dispatch(WrapRunnable(this,
128 &TransportTestPeer::Disconnect_s
),
131 std::cerr
<< "~TransportTestPeer() completed" << std::endl
;
134 void ConnectSocket(TransportTestPeer
*peer
) {
135 test_utils
->sts_target()->Dispatch(WrapRunnable(
136 this, &TransportTestPeer::ConnectSocket_s
, peer
),
140 void ConnectSocket_s(TransportTestPeer
*peer
) {
141 loopback_
->Connect(peer
->loopback_
);
143 ASSERT_EQ((nsresult
)NS_OK
, flow_
->PushLayer(loopback_
));
145 flow_
->SignalPacketReceived
.connect(this, &TransportTestPeer::PacketReceived
);
149 std::cerr
<< "Calling usrsctp_bind()" << std::endl
;
150 int r
= usrsctp_bind(sctp_
, reinterpret_cast<struct sockaddr
*>(
151 &local_addr_
), sizeof(local_addr_
));
154 std::cerr
<< "Calling usrsctp_connect()" << std::endl
;
155 r
= usrsctp_connect(sctp_
, reinterpret_cast<struct sockaddr
*>(
156 &remote_addr_
), sizeof(remote_addr_
));
160 void Disconnect_s() {
167 loopback_
->Disconnect();
171 void StartTransfer(size_t to_send
) {
172 periodic_
= new SendPeriodic(this, to_send
);
173 timer_
->SetTarget(test_utils
->sts_target());
174 timer_
->InitWithCallback(periodic_
, 10, nsITimer::TYPE_REPEATING_SLACK
);
178 unsigned char buf
[100];
179 memset(buf
, sent_
& 0xff, sizeof(buf
));
181 struct sctp_sndinfo info
;
184 info
.snd_ppid
= 50; // What the heck is this?
185 info
.snd_context
= 0;
186 info
.snd_assoc_id
= 0;
188 int r
= usrsctp_sendv(sctp_
, buf
, sizeof(buf
), nullptr, 0,
189 static_cast<void *>(&info
),
190 sizeof(info
), SCTP_SENDV_SNDINFO
, 0);
192 ASSERT_EQ(sizeof(buf
), (size_t)r
);
197 int sent() const { return sent_
; }
198 int received() const { return received_
; }
199 bool connected() const { return connected_
; }
201 static TransportResult
SendPacket_s(const unsigned char* data
, size_t len
,
202 const mozilla::RefPtr
<TransportFlow
>& flow
) {
203 TransportResult res
= flow
->SendPacket(data
, len
);
204 delete data
; // we always allocate
208 TransportResult
SendPacket(const unsigned char* data
, size_t len
) {
209 unsigned char *buffer
= new unsigned char[len
];
210 memcpy(buffer
, data
, len
);
212 // Uses DISPATCH_NORMAL to avoid possible deadlocks when we're called
213 // from MainThread especially during shutdown (same as DataChannels).
214 // RUN_ON_THREAD short-circuits if already on the STS thread, which is
215 // normal for most transfers outside of connect() and close(). Passes
216 // a refptr to flow_ to avoid any async deletion issues (since we can't
217 // make 'this' into a refptr as it isn't refcounted)
218 RUN_ON_THREAD(test_utils
->sts_target(), WrapRunnableNM(
219 &TransportTestPeer::SendPacket_s
, buffer
, len
, flow_
),
225 void PacketReceived(TransportFlow
* flow
, const unsigned char* data
,
227 std::cerr
<< "Received " << len
<< " bytes" << std::endl
;
229 // Pass the data to SCTP
231 usrsctp_conninput(static_cast<void *>(this), data
, len
, 0);
234 // Process SCTP notification
235 void Notification(union sctp_notification
*msg
, size_t len
) {
236 ASSERT_EQ(msg
->sn_header
.sn_length
, len
);
238 if (msg
->sn_header
.sn_type
== SCTP_ASSOC_CHANGE
) {
239 struct sctp_assoc_change
*change
= &msg
->sn_assoc_change
;
241 if (change
->sac_state
== SCTP_COMM_UP
) {
242 std::cerr
<< "Connection up" << std::endl
;
245 std::cerr
<< "Connection down" << std::endl
;
251 void SetConnected(bool state
) {
255 static int conn_output(void *addr
, void *buffer
, size_t length
, uint8_t tos
, uint8_t set_df
) {
256 TransportTestPeer
*peer
= static_cast<TransportTestPeer
*>(addr
);
258 peer
->SendPacket(static_cast<unsigned char *>(buffer
), length
);
263 static int receive_cb(struct socket
* sock
, union sctp_sockstore addr
,
264 void *data
, size_t datalen
,
265 struct sctp_rcvinfo rcv
, int flags
, void *ulp_info
) {
266 TransportTestPeer
*me
= static_cast<TransportTestPeer
*>(
267 addr
.sconn
.sconn_addr
);
270 if (flags
& MSG_NOTIFICATION
) {
271 union sctp_notification
*notif
=
272 static_cast<union sctp_notification
*>(data
);
274 me
->Notification(notif
, datalen
);
278 me
->received_
+= datalen
;
280 std::cerr
<< "receive_cb: sock " << sock
<< " data " << data
<< "(" << datalen
<< ") total received bytes = " << me
->received_
<< std::endl
;
291 mozilla::RefPtr
<TransportFlow
> flow_
;
292 TransportLayerLoopback
*loopback_
;
294 struct sockaddr_conn local_addr_
;
295 struct sockaddr_conn remote_addr_
;
296 struct socket
*sctp_
;
297 nsCOMPtr
<nsITimer
> timer_
;
298 nsRefPtr
<SendPeriodic
> periodic_
;
302 // Implemented here because it calls a method of TransportTestPeer
303 NS_IMETHODIMP
SendPeriodic::Notify(nsITimer
*timer
) {
312 class TransportTest
: public ::testing::Test
{
326 static void debug_printf(const char *format
, ...) {
329 va_start(ap
, format
);
335 static void SetUpTestCase() {
337 usrsctp_init(0, &TransportTestPeer::conn_output
, debug_printf
);
338 usrsctp_sysctl_set_sctp_debug_on(0xffffffff);
340 usrsctp_init(0, &TransportTestPeer::conn_output
, nullptr);
347 void ConnectSocket(int p1port
= 0, int p2port
= 0) {
349 p1port
= port_number
++;
351 p2port
= port_number
++;
353 p1_
= new TransportTestPeer("P1", p1port
, p2port
);
354 p2_
= new TransportTestPeer("P2", p2port
, p1port
);
356 p1_
->ConnectSocket(p2_
);
357 p2_
->ConnectSocket(p1_
);
358 ASSERT_TRUE_WAIT(p1_
->connected(), 2000);
359 ASSERT_TRUE_WAIT(p2_
->connected(), 2000);
362 void TestTransfer(int expected
= 1) {
363 std::cerr
<< "Starting trasnsfer test" << std::endl
;
364 p1_
->StartTransfer(expected
);
365 ASSERT_TRUE_WAIT(p1_
->sent() == expected
, 10000);
366 ASSERT_TRUE_WAIT(p2_
->received() == (expected
* 100), 10000);
367 std::cerr
<< "P2 received " << p2_
->received() << std::endl
;
371 TransportTestPeer
*p1_
;
372 TransportTestPeer
*p2_
;
375 TEST_F(TransportTest
, TestConnect
) {
379 TEST_F(TransportTest
, TestConnectSymmetricalPorts
) {
380 ConnectSocket(5002,5002);
383 TEST_F(TransportTest
, TestTransfer
) {
391 int main(int argc
, char **argv
)
393 test_utils
= new MtransportTestUtils();
395 ::testing::InitGoogleTest(&argc
, argv
);
397 for(int i
=0; i
<argc
; i
++) {
398 if (!strcmp(argv
[i
],"-v")) {
403 int rv
= RUN_ALL_TESTS();