Home | History | Annotate | Download | only in tunnel
      1 /*
      2  * libjingle
      3  * Copyright 2010, Google Inc.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are met:
      7  *
      8  *  1. Redistributions of source code must retain the above copyright notice,
      9  *     this list of conditions and the following disclaimer.
     10  *  2. Redistributions in binary form must reproduce the above copyright notice,
     11  *     this list of conditions and the following disclaimer in the documentation
     12  *     and/or other materials provided with the distribution.
     13  *  3. The name of the author may not be used to endorse or promote products
     14  *     derived from this software without specific prior written permission.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
     17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
     19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
     25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 #include <string>
     29 #include "talk/p2p/base/sessionmanager.h"
     30 #include "talk/p2p/base/transport.h"
     31 #include "talk/p2p/client/fakeportallocator.h"
     32 #include "talk/session/tunnel/tunnelsessionclient.h"
     33 #include "webrtc/base/gunit.h"
     34 #include "webrtc/base/messagehandler.h"
     35 #include "webrtc/base/scoped_ptr.h"
     36 #include "webrtc/base/stream.h"
     37 #include "webrtc/base/thread.h"
     38 #include "webrtc/base/timeutils.h"
     39 
     40 static const int kTimeoutMs = 10000;
     41 static const int kBlockSize = 4096;
     42 static const buzz::Jid kLocalJid("local@localhost");
     43 static const buzz::Jid kRemoteJid("remote@localhost");
     44 
     45 // This test fixture creates the necessary plumbing to create and run
     46 // two TunnelSessionClients that talk to each other.
     47 class TunnelSessionClientTest : public testing::Test,
     48                                 public rtc::MessageHandler,
     49                                 public sigslot::has_slots<> {
     50  public:
     51   TunnelSessionClientTest()
     52       : local_pa_(rtc::Thread::Current(), NULL),
     53         remote_pa_(rtc::Thread::Current(), NULL),
     54         local_sm_(&local_pa_, rtc::Thread::Current()),
     55         remote_sm_(&remote_pa_, rtc::Thread::Current()),
     56         local_client_(kLocalJid, &local_sm_),
     57         remote_client_(kRemoteJid, &remote_sm_),
     58         done_(false) {
     59     local_sm_.SignalRequestSignaling.connect(this,
     60         &TunnelSessionClientTest::OnLocalRequestSignaling);
     61     local_sm_.SignalOutgoingMessage.connect(this,
     62         &TunnelSessionClientTest::OnOutgoingMessage);
     63     remote_sm_.SignalRequestSignaling.connect(this,
     64         &TunnelSessionClientTest::OnRemoteRequestSignaling);
     65     remote_sm_.SignalOutgoingMessage.connect(this,
     66         &TunnelSessionClientTest::OnOutgoingMessage);
     67     remote_client_.SignalIncomingTunnel.connect(this,
     68         &TunnelSessionClientTest::OnIncomingTunnel);
     69   }
     70 
     71   // Transfer the desired amount of data from the local to the remote client.
     72   void TestTransfer(int size) {
     73     // Create some dummy data to send.
     74     send_stream_.ReserveSize(size);
     75     for (int i = 0; i < size; ++i) {
     76       char ch = static_cast<char>(i);
     77       send_stream_.Write(&ch, 1, NULL, NULL);
     78     }
     79     send_stream_.Rewind();
     80     // Prepare the receive stream.
     81     recv_stream_.ReserveSize(size);
     82     // Create the tunnel and set things in motion.
     83     local_tunnel_.reset(local_client_.CreateTunnel(kRemoteJid, "test"));
     84     local_tunnel_->SignalEvent.connect(this,
     85         &TunnelSessionClientTest::OnStreamEvent);
     86     EXPECT_TRUE_WAIT(done_, kTimeoutMs);
     87     // Make sure we received the right data.
     88     EXPECT_EQ(0, memcmp(send_stream_.GetBuffer(),
     89                         recv_stream_.GetBuffer(), size));
     90   }
     91 
     92  private:
     93   enum { MSG_LSIGNAL, MSG_RSIGNAL };
     94 
     95   // There's no SessionManager* argument in this callback, so we need 2 of them.
     96   void OnLocalRequestSignaling() {
     97     local_sm_.OnSignalingReady();
     98   }
     99   void OnRemoteRequestSignaling() {
    100     remote_sm_.OnSignalingReady();
    101   }
    102 
    103   // Post a message, to avoid problems with directly connecting the callbacks.
    104   void OnOutgoingMessage(cricket::SessionManager* manager,
    105                          const buzz::XmlElement* stanza) {
    106     if (manager == &local_sm_) {
    107       rtc::Thread::Current()->Post(this, MSG_LSIGNAL,
    108           rtc::WrapMessageData(*stanza));
    109     } else if (manager == &remote_sm_) {
    110       rtc::Thread::Current()->Post(this, MSG_RSIGNAL,
    111           rtc::WrapMessageData(*stanza));
    112     }
    113   }
    114 
    115   // Need to add a "from=" attribute (normally added by the server)
    116   // Then route the incoming signaling message to the "other" session manager.
    117   virtual void OnMessage(rtc::Message* message) {
    118     rtc::TypedMessageData<buzz::XmlElement>* data =
    119         static_cast<rtc::TypedMessageData<buzz::XmlElement>*>(
    120             message->pdata);
    121     bool response = data->data().Attr(buzz::QN_TYPE) == buzz::STR_RESULT;
    122     if (message->message_id == MSG_RSIGNAL) {
    123       data->data().AddAttr(buzz::QN_FROM, remote_client_.jid().Str());
    124       if (!response) {
    125         local_sm_.OnIncomingMessage(&data->data());
    126       } else {
    127         local_sm_.OnIncomingResponse(NULL, &data->data());
    128       }
    129     } else if (message->message_id == MSG_LSIGNAL) {
    130       data->data().AddAttr(buzz::QN_FROM, local_client_.jid().Str());
    131       if (!response) {
    132         remote_sm_.OnIncomingMessage(&data->data());
    133       } else {
    134         remote_sm_.OnIncomingResponse(NULL, &data->data());
    135       }
    136     }
    137     delete data;
    138   }
    139 
    140   // Accept the tunnel when it arrives and wire up the stream.
    141   void OnIncomingTunnel(cricket::TunnelSessionClient* client,
    142                         buzz::Jid jid, std::string description,
    143                         cricket::Session* session) {
    144     remote_tunnel_.reset(remote_client_.AcceptTunnel(session));
    145     remote_tunnel_->SignalEvent.connect(this,
    146         &TunnelSessionClientTest::OnStreamEvent);
    147   }
    148 
    149   // Send from send_stream_ as long as we're not flow-controlled.
    150   // Read bytes out into recv_stream_ as they arrive.
    151   // End the test when we are notified that the local side has closed the
    152   // tunnel. All data has been read out at this point.
    153   void OnStreamEvent(rtc::StreamInterface* stream, int events,
    154                      int error) {
    155     if (events & rtc::SE_READ) {
    156       if (stream == remote_tunnel_.get()) {
    157         ReadData();
    158       }
    159     }
    160     if (events & rtc::SE_WRITE) {
    161       if (stream == local_tunnel_.get()) {
    162         bool done = false;
    163         WriteData(&done);
    164         if (done) {
    165           local_tunnel_->Close();
    166         }
    167       }
    168     }
    169     if (events & rtc::SE_CLOSE) {
    170       if (stream == remote_tunnel_.get()) {
    171         remote_tunnel_->Close();
    172         done_ = true;
    173       }
    174     }
    175   }
    176 
    177   // Spool from the tunnel into recv_stream.
    178   // Flow() doesn't work here because it won't write if the read blocks.
    179   void ReadData() {
    180     char block[kBlockSize];
    181     size_t read, position;
    182     rtc::StreamResult res;
    183     while ((res = remote_tunnel_->Read(block, sizeof(block), &read, NULL)) ==
    184         rtc::SR_SUCCESS) {
    185       recv_stream_.Write(block, read, NULL, NULL);
    186     }
    187     ASSERT(res != rtc::SR_EOS);
    188     recv_stream_.GetPosition(&position);
    189     LOG(LS_VERBOSE) << "Recv position: " << position;
    190   }
    191   // Spool from send_stream into the tunnel. Back up if we get flow controlled.
    192   void WriteData(bool* done) {
    193     char block[kBlockSize];
    194     size_t leftover = 0, position;
    195     rtc::StreamResult res = rtc::Flow(&send_stream_,
    196         block, sizeof(block), local_tunnel_.get(), &leftover);
    197     if (res == rtc::SR_BLOCK) {
    198       send_stream_.GetPosition(&position);
    199       send_stream_.SetPosition(position - leftover);
    200       LOG(LS_VERBOSE) << "Send position: " << position - leftover;
    201       *done = false;
    202     } else if (res == rtc::SR_SUCCESS) {
    203       *done = true;
    204     } else {
    205       ASSERT(false);  // shouldn't happen
    206     }
    207   }
    208 
    209  private:
    210   cricket::FakePortAllocator local_pa_;
    211   cricket::FakePortAllocator remote_pa_;
    212   cricket::SessionManager local_sm_;
    213   cricket::SessionManager remote_sm_;
    214   cricket::TunnelSessionClient local_client_;
    215   cricket::TunnelSessionClient remote_client_;
    216   rtc::scoped_ptr<rtc::StreamInterface> local_tunnel_;
    217   rtc::scoped_ptr<rtc::StreamInterface> remote_tunnel_;
    218   rtc::MemoryStream send_stream_;
    219   rtc::MemoryStream recv_stream_;
    220   bool done_;
    221 };
    222 
    223 // Test the normal case of sending data from one side to the other.
    224 TEST_F(TunnelSessionClientTest, TestTransfer) {
    225   TestTransfer(1000000);
    226 }
    227