1 /* 2 * libjingle 3 * Copyright 2004 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 "talk/base/gunit.h" 29 #include "talk/base/helpers.h" 30 #include "talk/base/logging.h" 31 #include "talk/base/timeutils.h" 32 #include "talk/p2p/base/stunrequest.h" 33 34 using namespace cricket; 35 36 class StunRequestTest : public testing::Test, 37 public sigslot::has_slots<> { 38 public: 39 static void SetUpTestCase() { 40 talk_base::InitRandom(NULL, 0); 41 } 42 StunRequestTest() 43 : manager_(talk_base::Thread::Current()), 44 request_count_(0), response_(NULL), 45 success_(false), failure_(false), timeout_(false) { 46 manager_.SignalSendPacket.connect(this, &StunRequestTest::OnSendPacket); 47 } 48 49 void OnSendPacket(const void* data, size_t size, StunRequest* req) { 50 request_count_++; 51 } 52 53 void OnResponse(StunMessage* res) { 54 response_ = res; 55 success_ = true; 56 } 57 void OnErrorResponse(StunMessage* res) { 58 response_ = res; 59 failure_ = true; 60 } 61 void OnTimeout() { 62 timeout_ = true; 63 } 64 65 protected: 66 static StunMessage* CreateStunMessage(StunMessageType type, 67 StunMessage* req) { 68 StunMessage* msg = new StunMessage(); 69 msg->SetType(type); 70 if (req) { 71 msg->SetTransactionID(req->transaction_id()); 72 } 73 return msg; 74 } 75 static int TotalDelay(int sends) { 76 int total = 0; 77 for (int i = 0; i < sends; i++) { 78 if (i < 4) 79 total += 100 << i; 80 else 81 total += 1600; 82 } 83 return total; 84 } 85 86 StunRequestManager manager_; 87 int request_count_; 88 StunMessage* response_; 89 bool success_; 90 bool failure_; 91 bool timeout_; 92 }; 93 94 // Forwards results to the test class. 95 class StunRequestThunker : public StunRequest { 96 public: 97 StunRequestThunker(StunMessage* msg, StunRequestTest* test) 98 : StunRequest(msg), test_(test) {} 99 explicit StunRequestThunker(StunRequestTest* test) : test_(test) {} 100 private: 101 virtual void OnResponse(StunMessage* res) { 102 test_->OnResponse(res); 103 } 104 virtual void OnErrorResponse(StunMessage* res) { 105 test_->OnErrorResponse(res); 106 } 107 virtual void OnTimeout() { 108 test_->OnTimeout(); 109 } 110 111 virtual void Prepare(StunMessage* request) { 112 request->SetType(STUN_BINDING_REQUEST); 113 } 114 115 StunRequestTest* test_; 116 }; 117 118 // Test handling of a normal binding response. 119 TEST_F(StunRequestTest, TestSuccess) { 120 StunMessage* req = CreateStunMessage(STUN_BINDING_REQUEST, NULL); 121 122 manager_.Send(new StunRequestThunker(req, this)); 123 StunMessage* res = CreateStunMessage(STUN_BINDING_RESPONSE, req); 124 EXPECT_TRUE(manager_.CheckResponse(res)); 125 126 EXPECT_TRUE(response_ == res); 127 EXPECT_TRUE(success_); 128 EXPECT_FALSE(failure_); 129 EXPECT_FALSE(timeout_); 130 delete res; 131 } 132 133 // Test handling of an error binding response. 134 TEST_F(StunRequestTest, TestError) { 135 StunMessage* req = CreateStunMessage(STUN_BINDING_REQUEST, NULL); 136 137 manager_.Send(new StunRequestThunker(req, this)); 138 StunMessage* res = CreateStunMessage(STUN_BINDING_ERROR_RESPONSE, req); 139 EXPECT_TRUE(manager_.CheckResponse(res)); 140 141 EXPECT_TRUE(response_ == res); 142 EXPECT_FALSE(success_); 143 EXPECT_TRUE(failure_); 144 EXPECT_FALSE(timeout_); 145 delete res; 146 } 147 148 // Test handling of a binding response with the wrong transaction id. 149 TEST_F(StunRequestTest, TestUnexpected) { 150 StunMessage* req = CreateStunMessage(STUN_BINDING_REQUEST, NULL); 151 152 manager_.Send(new StunRequestThunker(req, this)); 153 StunMessage* res = CreateStunMessage(STUN_BINDING_RESPONSE, NULL); 154 EXPECT_FALSE(manager_.CheckResponse(res)); 155 156 EXPECT_TRUE(response_ == NULL); 157 EXPECT_FALSE(success_); 158 EXPECT_FALSE(failure_); 159 EXPECT_FALSE(timeout_); 160 delete res; 161 } 162 163 // Test that requests are sent at the right times, and that the 9th request 164 // (sent at 7900 ms) can be properly replied to. 165 TEST_F(StunRequestTest, TestBackoff) { 166 StunMessage* req = CreateStunMessage(STUN_BINDING_REQUEST, NULL); 167 168 uint32 start = talk_base::Time(); 169 manager_.Send(new StunRequestThunker(req, this)); 170 StunMessage* res = CreateStunMessage(STUN_BINDING_RESPONSE, req); 171 for (int i = 0; i < 9; ++i) { 172 while (request_count_ == i) 173 talk_base::Thread::Current()->ProcessMessages(1); 174 int32 elapsed = talk_base::TimeSince(start); 175 LOG(LS_INFO) << "STUN request #" << (i + 1) 176 << " sent at " << elapsed << " ms"; 177 EXPECT_GE(TotalDelay(i + 1), elapsed); 178 } 179 EXPECT_TRUE(manager_.CheckResponse(res)); 180 181 EXPECT_TRUE(response_ == res); 182 EXPECT_TRUE(success_); 183 EXPECT_FALSE(failure_); 184 EXPECT_FALSE(timeout_); 185 delete res; 186 } 187 188 // Test that we timeout properly if no response is received in 9500 ms. 189 TEST_F(StunRequestTest, TestTimeout) { 190 StunMessage* req = CreateStunMessage(STUN_BINDING_REQUEST, NULL); 191 StunMessage* res = CreateStunMessage(STUN_BINDING_RESPONSE, req); 192 193 manager_.Send(new StunRequestThunker(req, this)); 194 talk_base::Thread::Current()->ProcessMessages(10000); // > STUN timeout 195 EXPECT_FALSE(manager_.CheckResponse(res)); 196 197 EXPECT_TRUE(response_ == NULL); 198 EXPECT_FALSE(success_); 199 EXPECT_FALSE(failure_); 200 EXPECT_TRUE(timeout_); 201 delete res; 202 } 203 204 // Regression test for specific crash where we receive a response with the 205 // same id as a request that doesn't have an underlying StunMessage yet. 206 TEST_F(StunRequestTest, TestNoEmptyRequest) { 207 StunRequestThunker* request = new StunRequestThunker(this); 208 209 manager_.SendDelayed(request, 100); 210 211 StunMessage dummy_req; 212 dummy_req.SetTransactionID(request->id()); 213 StunMessage* res = CreateStunMessage(STUN_BINDING_RESPONSE, &dummy_req); 214 215 EXPECT_TRUE(manager_.CheckResponse(res)); 216 217 EXPECT_TRUE(response_ == res); 218 EXPECT_TRUE(success_); 219 EXPECT_FALSE(failure_); 220 EXPECT_FALSE(timeout_); 221 delete res; 222 } 223