| OLD | NEW |
| (Empty) |
| 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 "webrtc/base/gunit.h" | |
| 30 #include "webrtc/base/messagehandler.h" | |
| 31 #include "webrtc/base/scoped_ptr.h" | |
| 32 #include "webrtc/base/stream.h" | |
| 33 #include "webrtc/base/thread.h" | |
| 34 #include "webrtc/base/timeutils.h" | |
| 35 #include "webrtc/libjingle/session/sessionmanager.h" | |
| 36 #include "webrtc/libjingle/session/tunnel/tunnelsessionclient.h" | |
| 37 #include "webrtc/p2p/base/transport.h" | |
| 38 #include "webrtc/p2p/client/fakeportallocator.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 } | |
| OLD | NEW |