| 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 |