OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // The tests in this file attempt to verify the following through simulation: | 5 // The tests in this file attempt to verify the following through simulation: |
6 // a) That a server experiencing overload will actually benefit from the | 6 // a) That a server experiencing overload will actually benefit from the |
7 // anti-DDoS throttling logic, i.e. that its traffic spike will subside | 7 // anti-DDoS throttling logic, i.e. that its traffic spike will subside |
8 // and be distributed over a longer period of time; | 8 // and be distributed over a longer period of time; |
9 // b) That "well-behaved" clients of a server under DDoS attack actually | 9 // b) That "well-behaved" clients of a server under DDoS attack actually |
10 // benefit from the anti-DDoS throttling logic; and | 10 // benefit from the anti-DDoS throttling logic; and |
11 // c) That the approximate increase in "perceived downtime" introduced by | 11 // c) That the approximate increase in "perceived downtime" introduced by |
12 // anti-DDoS throttling for various different actual downtimes is what | 12 // anti-DDoS throttling for various different actual downtimes is what |
13 // we expect it to be. | 13 // we expect it to be. |
14 | 14 |
15 #include <cmath> | 15 #include <cmath> |
16 #include <limits> | 16 #include <limits> |
17 #include <vector> | 17 #include <vector> |
18 | 18 |
19 #include "base/environment.h" | 19 #include "base/environment.h" |
20 #include "base/memory/scoped_vector.h" | 20 #include "base/memory/scoped_vector.h" |
21 #include "base/rand_util.h" | 21 #include "base/rand_util.h" |
22 #include "base/time.h" | 22 #include "base/time.h" |
| 23 #include "net/url_request/url_request_test_util.h" |
23 #include "net/url_request/url_request_throttler_manager.h" | 24 #include "net/url_request/url_request_throttler_manager.h" |
24 #include "net/url_request/url_request_throttler_test_support.h" | 25 #include "net/url_request/url_request_throttler_test_support.h" |
25 #include "testing/gtest/include/gtest/gtest.h" | 26 #include "testing/gtest/include/gtest/gtest.h" |
26 | 27 |
27 using base::TimeDelta; | 28 using base::TimeDelta; |
28 using base::TimeTicks; | 29 using base::TimeTicks; |
29 | 30 |
30 namespace net { | 31 namespace net { |
31 namespace { | 32 namespace { |
32 | 33 |
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
115 // after all |Requester| objects. | 116 // after all |Requester| objects. |
116 class Server : public DiscreteTimeSimulation::Actor { | 117 class Server : public DiscreteTimeSimulation::Actor { |
117 public: | 118 public: |
118 Server(int max_queries_per_tick, | 119 Server(int max_queries_per_tick, |
119 double request_drop_ratio) | 120 double request_drop_ratio) |
120 : max_queries_per_tick_(max_queries_per_tick), | 121 : max_queries_per_tick_(max_queries_per_tick), |
121 request_drop_ratio_(request_drop_ratio), | 122 request_drop_ratio_(request_drop_ratio), |
122 num_overloaded_ticks_remaining_(0), | 123 num_overloaded_ticks_remaining_(0), |
123 num_current_tick_queries_(0), | 124 num_current_tick_queries_(0), |
124 num_overloaded_ticks_(0), | 125 num_overloaded_ticks_(0), |
125 max_experienced_queries_per_tick_(0) { | 126 max_experienced_queries_per_tick_(0), |
| 127 mock_request_(GURL(), NULL) { |
126 } | 128 } |
127 | 129 |
128 void SetDowntime(const TimeTicks& start_time, const TimeDelta& duration) { | 130 void SetDowntime(const TimeTicks& start_time, const TimeDelta& duration) { |
129 start_downtime_ = start_time; | 131 start_downtime_ = start_time; |
130 end_downtime_ = start_time + duration; | 132 end_downtime_ = start_time + duration; |
131 } | 133 } |
132 | 134 |
133 virtual void AdvanceTime(const TimeTicks& absolute_time) OVERRIDE { | 135 virtual void AdvanceTime(const TimeTicks& absolute_time) OVERRIDE { |
134 now_ = absolute_time; | 136 now_ = absolute_time; |
135 } | 137 } |
(...skipping 16 matching lines...) Expand all Loading... |
152 requests_per_tick_.push_back(num_current_tick_queries_); | 154 requests_per_tick_.push_back(num_current_tick_queries_); |
153 num_current_tick_queries_ = 0; | 155 num_current_tick_queries_ = 0; |
154 } | 156 } |
155 | 157 |
156 // This is called by Requester. It returns the response code from | 158 // This is called by Requester. It returns the response code from |
157 // the server. | 159 // the server. |
158 int HandleRequest() { | 160 int HandleRequest() { |
159 ++num_current_tick_queries_; | 161 ++num_current_tick_queries_; |
160 if (!start_downtime_.is_null() && | 162 if (!start_downtime_.is_null() && |
161 start_downtime_ < now_ && now_ < end_downtime_) { | 163 start_downtime_ < now_ && now_ < end_downtime_) { |
162 // TODO(joi): For the simulation measuring the increase in perceived | 164 // For the simulation measuring the increase in perceived |
163 // downtime, it might be interesting to count separately the | 165 // downtime, it might be interesting to count separately the |
164 // queries seen by the server (assuming a front-end reverse proxy | 166 // queries seen by the server (assuming a front-end reverse proxy |
165 // is what actually serves up the 503s in this case) so that we could | 167 // is what actually serves up the 503s in this case) so that we could |
166 // visualize the traffic spike seen by the server when it comes up, | 168 // visualize the traffic spike seen by the server when it comes up, |
167 // which would in many situations be ameliorated by the anti-DDoS | 169 // which would in many situations be ameliorated by the anti-DDoS |
168 // throttling. | 170 // throttling. |
169 return 503; | 171 return 503; |
170 } | 172 } |
171 | 173 |
172 if ((num_overloaded_ticks_remaining_ > 0 || | 174 if ((num_overloaded_ticks_remaining_ > 0 || |
173 num_current_tick_queries_ > max_queries_per_tick_) && | 175 num_current_tick_queries_ > max_queries_per_tick_) && |
174 base::RandDouble() < request_drop_ratio_) { | 176 base::RandDouble() < request_drop_ratio_) { |
175 return 503; | 177 return 503; |
176 } | 178 } |
177 | 179 |
178 return 200; | 180 return 200; |
179 } | 181 } |
180 | 182 |
181 int num_overloaded_ticks() const { | 183 int num_overloaded_ticks() const { |
182 return num_overloaded_ticks_; | 184 return num_overloaded_ticks_; |
183 } | 185 } |
184 | 186 |
185 int max_experienced_queries_per_tick() const { | 187 int max_experienced_queries_per_tick() const { |
186 return max_experienced_queries_per_tick_; | 188 return max_experienced_queries_per_tick_; |
187 } | 189 } |
188 | 190 |
| 191 const URLRequest& mock_request() const { |
| 192 return mock_request_; |
| 193 } |
| 194 |
189 std::string VisualizeASCII(int terminal_width) { | 195 std::string VisualizeASCII(int terminal_width) { |
190 // Account for | characters we place at left of graph. | 196 // Account for | characters we place at left of graph. |
191 terminal_width -= 1; | 197 terminal_width -= 1; |
192 | 198 |
193 VerboseOut("Overloaded for %d of %d ticks.\n", | 199 VerboseOut("Overloaded for %d of %d ticks.\n", |
194 num_overloaded_ticks_, requests_per_tick_.size()); | 200 num_overloaded_ticks_, requests_per_tick_.size()); |
195 VerboseOut("Got maximum of %d requests in a tick.\n\n", | 201 VerboseOut("Got maximum of %d requests in a tick.\n\n", |
196 max_experienced_queries_per_tick_); | 202 max_experienced_queries_per_tick_); |
197 | 203 |
198 VerboseOut("Traffic graph:\n\n"); | 204 VerboseOut("Traffic graph:\n\n"); |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
274 TimeTicks now_; | 280 TimeTicks now_; |
275 TimeTicks start_downtime_; // Can be 0 to say "no downtime". | 281 TimeTicks start_downtime_; // Can be 0 to say "no downtime". |
276 TimeTicks end_downtime_; | 282 TimeTicks end_downtime_; |
277 const int max_queries_per_tick_; | 283 const int max_queries_per_tick_; |
278 const double request_drop_ratio_; // Ratio of requests to 503 when failing. | 284 const double request_drop_ratio_; // Ratio of requests to 503 when failing. |
279 int num_overloaded_ticks_remaining_; | 285 int num_overloaded_ticks_remaining_; |
280 int num_current_tick_queries_; | 286 int num_current_tick_queries_; |
281 int num_overloaded_ticks_; | 287 int num_overloaded_ticks_; |
282 int max_experienced_queries_per_tick_; | 288 int max_experienced_queries_per_tick_; |
283 std::vector<int> requests_per_tick_; | 289 std::vector<int> requests_per_tick_; |
| 290 TestURLRequest mock_request_; |
284 | 291 |
285 DISALLOW_COPY_AND_ASSIGN(Server); | 292 DISALLOW_COPY_AND_ASSIGN(Server); |
286 }; | 293 }; |
287 | 294 |
288 class TestingURLRequestThrottlerManager : public URLRequestThrottlerManager { | |
289 public: | |
290 TestingURLRequestThrottlerManager() : URLRequestThrottlerManager() { | |
291 } | |
292 }; | |
293 | |
294 // Mock throttler entry used by Requester class. | 295 // Mock throttler entry used by Requester class. |
295 class MockURLRequestThrottlerEntry : public URLRequestThrottlerEntry { | 296 class MockURLRequestThrottlerEntry : public URLRequestThrottlerEntry { |
296 public: | 297 public: |
297 explicit MockURLRequestThrottlerEntry( | 298 explicit MockURLRequestThrottlerEntry( |
298 URLRequestThrottlerManager* manager) | 299 URLRequestThrottlerManager* manager) |
299 : URLRequestThrottlerEntry(manager, ""), | 300 : URLRequestThrottlerEntry(manager, ""), |
300 mock_backoff_entry_(&backoff_policy_) { | 301 mock_backoff_entry_(&backoff_policy_) { |
301 } | 302 } |
302 | 303 |
303 virtual const BackoffEntry* GetBackoffEntry() const OVERRIDE { | 304 virtual const BackoffEntry* GetBackoffEntry() const OVERRIDE { |
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
420 TimeDelta current_jitter = TimeDelta::FromMilliseconds( | 421 TimeDelta current_jitter = TimeDelta::FromMilliseconds( |
421 request_jitter_.InMilliseconds() * base::RandDouble()); | 422 request_jitter_.InMilliseconds() * base::RandDouble()); |
422 if (base::RandInt(0, 1)) { | 423 if (base::RandInt(0, 1)) { |
423 effective_delay -= current_jitter; | 424 effective_delay -= current_jitter; |
424 } else { | 425 } else { |
425 effective_delay += current_jitter; | 426 effective_delay += current_jitter; |
426 } | 427 } |
427 | 428 |
428 if (throttler_entry_->fake_now() - time_of_last_attempt_ > | 429 if (throttler_entry_->fake_now() - time_of_last_attempt_ > |
429 effective_delay) { | 430 effective_delay) { |
430 if (!throttler_entry_->ShouldRejectRequest(0)) { | 431 if (!throttler_entry_->ShouldRejectRequest(server_->mock_request())) { |
431 int status_code = server_->HandleRequest(); | 432 int status_code = server_->HandleRequest(); |
432 MockURLRequestThrottlerHeaderAdapter response_headers(status_code); | 433 MockURLRequestThrottlerHeaderAdapter response_headers(status_code); |
433 throttler_entry_->UpdateWithResponse("", &response_headers); | 434 throttler_entry_->UpdateWithResponse("", &response_headers); |
434 | 435 |
435 if (status_code == 200) { | 436 if (status_code == 200) { |
436 if (results_) | 437 if (results_) |
437 results_->AddSuccess(); | 438 results_->AddSuccess(); |
438 | 439 |
439 if (last_attempt_was_failure_) { | 440 if (last_attempt_was_failure_) { |
440 last_downtime_duration_ = | 441 last_downtime_duration_ = |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
486 DISALLOW_COPY_AND_ASSIGN(Requester); | 487 DISALLOW_COPY_AND_ASSIGN(Requester); |
487 }; | 488 }; |
488 | 489 |
489 void SimulateAttack(Server* server, | 490 void SimulateAttack(Server* server, |
490 RequesterResults* attacker_results, | 491 RequesterResults* attacker_results, |
491 RequesterResults* client_results, | 492 RequesterResults* client_results, |
492 bool enable_throttling) { | 493 bool enable_throttling) { |
493 const size_t kNumAttackers = 50; | 494 const size_t kNumAttackers = 50; |
494 const size_t kNumClients = 50; | 495 const size_t kNumClients = 50; |
495 DiscreteTimeSimulation simulation; | 496 DiscreteTimeSimulation simulation; |
496 TestingURLRequestThrottlerManager manager; | 497 URLRequestThrottlerManager manager; |
497 ScopedVector<Requester> requesters; | 498 ScopedVector<Requester> requesters; |
498 for (size_t i = 0; i < kNumAttackers; ++i) { | 499 for (size_t i = 0; i < kNumAttackers; ++i) { |
499 // Use a tiny time_between_requests so the attackers will ping the | 500 // Use a tiny time_between_requests so the attackers will ping the |
500 // server at every tick of the simulation. | 501 // server at every tick of the simulation. |
501 scoped_refptr<MockURLRequestThrottlerEntry> throttler_entry( | 502 scoped_refptr<MockURLRequestThrottlerEntry> throttler_entry( |
502 new MockURLRequestThrottlerEntry(&manager)); | 503 new MockURLRequestThrottlerEntry(&manager)); |
503 if (!enable_throttling) | 504 if (!enable_throttling) |
504 throttler_entry->DisableBackoffThrottling(); | 505 throttler_entry->DisableBackoffThrottling(); |
505 | 506 |
506 Requester* attacker = new Requester(throttler_entry.get(), | 507 Requester* attacker = new Requester(throttler_entry.get(), |
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
589 double SimulateDowntime(const TimeDelta& duration, | 590 double SimulateDowntime(const TimeDelta& duration, |
590 const TimeDelta& average_client_interval, | 591 const TimeDelta& average_client_interval, |
591 bool enable_throttling) { | 592 bool enable_throttling) { |
592 TimeDelta time_between_ticks = duration / 200; | 593 TimeDelta time_between_ticks = duration / 200; |
593 TimeTicks start_downtime = TimeTicks() + (duration / 2); | 594 TimeTicks start_downtime = TimeTicks() + (duration / 2); |
594 | 595 |
595 // A server that never rejects requests, but will go down for maintenance. | 596 // A server that never rejects requests, but will go down for maintenance. |
596 Server server(std::numeric_limits<int>::max(), 1.0); | 597 Server server(std::numeric_limits<int>::max(), 1.0); |
597 server.SetDowntime(start_downtime, duration); | 598 server.SetDowntime(start_downtime, duration); |
598 | 599 |
599 TestingURLRequestThrottlerManager manager; | 600 URLRequestThrottlerManager manager; |
600 scoped_refptr<MockURLRequestThrottlerEntry> throttler_entry( | 601 scoped_refptr<MockURLRequestThrottlerEntry> throttler_entry( |
601 new MockURLRequestThrottlerEntry(&manager)); | 602 new MockURLRequestThrottlerEntry(&manager)); |
602 if (!enable_throttling) | 603 if (!enable_throttling) |
603 throttler_entry->DisableBackoffThrottling(); | 604 throttler_entry->DisableBackoffThrottling(); |
604 | 605 |
605 Requester requester( | 606 Requester requester( |
606 throttler_entry.get(), average_client_interval, &server, NULL); | 607 throttler_entry.get(), average_client_interval, &server, NULL); |
607 requester.SetStartupJitter(duration / 3); | 608 requester.SetStartupJitter(duration / 3); |
608 requester.SetRequestJitter(average_client_interval); | 609 requester.SetRequestJitter(average_client_interval); |
609 | 610 |
(...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
745 trials[i].PrintTrialDescription(); | 746 trials[i].PrintTrialDescription(); |
746 trials[i].stats.ReportTrialResult(increase_ratio); | 747 trials[i].stats.ReportTrialResult(increase_ratio); |
747 } | 748 } |
748 | 749 |
749 VerboseOut("Average increase ratio was %.4f\n", average_increase_ratio); | 750 VerboseOut("Average increase ratio was %.4f\n", average_increase_ratio); |
750 VerboseOut("Maximum increase ratio was %.4f\n", max_increase_ratio); | 751 VerboseOut("Maximum increase ratio was %.4f\n", max_increase_ratio); |
751 } | 752 } |
752 | 753 |
753 } // namespace | 754 } // namespace |
754 } // namespace net | 755 } // namespace net |
OLD | NEW |