blob: 5fb34a85d263e1ec90e74dd59711e3cb9d97557a [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"
12#include "webrtc/base/gunit.h"
13#include "webrtc/base/helpers.h"
14#include "webrtc/base/logging.h"
15#include "webrtc/base/ssladapter.h"
16#include "webrtc/base/timeutils.h"
17
18using namespace cricket;
19
deadbeef4c2db412016-05-26 14:34:09 -070020// STUN timeout (with all retries) is 9500ms.
21// Add some margin of error for slow bots.
22// TODO(deadbeef): Use simulated clock instead of just increasing timeouts to
23// fix flaky tests.
24static const int kTimeoutMs = 15000;
25
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +000026class StunRequestTest : public testing::Test,
27 public sigslot::has_slots<> {
28 public:
29 StunRequestTest()
30 : manager_(rtc::Thread::Current()),
31 request_count_(0), response_(NULL),
32 success_(false), failure_(false), timeout_(false) {
33 manager_.SignalSendPacket.connect(this, &StunRequestTest::OnSendPacket);
34 }
35
36 void OnSendPacket(const void* data, size_t size, StunRequest* req) {
37 request_count_++;
38 }
39
40 void OnResponse(StunMessage* res) {
41 response_ = res;
42 success_ = true;
43 }
44 void OnErrorResponse(StunMessage* res) {
45 response_ = res;
46 failure_ = true;
47 }
48 void OnTimeout() {
49 timeout_ = true;
50 }
51
52 protected:
53 static StunMessage* CreateStunMessage(StunMessageType type,
54 StunMessage* req) {
55 StunMessage* msg = new StunMessage();
56 msg->SetType(type);
57 if (req) {
58 msg->SetTransactionID(req->transaction_id());
59 }
60 return msg;
61 }
62 static int TotalDelay(int sends) {
63 int total = 0;
64 for (int i = 0; i < sends; i++) {
65 if (i < 4)
66 total += 100 << i;
67 else
68 total += 1600;
69 }
70 return total;
71 }
72
73 StunRequestManager manager_;
74 int request_count_;
75 StunMessage* response_;
76 bool success_;
77 bool failure_;
78 bool timeout_;
79};
80
81// Forwards results to the test class.
82class StunRequestThunker : public StunRequest {
83 public:
84 StunRequestThunker(StunMessage* msg, StunRequestTest* test)
85 : StunRequest(msg), test_(test) {}
86 explicit StunRequestThunker(StunRequestTest* test) : test_(test) {}
87 private:
88 virtual void OnResponse(StunMessage* res) {
89 test_->OnResponse(res);
90 }
91 virtual void OnErrorResponse(StunMessage* res) {
92 test_->OnErrorResponse(res);
93 }
94 virtual void OnTimeout() {
95 test_->OnTimeout();
96 }
97
98 virtual void Prepare(StunMessage* request) {
99 request->SetType(STUN_BINDING_REQUEST);
100 }
101
102 StunRequestTest* test_;
103};
104
105// Test handling of a normal binding response.
106TEST_F(StunRequestTest, TestSuccess) {
107 StunMessage* req = CreateStunMessage(STUN_BINDING_REQUEST, NULL);
108
109 manager_.Send(new StunRequestThunker(req, this));
110 StunMessage* res = CreateStunMessage(STUN_BINDING_RESPONSE, req);
111 EXPECT_TRUE(manager_.CheckResponse(res));
112
113 EXPECT_TRUE(response_ == res);
114 EXPECT_TRUE(success_);
115 EXPECT_FALSE(failure_);
116 EXPECT_FALSE(timeout_);
117 delete res;
118}
119
120// Test handling of an error binding response.
121TEST_F(StunRequestTest, TestError) {
122 StunMessage* req = CreateStunMessage(STUN_BINDING_REQUEST, NULL);
123
124 manager_.Send(new StunRequestThunker(req, this));
125 StunMessage* res = CreateStunMessage(STUN_BINDING_ERROR_RESPONSE, req);
126 EXPECT_TRUE(manager_.CheckResponse(res));
127
128 EXPECT_TRUE(response_ == res);
129 EXPECT_FALSE(success_);
130 EXPECT_TRUE(failure_);
131 EXPECT_FALSE(timeout_);
132 delete res;
133}
134
135// Test handling of a binding response with the wrong transaction id.
136TEST_F(StunRequestTest, TestUnexpected) {
137 StunMessage* req = CreateStunMessage(STUN_BINDING_REQUEST, NULL);
138
139 manager_.Send(new StunRequestThunker(req, this));
140 StunMessage* res = CreateStunMessage(STUN_BINDING_RESPONSE, NULL);
141 EXPECT_FALSE(manager_.CheckResponse(res));
142
143 EXPECT_TRUE(response_ == NULL);
144 EXPECT_FALSE(success_);
145 EXPECT_FALSE(failure_);
146 EXPECT_FALSE(timeout_);
147 delete res;
148}
149
150// Test that requests are sent at the right times, and that the 9th request
151// (sent at 7900 ms) can be properly replied to.
152TEST_F(StunRequestTest, TestBackoff) {
153 StunMessage* req = CreateStunMessage(STUN_BINDING_REQUEST, NULL);
154
nisse1bffc1d2016-05-02 08:18:55 -0700155 int64_t start = rtc::TimeMillis();
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000156 manager_.Send(new StunRequestThunker(req, this));
157 StunMessage* res = CreateStunMessage(STUN_BINDING_RESPONSE, req);
158 for (int i = 0; i < 9; ++i) {
159 while (request_count_ == i)
160 rtc::Thread::Current()->ProcessMessages(1);
nisse1bffc1d2016-05-02 08:18:55 -0700161 int64_t elapsed = rtc::TimeMillis() - start;
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000162 LOG(LS_INFO) << "STUN request #" << (i + 1)
163 << " sent at " << elapsed << " ms";
164 EXPECT_GE(TotalDelay(i + 1), elapsed);
165 }
166 EXPECT_TRUE(manager_.CheckResponse(res));
167
168 EXPECT_TRUE(response_ == res);
169 EXPECT_TRUE(success_);
170 EXPECT_FALSE(failure_);
171 EXPECT_FALSE(timeout_);
172 delete res;
173}
174
175// Test that we timeout properly if no response is received in 9500 ms.
176TEST_F(StunRequestTest, TestTimeout) {
177 StunMessage* req = CreateStunMessage(STUN_BINDING_REQUEST, NULL);
178 StunMessage* res = CreateStunMessage(STUN_BINDING_RESPONSE, req);
179
180 manager_.Send(new StunRequestThunker(req, this));
deadbeef4c2db412016-05-26 14:34:09 -0700181 rtc::Thread::Current()->ProcessMessages(kTimeoutMs);
henrike@webrtc.org269fb4b2014-10-28 22:20:11 +0000182 EXPECT_FALSE(manager_.CheckResponse(res));
183
184 EXPECT_TRUE(response_ == NULL);
185 EXPECT_FALSE(success_);
186 EXPECT_FALSE(failure_);
187 EXPECT_TRUE(timeout_);
188 delete res;
189}
190
191// Regression test for specific crash where we receive a response with the
192// same id as a request that doesn't have an underlying StunMessage yet.
193TEST_F(StunRequestTest, TestNoEmptyRequest) {
194 StunRequestThunker* request = new StunRequestThunker(this);
195
196 manager_.SendDelayed(request, 100);
197
198 StunMessage dummy_req;
199 dummy_req.SetTransactionID(request->id());
200 StunMessage* res = CreateStunMessage(STUN_BINDING_RESPONSE, &dummy_req);
201
202 EXPECT_TRUE(manager_.CheckResponse(res));
203
204 EXPECT_TRUE(response_ == res);
205 EXPECT_TRUE(success_);
206 EXPECT_FALSE(failure_);
207 EXPECT_FALSE(timeout_);
208 delete res;
209}