1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <memory>
6
7 #include "base/metrics/histogram_samples.h"
8 #include "base/pickle.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/test/metrics/histogram_tester.h"
11 #include "base/time/time.h"
12 #include "net/base/load_flags.h"
13 #include "net/base/request_priority.h"
14 #include "net/base/test_completion_callback.h"
15 #include "net/test/test_with_task_environment.h"
16 #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
17 #include "net/url_request/url_request.h"
18 #include "net/url_request/url_request_context.h"
19 #include "net/url_request/url_request_context_builder.h"
20 #include "net/url_request/url_request_test_util.h"
21 #include "net/url_request/url_request_throttler_manager.h"
22 #include "net/url_request/url_request_throttler_test_support.h"
23 #include "testing/gtest/include/gtest/gtest.h"
24
25 using base::TimeTicks;
26
27 namespace net {
28
29 namespace {
30
31 const char kRequestThrottledHistogramName[] = "Throttling.RequestThrottled";
32
33 class MockURLRequestThrottlerEntry : public URLRequestThrottlerEntry {
34 public:
MockURLRequestThrottlerEntry(URLRequestThrottlerManager * manager)35 explicit MockURLRequestThrottlerEntry(
36 URLRequestThrottlerManager* manager)
37 : URLRequestThrottlerEntry(manager, std::string()),
38 backoff_entry_(&backoff_policy_, &fake_clock_) {
39 InitPolicy();
40 }
MockURLRequestThrottlerEntry(URLRequestThrottlerManager * manager,const TimeTicks & exponential_backoff_release_time,const TimeTicks & sliding_window_release_time,const TimeTicks & fake_now)41 MockURLRequestThrottlerEntry(
42 URLRequestThrottlerManager* manager,
43 const TimeTicks& exponential_backoff_release_time,
44 const TimeTicks& sliding_window_release_time,
45 const TimeTicks& fake_now)
46 : URLRequestThrottlerEntry(manager, std::string()),
47 fake_clock_(fake_now),
48 backoff_entry_(&backoff_policy_, &fake_clock_) {
49 InitPolicy();
50
51 set_exponential_backoff_release_time(exponential_backoff_release_time);
52 set_sliding_window_release_time(sliding_window_release_time);
53 }
54
InitPolicy()55 void InitPolicy() {
56 // Some tests become flaky if we have jitter.
57 backoff_policy_.jitter_factor = 0.0;
58
59 // This lets us avoid having to make multiple failures initially (this
60 // logic is already tested in the BackoffEntry unit tests).
61 backoff_policy_.num_errors_to_ignore = 0;
62 }
63
GetBackoffEntry() const64 const BackoffEntry* GetBackoffEntry() const override {
65 return &backoff_entry_;
66 }
67
GetBackoffEntry()68 BackoffEntry* GetBackoffEntry() override { return &backoff_entry_; }
69
ResetToBlank(const TimeTicks & time_now)70 void ResetToBlank(const TimeTicks& time_now) {
71 fake_clock_.set_now(time_now);
72
73 GetBackoffEntry()->Reset();
74 set_sliding_window_release_time(time_now);
75 }
76
77 // Overridden for tests.
ImplGetTimeNow() const78 TimeTicks ImplGetTimeNow() const override { return fake_clock_.NowTicks(); }
79
set_fake_now(const TimeTicks & now)80 void set_fake_now(const TimeTicks& now) { fake_clock_.set_now(now); }
81
set_exponential_backoff_release_time(const TimeTicks & release_time)82 void set_exponential_backoff_release_time(const TimeTicks& release_time) {
83 GetBackoffEntry()->SetCustomReleaseTime(release_time);
84 }
85
sliding_window_release_time() const86 TimeTicks sliding_window_release_time() const {
87 return URLRequestThrottlerEntry::sliding_window_release_time();
88 }
89
set_sliding_window_release_time(const TimeTicks & release_time)90 void set_sliding_window_release_time(const TimeTicks& release_time) {
91 URLRequestThrottlerEntry::set_sliding_window_release_time(release_time);
92 }
93
94 protected:
95 ~MockURLRequestThrottlerEntry() override = default;
96
97 private:
98 mutable TestTickClock fake_clock_;
99 BackoffEntry backoff_entry_;
100 };
101
102 class MockURLRequestThrottlerManager : public URLRequestThrottlerManager {
103 public:
104 MockURLRequestThrottlerManager() = default;
105
106 // Method to process the URL using URLRequestThrottlerManager protected
107 // method.
DoGetUrlIdFromUrl(const GURL & url)108 std::string DoGetUrlIdFromUrl(const GURL& url) { return GetIdFromUrl(url); }
109
110 // Method to use the garbage collecting method of URLRequestThrottlerManager.
DoGarbageCollectEntries()111 void DoGarbageCollectEntries() { GarbageCollectEntries(); }
112
113 // Returns the number of entries in the map.
GetNumberOfEntries() const114 int GetNumberOfEntries() const { return GetNumberOfEntriesForTests(); }
115
CreateEntry(bool is_outdated)116 void CreateEntry(bool is_outdated) {
117 TimeTicks time = TimeTicks::Now();
118 if (is_outdated) {
119 time -= base::Milliseconds(
120 MockURLRequestThrottlerEntry::kDefaultEntryLifetimeMs + 1000);
121 }
122 std::string fake_url_string("http://www.fakeurl.com/");
123 fake_url_string.append(base::NumberToString(create_entry_index_++));
124 GURL fake_url(fake_url_string);
125 OverrideEntryForTests(fake_url,
126 base::MakeRefCounted<MockURLRequestThrottlerEntry>(
127 this, time, TimeTicks::Now(), TimeTicks::Now()));
128 }
129
130 private:
131 int create_entry_index_ = 0;
132 };
133
134 struct TimeAndBool {
TimeAndBoolnet::__anond06731b40111::TimeAndBool135 TimeAndBool(const TimeTicks& time_value, bool expected, int line_num) {
136 time = time_value;
137 result = expected;
138 line = line_num;
139 }
140 TimeTicks time;
141 bool result;
142 int line;
143 };
144
145 struct GurlAndString {
GurlAndStringnet::__anond06731b40111::GurlAndString146 GurlAndString(const GURL& url_value,
147 const std::string& expected,
148 int line_num) {
149 url = url_value;
150 result = expected;
151 line = line_num;
152 }
153 GURL url;
154 std::string result;
155 int line;
156 };
157
158 } // namespace
159
160 class URLRequestThrottlerEntryTest : public TestWithTaskEnvironment {
161 protected:
URLRequestThrottlerEntryTest()162 URLRequestThrottlerEntryTest()
163 : context_(CreateTestURLRequestContextBuilder()->Build()),
164 request_(context_->CreateRequest(GURL(),
165 DEFAULT_PRIORITY,
166 nullptr,
167 TRAFFIC_ANNOTATION_FOR_TESTS)) {}
168
169 void SetUp() override;
170
171 TimeTicks now_;
172 MockURLRequestThrottlerManager manager_; // Dummy object, not used.
173 scoped_refptr<MockURLRequestThrottlerEntry> entry_;
174
175 std::unique_ptr<URLRequestContext> context_;
176 std::unique_ptr<URLRequest> request_;
177 };
178
SetUp()179 void URLRequestThrottlerEntryTest::SetUp() {
180 request_->SetLoadFlags(0);
181
182 now_ = TimeTicks::Now();
183 entry_ = base::MakeRefCounted<MockURLRequestThrottlerEntry>(&manager_);
184 entry_->ResetToBlank(now_);
185 }
186
operator <<(std::ostream & out,const base::TimeTicks & time)187 std::ostream& operator<<(std::ostream& out, const base::TimeTicks& time) {
188 return out << time.ToInternalValue();
189 }
190
TEST_F(URLRequestThrottlerEntryTest,InterfaceDuringExponentialBackoff)191 TEST_F(URLRequestThrottlerEntryTest, InterfaceDuringExponentialBackoff) {
192 base::HistogramTester histogram_tester;
193 entry_->set_exponential_backoff_release_time(entry_->ImplGetTimeNow() +
194 base::Milliseconds(1));
195 EXPECT_TRUE(entry_->ShouldRejectRequest(*request_));
196
197 histogram_tester.ExpectBucketCount(kRequestThrottledHistogramName, 0, 0);
198 histogram_tester.ExpectBucketCount(kRequestThrottledHistogramName, 1, 1);
199 }
200
TEST_F(URLRequestThrottlerEntryTest,InterfaceNotDuringExponentialBackoff)201 TEST_F(URLRequestThrottlerEntryTest, InterfaceNotDuringExponentialBackoff) {
202 base::HistogramTester histogram_tester;
203 entry_->set_exponential_backoff_release_time(entry_->ImplGetTimeNow());
204 EXPECT_FALSE(entry_->ShouldRejectRequest(*request_));
205 entry_->set_exponential_backoff_release_time(entry_->ImplGetTimeNow() -
206 base::Milliseconds(1));
207 EXPECT_FALSE(entry_->ShouldRejectRequest(*request_));
208
209 histogram_tester.ExpectBucketCount(kRequestThrottledHistogramName, 0, 2);
210 histogram_tester.ExpectBucketCount(kRequestThrottledHistogramName, 1, 0);
211 }
212
TEST_F(URLRequestThrottlerEntryTest,InterfaceUpdateFailure)213 TEST_F(URLRequestThrottlerEntryTest, InterfaceUpdateFailure) {
214 entry_->UpdateWithResponse(503);
215 EXPECT_GT(entry_->GetExponentialBackoffReleaseTime(),
216 entry_->ImplGetTimeNow())
217 << "A failure should increase the release_time";
218 }
219
TEST_F(URLRequestThrottlerEntryTest,InterfaceUpdateSuccess)220 TEST_F(URLRequestThrottlerEntryTest, InterfaceUpdateSuccess) {
221 entry_->UpdateWithResponse(200);
222 EXPECT_EQ(entry_->GetExponentialBackoffReleaseTime(),
223 entry_->ImplGetTimeNow())
224 << "A success should not add any delay";
225 }
226
TEST_F(URLRequestThrottlerEntryTest,InterfaceUpdateSuccessThenFailure)227 TEST_F(URLRequestThrottlerEntryTest, InterfaceUpdateSuccessThenFailure) {
228 entry_->UpdateWithResponse(200);
229 entry_->UpdateWithResponse(503);
230 EXPECT_GT(entry_->GetExponentialBackoffReleaseTime(),
231 entry_->ImplGetTimeNow())
232 << "This scenario should add delay";
233 entry_->UpdateWithResponse(200);
234 }
235
TEST_F(URLRequestThrottlerEntryTest,IsEntryReallyOutdated)236 TEST_F(URLRequestThrottlerEntryTest, IsEntryReallyOutdated) {
237 base::TimeDelta lifetime =
238 base::Milliseconds(MockURLRequestThrottlerEntry::kDefaultEntryLifetimeMs);
239 const base::TimeDelta kFiveMs = base::Milliseconds(5);
240
241 TimeAndBool test_values[] = {
242 TimeAndBool(now_, false, __LINE__),
243 TimeAndBool(now_ - kFiveMs, false, __LINE__),
244 TimeAndBool(now_ + kFiveMs, false, __LINE__),
245 TimeAndBool(now_ - (lifetime - kFiveMs), false, __LINE__),
246 TimeAndBool(now_ - lifetime, true, __LINE__),
247 TimeAndBool(now_ - (lifetime + kFiveMs), true, __LINE__)};
248
249 for (unsigned int i = 0; i < std::size(test_values); ++i) {
250 entry_->set_exponential_backoff_release_time(test_values[i].time);
251 EXPECT_EQ(entry_->IsEntryOutdated(), test_values[i].result) <<
252 "Test case #" << i << " line " << test_values[i].line << " failed";
253 }
254 }
255
TEST_F(URLRequestThrottlerEntryTest,MaxAllowedBackoff)256 TEST_F(URLRequestThrottlerEntryTest, MaxAllowedBackoff) {
257 for (int i = 0; i < 30; ++i) {
258 entry_->UpdateWithResponse(503);
259 }
260
261 base::TimeDelta delay = entry_->GetExponentialBackoffReleaseTime() - now_;
262 EXPECT_EQ(delay.InMilliseconds(),
263 MockURLRequestThrottlerEntry::kDefaultMaximumBackoffMs);
264 }
265
TEST_F(URLRequestThrottlerEntryTest,MalformedContent)266 TEST_F(URLRequestThrottlerEntryTest, MalformedContent) {
267 for (int i = 0; i < 5; ++i)
268 entry_->UpdateWithResponse(503);
269
270 TimeTicks release_after_failures = entry_->GetExponentialBackoffReleaseTime();
271
272 // Inform the entry that a response body was malformed, which is supposed to
273 // increase the back-off time. Note that we also submit a successful
274 // UpdateWithResponse to pair with ReceivedContentWasMalformed() since that
275 // is what happens in practice (if a body is received, then a non-500
276 // response must also have been received).
277 entry_->ReceivedContentWasMalformed(200);
278 entry_->UpdateWithResponse(200);
279 EXPECT_GT(entry_->GetExponentialBackoffReleaseTime(), release_after_failures);
280 }
281
TEST_F(URLRequestThrottlerEntryTest,SlidingWindow)282 TEST_F(URLRequestThrottlerEntryTest, SlidingWindow) {
283 int max_send = URLRequestThrottlerEntry::kDefaultMaxSendThreshold;
284 int sliding_window =
285 URLRequestThrottlerEntry::kDefaultSlidingWindowPeriodMs;
286
287 TimeTicks time_1 =
288 entry_->ImplGetTimeNow() + base::Milliseconds(sliding_window / 3);
289 TimeTicks time_2 =
290 entry_->ImplGetTimeNow() + base::Milliseconds(2 * sliding_window / 3);
291 TimeTicks time_3 =
292 entry_->ImplGetTimeNow() + base::Milliseconds(sliding_window);
293 TimeTicks time_4 =
294 entry_->ImplGetTimeNow() +
295 base::Milliseconds(sliding_window + 2 * sliding_window / 3);
296
297 entry_->set_exponential_backoff_release_time(time_1);
298
299 for (int i = 0; i < max_send / 2; ++i) {
300 EXPECT_EQ(2 * sliding_window / 3,
301 entry_->ReserveSendingTimeForNextRequest(time_2));
302 }
303 EXPECT_EQ(time_2, entry_->sliding_window_release_time());
304
305 entry_->set_fake_now(time_3);
306
307 for (int i = 0; i < (max_send + 1) / 2; ++i)
308 EXPECT_EQ(0, entry_->ReserveSendingTimeForNextRequest(TimeTicks()));
309
310 EXPECT_EQ(time_4, entry_->sliding_window_release_time());
311 }
312
313 class URLRequestThrottlerManagerTest : public TestWithTaskEnvironment {
314 protected:
URLRequestThrottlerManagerTest()315 URLRequestThrottlerManagerTest()
316 : context_(CreateTestURLRequestContextBuilder()->Build()),
317 request_(context_->CreateRequest(GURL(),
318 DEFAULT_PRIORITY,
319 nullptr,
320 TRAFFIC_ANNOTATION_FOR_TESTS)) {}
321
SetUp()322 void SetUp() override { request_->SetLoadFlags(0); }
323
324 // context_ must be declared before request_.
325 std::unique_ptr<URLRequestContext> context_;
326 std::unique_ptr<URLRequest> request_;
327 };
328
TEST_F(URLRequestThrottlerManagerTest,IsUrlStandardised)329 TEST_F(URLRequestThrottlerManagerTest, IsUrlStandardised) {
330 MockURLRequestThrottlerManager manager;
331 GurlAndString test_values[] = {
332 GurlAndString(GURL("http://www.example.com"),
333 std::string("http://www.example.com/"),
334 __LINE__),
335 GurlAndString(GURL("http://www.Example.com"),
336 std::string("http://www.example.com/"),
337 __LINE__),
338 GurlAndString(GURL("http://www.ex4mple.com/Pr4c71c41"),
339 std::string("http://www.ex4mple.com/pr4c71c41"),
340 __LINE__),
341 GurlAndString(GURL("http://www.example.com/0/token/false"),
342 std::string("http://www.example.com/0/token/false"),
343 __LINE__),
344 GurlAndString(GURL("http://www.example.com/index.php?code=javascript"),
345 std::string("http://www.example.com/index.php"),
346 __LINE__),
347 GurlAndString(GURL("http://www.example.com/index.php?code=1#superEntry"),
348 std::string("http://www.example.com/index.php"),
349 __LINE__),
350 GurlAndString(GURL("http://www.example.com/index.php#superEntry"),
351 std::string("http://www.example.com/index.php"),
352 __LINE__),
353 GurlAndString(GURL("http://www.example.com:1234/"),
354 std::string("http://www.example.com:1234/"),
355 __LINE__)};
356
357 for (unsigned int i = 0; i < std::size(test_values); ++i) {
358 std::string temp = manager.DoGetUrlIdFromUrl(test_values[i].url);
359 EXPECT_EQ(temp, test_values[i].result) <<
360 "Test case #" << i << " line " << test_values[i].line << " failed";
361 }
362 }
363
TEST_F(URLRequestThrottlerManagerTest,AreEntriesBeingCollected)364 TEST_F(URLRequestThrottlerManagerTest, AreEntriesBeingCollected) {
365 MockURLRequestThrottlerManager manager;
366
367 manager.CreateEntry(true); // true = Entry is outdated.
368 manager.CreateEntry(true);
369 manager.CreateEntry(true);
370 manager.DoGarbageCollectEntries();
371 EXPECT_EQ(0, manager.GetNumberOfEntries());
372
373 manager.CreateEntry(false);
374 manager.CreateEntry(false);
375 manager.CreateEntry(false);
376 manager.CreateEntry(true);
377 manager.DoGarbageCollectEntries();
378 EXPECT_EQ(3, manager.GetNumberOfEntries());
379 }
380
TEST_F(URLRequestThrottlerManagerTest,IsHostBeingRegistered)381 TEST_F(URLRequestThrottlerManagerTest, IsHostBeingRegistered) {
382 MockURLRequestThrottlerManager manager;
383
384 manager.RegisterRequestUrl(GURL("http://www.example.com/"));
385 manager.RegisterRequestUrl(GURL("http://www.google.com/"));
386 manager.RegisterRequestUrl(GURL("http://www.google.com/index/0"));
387 manager.RegisterRequestUrl(GURL("http://www.google.com/index/0?code=1"));
388 manager.RegisterRequestUrl(GURL("http://www.google.com/index/0#lolsaure"));
389
390 EXPECT_EQ(3, manager.GetNumberOfEntries());
391 }
392
TEST_F(URLRequestThrottlerManagerTest,LocalHostOptedOut)393 TEST_F(URLRequestThrottlerManagerTest, LocalHostOptedOut) {
394 MockURLRequestThrottlerManager manager;
395 // A localhost entry should always be opted out.
396 scoped_refptr<URLRequestThrottlerEntryInterface> localhost_entry =
397 manager.RegisterRequestUrl(GURL("http://localhost/hello"));
398 EXPECT_FALSE(localhost_entry->ShouldRejectRequest(*request_));
399 for (int i = 0; i < 10; ++i) {
400 localhost_entry->UpdateWithResponse(503);
401 }
402 EXPECT_FALSE(localhost_entry->ShouldRejectRequest(*request_));
403
404 // We're not mocking out GetTimeNow() in this scenario
405 // so add a 100 ms buffer to avoid flakiness (that should always
406 // give enough time to get from the TimeTicks::Now() call here
407 // to the TimeTicks::Now() call in the entry class).
408 EXPECT_GT(TimeTicks::Now() + base::Milliseconds(100),
409 localhost_entry->GetExponentialBackoffReleaseTime());
410 }
411
TEST_F(URLRequestThrottlerManagerTest,ClearOnNetworkChange)412 TEST_F(URLRequestThrottlerManagerTest, ClearOnNetworkChange) {
413 for (int i = 0; i < 3; ++i) {
414 MockURLRequestThrottlerManager manager;
415 scoped_refptr<URLRequestThrottlerEntryInterface> entry_before =
416 manager.RegisterRequestUrl(GURL("http://www.example.com/"));
417 for (int j = 0; j < 10; ++j) {
418 entry_before->UpdateWithResponse(503);
419 }
420 EXPECT_TRUE(entry_before->ShouldRejectRequest(*request_));
421
422 switch (i) {
423 case 0:
424 manager.OnIPAddressChanged();
425 break;
426 case 1:
427 manager.OnConnectionTypeChanged(
428 NetworkChangeNotifier::CONNECTION_UNKNOWN);
429 break;
430 case 2:
431 manager.OnConnectionTypeChanged(NetworkChangeNotifier::CONNECTION_NONE);
432 break;
433 default:
434 FAIL();
435 }
436
437 scoped_refptr<URLRequestThrottlerEntryInterface> entry_after =
438 manager.RegisterRequestUrl(GURL("http://www.example.com/"));
439 EXPECT_FALSE(entry_after->ShouldRejectRequest(*request_));
440 }
441 }
442
443 } // namespace net
444