media/mtransport/test/sctp_unittest.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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
     9 #include <iostream>
    10 #include <string>
    11 #include <map>
    13 #include "sigslot.h"
    15 #include "logging.h"
    16 #include "nsNetCID.h"
    17 #include "nsITimer.h"
    18 #include "nsComponentManagerUtils.h"
    19 #include "nsThreadUtils.h"
    20 #include "nsXPCOM.h"
    22 #include "transportflow.h"
    23 #include "transportlayer.h"
    24 #include "transportlayerloopback.h"
    26 #include "mtransport_test_utils.h"
    27 #include "runnable_utils.h"
    28 #include "usrsctp.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;
    41 namespace {
    43 class TransportTestPeer;
    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() {}
    52   NS_DECL_THREADSAFE_ISUPPORTS
    53   NS_DECL_NSITIMERCALLBACK
    55  protected:
    56   TransportTestPeer *peer_;
    57   int to_send_;
    58 };
    60 NS_IMPL_ISUPPORTS(SendPeriodic, nsITimerCallback)
    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;
    78     usrsctp_register_address(static_cast<void *>(this));
    79     int r = usrsctp_set_non_blocking(sctp_, 1);
    80     EXPECT_GE(r, 0);
    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);
    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);
    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);
   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);
   115     nsresult res;
   116     res = loopback_->Init();
   117     EXPECT_EQ((nsresult)NS_OK, res);
   118   }
   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));
   126     test_utils->sts_target()->Dispatch(WrapRunnable(this,
   127                                                    &TransportTestPeer::Disconnect_s),
   128                                       NS_DISPATCH_SYNC);
   130     std::cerr << "~TransportTestPeer() completed" << std::endl;
   131   }
   133   void ConnectSocket(TransportTestPeer *peer) {
   134     test_utils->sts_target()->Dispatch(WrapRunnable(
   135         this, &TransportTestPeer::ConnectSocket_s, peer),
   136                                        NS_DISPATCH_SYNC);
   137   }
   139   void ConnectSocket_s(TransportTestPeer *peer) {
   140     loopback_->Connect(peer->loopback_);
   142     ASSERT_EQ((nsresult)NS_OK, flow_->PushLayer(loopback_));
   144     flow_->SignalPacketReceived.connect(this, &TransportTestPeer::PacketReceived);
   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);
   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   }
   159   void Disconnect_s() {
   160     if (flow_) {
   161       flow_ = nullptr;
   162     }
   163   }
   165   void Disconnect() {
   166     loopback_->Disconnect();
   167   }
   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   }
   176   void SendOne() {
   177     unsigned char buf[100];
   178     memset(buf, sent_ & 0xff, sizeof(buf));
   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;
   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);
   193     ++sent_;
   194   }
   196   int sent() const { return sent_; }
   197   int received() const { return received_; }
   198   bool connected() const { return connected_; }
   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   }
   207   TransportResult SendPacket(const unsigned char* data, size_t len) {
   208     unsigned char *buffer = new unsigned char[len];
   209     memcpy(buffer, data, len);
   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);
   221     return 0;
   222   }
   224   void PacketReceived(TransportFlow * flow, const unsigned char* data,
   225                       size_t len) {
   226     std::cerr << "Received " << len << " bytes" << std::endl;
   228     // Pass the data to SCTP
   230     usrsctp_conninput(static_cast<void *>(this), data, len, 0);
   231   }
   233   // Process SCTP notification
   234   void Notification(union sctp_notification *msg, size_t len) {
   235     ASSERT_EQ(msg->sn_header.sn_length, len);
   237     if (msg->sn_header.sn_type == SCTP_ASSOC_CHANGE) {
   238       struct sctp_assoc_change *change = &msg->sn_assoc_change;
   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   }
   250   void SetConnected(bool state) {
   251     connected_ = state;
   252   }
   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);
   257     peer->SendPacket(static_cast<unsigned char *>(buffer), length);
   259     return 0;
   260   }
   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);
   269     if (flags & MSG_NOTIFICATION) {
   270       union sctp_notification *notif =
   271           static_cast<union sctp_notification *>(data);
   273       me->Notification(notif, datalen);
   274       return 0;
   275     }
   277     me->received_ += datalen;
   279     std::cerr << "receive_cb: sock " << sock << " data " << data << "(" << datalen << ") total received bytes = " << me->received_ << std::endl;
   281     return 0;
   282   }
   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_;
   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 };
   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 }
   311 class TransportTest : public ::testing::Test {
   312  public:
   313   TransportTest() {
   314   }
   316   ~TransportTest() {
   317     if (p1_)
   318       p1_->Disconnect();
   319     if (p2_)
   320       p2_->Disconnect();
   321     delete p1_;
   322     delete p2_;
   323   }
   325   static void debug_printf(const char *format, ...) {
   326     va_list ap;
   328     va_start(ap, format);
   329     vprintf(format, ap);
   330     va_end(ap);
   331   }
   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   }
   343   void SetUp() {
   344   }
   346   void ConnectSocket(int p1port = 0, int p2port = 0) {
   347     if (!p1port)
   348       p1port = port_number++;
   349     if (!p2port)
   350       p2port = port_number++;
   352     p1_ = new TransportTestPeer("P1", p1port, p2port);
   353     p2_ = new TransportTestPeer("P2", p2port, p1port);
   355     p1_->ConnectSocket(p2_);
   356     p2_->ConnectSocket(p1_);
   357     ASSERT_TRUE_WAIT(p1_->connected(), 2000);
   358     ASSERT_TRUE_WAIT(p2_->connected(), 2000);
   359   }
   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   }
   369  protected:
   370   TransportTestPeer *p1_;
   371   TransportTestPeer *p2_;
   372 };
   374 TEST_F(TransportTest, TestConnect) {
   375   ConnectSocket();
   376 }
   378 TEST_F(TransportTest, TestConnectSymmetricalPorts) {
   379   ConnectSocket(5002,5002);
   380 }
   382 TEST_F(TransportTest, TestTransfer) {
   383   ConnectSocket();
   384   TestTransfer(50);
   385 }
   388 }  // end namespace
   390 int main(int argc, char **argv)
   391 {
   392   test_utils = new MtransportTestUtils();
   393   // Start the tests
   394   ::testing::InitGoogleTest(&argc, argv);
   396   for(int i=0; i<argc; i++) {
   397     if (!strcmp(argv[i],"-v")) {
   398       sctp_logging = true;
   399     }
   400   }
   402   int rv = RUN_ALL_TESTS();
   403   delete test_utils;
   404   return rv;
   405 }

mercurial