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