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