blob: 47583b90e8e9072b36a18a3ecfd4059303cb2dda [file] [log] [blame]
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +00001/*
2 * Copyright 2004 The WebRTC Project Authors. All rights reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "webrtc/p2p/base/stunrequest.h"
skvladb9d8d102016-09-06 17:17:17 -070012#include "webrtc/base/fakeclock.h"
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000013#include "webrtc/base/gunit.h"
14#include "webrtc/base/helpers.h"
15#include "webrtc/base/logging.h"
16#include "webrtc/base/ssladapter.h"
17#include "webrtc/base/timeutils.h"
18
19using namespace cricket;
20
21class StunRequestTest : public testing::Test,
22 public sigslot::has_slots<> {
23 public:
24 StunRequestTest()
25 : manager_(rtc::Thread::Current()),
26 request_count_(0), response_(NULL),
27 success_(false), failure_(false), timeout_(false) {
28 manager_.SignalSendPacket.connect(this, &StunRequestTest::OnSendPacket);
29 }
30
31 void OnSendPacket(const void* data, size_t size, StunRequest* req) {
32 request_count_++;
33 }
34
35 void OnResponse(StunMessage* res) {
36 response_ = res;
37 success_ = true;
38 }
39 void OnErrorResponse(StunMessage* res) {
40 response_ = res;
41 failure_ = true;
42 }
43 void OnTimeout() {
44 timeout_ = true;
45 }
46
47 protected:
48 static StunMessage* CreateStunMessage(StunMessageType type,
49 StunMessage* req) {
50 StunMessage* msg = new StunMessage();
51 msg->SetType(type);
52 if (req) {
53 msg->SetTransactionID(req->transaction_id());
54 }
55 return msg;
56 }
57 static int TotalDelay(int sends) {
58 int total = 0;
59 for (int i = 0; i < sends; i++) {
60 if (i < 4)
61 total += 100 << i;
62 else
63 total += 1600;
64 }
65 return total;
66 }
67
68 StunRequestManager manager_;
69 int request_count_;
70 StunMessage* response_;
71 bool success_;
72 bool failure_;
73 bool timeout_;
74};
75
76// Forwards results to the test class.
77class StunRequestThunker : public StunRequest {
78 public:
79 StunRequestThunker(StunMessage* msg, StunRequestTest* test)
80 : StunRequest(msg), test_(test) {}
81 explicit StunRequestThunker(StunRequestTest* test) : test_(test) {}
82 private:
83 virtual void OnResponse(StunMessage* res) {
84 test_->OnResponse(res);
85 }
86 virtual void OnErrorResponse(StunMessage* res) {
87 test_->OnErrorResponse(res);
88 }
89 virtual void OnTimeout() {
90 test_->OnTimeout();
91 }
92
93 virtual void Prepare(StunMessage* request) {
94 request->SetType(STUN_BINDING_REQUEST);
95 }
96
97 StunRequestTest* test_;
98};
99
100// Test handling of a normal binding response.
101TEST_F(StunRequestTest, TestSuccess) {
102 StunMessage* req = CreateStunMessage(STUN_BINDING_REQUEST, NULL);
103
104 manager_.Send(new StunRequestThunker(req, this));
105 StunMessage* res = CreateStunMessage(STUN_BINDING_RESPONSE, req);
106 EXPECT_TRUE(manager_.CheckResponse(res));
107
108 EXPECT_TRUE(response_ == res);
109 EXPECT_TRUE(success_);
110 EXPECT_FALSE(failure_);
111 EXPECT_FALSE(timeout_);
112 delete res;
113}
114
115// Test handling of an error binding response.
116TEST_F(StunRequestTest, TestError) {
117 StunMessage* req = CreateStunMessage(STUN_BINDING_REQUEST, NULL);
118
119 manager_.Send(new StunRequestThunker(req, this));
120 StunMessage* res = CreateStunMessage(STUN_BINDING_ERROR_RESPONSE, req);
121 EXPECT_TRUE(manager_.CheckResponse(res));
122
123 EXPECT_TRUE(response_ == res);
124 EXPECT_FALSE(success_);
125 EXPECT_TRUE(failure_);
126 EXPECT_FALSE(timeout_);
127 delete res;
128}
129
130// Test handling of a binding response with the wrong transaction id.
131TEST_F(StunRequestTest, TestUnexpected) {
132 StunMessage* req = CreateStunMessage(STUN_BINDING_REQUEST, NULL);
133
134 manager_.Send(new StunRequestThunker(req, this));
135 StunMessage* res = CreateStunMessage(STUN_BINDING_RESPONSE, NULL);
136 EXPECT_FALSE(manager_.CheckResponse(res));
137
138 EXPECT_TRUE(response_ == NULL);
139 EXPECT_FALSE(success_);
140 EXPECT_FALSE(failure_);
141 EXPECT_FALSE(timeout_);
142 delete res;
143}
144
145// Test that requests are sent at the right times, and that the 9th request
146// (sent at 7900 ms) can be properly replied to.
147TEST_F(StunRequestTest, TestBackoff) {
skvladb9d8d102016-09-06 17:17:17 -0700148 const int MAX_TIMEOUT_MS = 10000;
149 rtc::ScopedFakeClock fake_clock;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000150 StunMessage* req = CreateStunMessage(STUN_BINDING_REQUEST, NULL);
151
nisse1bffc1d2016-05-02 08:18:55 -0700152 int64_t start = rtc::TimeMillis();
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000153 manager_.Send(new StunRequestThunker(req, this));
154 StunMessage* res = CreateStunMessage(STUN_BINDING_RESPONSE, req);
155 for (int i = 0; i < 9; ++i) {
skvladb9d8d102016-09-06 17:17:17 -0700156 EXPECT_TRUE_SIMULATED_WAIT(request_count_ != i, MAX_TIMEOUT_MS, fake_clock);
nisse1bffc1d2016-05-02 08:18:55 -0700157 int64_t elapsed = rtc::TimeMillis() - start;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000158 LOG(LS_INFO) << "STUN request #" << (i + 1)
159 << " sent at " << elapsed << " ms";
skvladb9d8d102016-09-06 17:17:17 -0700160 EXPECT_EQ(TotalDelay(i), elapsed);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000161 }
162 EXPECT_TRUE(manager_.CheckResponse(res));
163
164 EXPECT_TRUE(response_ == res);
165 EXPECT_TRUE(success_);
166 EXPECT_FALSE(failure_);
167 EXPECT_FALSE(timeout_);
168 delete res;
169}
170
171// Test that we timeout properly if no response is received in 9500 ms.
172TEST_F(StunRequestTest, TestTimeout) {
skvladb9d8d102016-09-06 17:17:17 -0700173 rtc::ScopedFakeClock fake_clock;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000174 StunMessage* req = CreateStunMessage(STUN_BINDING_REQUEST, NULL);
175 StunMessage* res = CreateStunMessage(STUN_BINDING_RESPONSE, req);
176
177 manager_.Send(new StunRequestThunker(req, this));
skvladb9d8d102016-09-06 17:17:17 -0700178 // Simulate the 9500 ms STUN timeout
179 SIMULATED_WAIT(false, 9500, fake_clock);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000180
skvladb9d8d102016-09-06 17:17:17 -0700181 EXPECT_FALSE(manager_.CheckResponse(res));
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000182 EXPECT_TRUE(response_ == NULL);
183 EXPECT_FALSE(success_);
184 EXPECT_FALSE(failure_);
185 EXPECT_TRUE(timeout_);
186 delete res;
187}
188
189// Regression test for specific crash where we receive a response with the
190// same id as a request that doesn't have an underlying StunMessage yet.
191TEST_F(StunRequestTest, TestNoEmptyRequest) {
192 StunRequestThunker* request = new StunRequestThunker(this);
193
194 manager_.SendDelayed(request, 100);
195
196 StunMessage dummy_req;
197 dummy_req.SetTransactionID(request->id());
198 StunMessage* res = CreateStunMessage(STUN_BINDING_RESPONSE, &dummy_req);
199
200 EXPECT_TRUE(manager_.CheckResponse(res));
201
202 EXPECT_TRUE(response_ == res);
203 EXPECT_TRUE(success_);
204 EXPECT_FALSE(failure_);
205 EXPECT_FALSE(timeout_);
206 delete res;
207}