15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The tests in this file attempt to verify the following through simulation:
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// a) That a server experiencing overload will actually benefit from the
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//    anti-DDoS throttling logic, i.e. that its traffic spike will subside
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//    and be distributed over a longer period of time;
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// b) That "well-behaved" clients of a server under DDoS attack actually
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//    benefit from the anti-DDoS throttling logic; and
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// c) That the approximate increase in "perceived downtime" introduced by
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//    anti-DDoS throttling for various different actual downtimes is what
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//    we expect it to be.
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <cmath>
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <limits>
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <vector>
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/environment.h"
201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/memory/scoped_ptr.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/memory/scoped_vector.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/rand_util.h"
23eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/time/time.h"
240f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)#include "net/base/request_priority.h"
251320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "net/url_request/url_request.h"
2603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)#include "net/url_request/url_request_context.h"
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/url_request/url_request_test_util.h"
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/url_request/url_request_throttler_manager.h"
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/url_request/url_request_throttler_test_support.h"
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "testing/gtest/include/gtest/gtest.h"
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using base::TimeDelta;
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using base::TimeTicks;
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace net {
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Set this variable in your environment if you want to see verbose results
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// of the simulation tests.
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kShowSimulationVariableName[] = "SHOW_SIMULATION_RESULTS";
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Prints output only if a given environment variable is set. We use this
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// to not print any output for human evaluation when the test is run without
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// supervision.
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void VerboseOut(const char* format, ...) {
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static bool have_checked_environment = false;
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  static bool should_print = false;
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!have_checked_environment) {
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    have_checked_environment = true;
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    scoped_ptr<base::Environment> env(base::Environment::Create());
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (env->HasVar(kShowSimulationVariableName))
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      should_print = true;
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (should_print) {
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    va_list arglist;
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    va_start(arglist, format);
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    vprintf(format, arglist);
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    va_end(arglist);
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// A simple two-phase discrete time simulation. Actors are added in the order
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// they should take action at every tick of the clock. Ticks of the clock
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// are two-phase:
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// - Phase 1 advances every actor's time to a new absolute time.
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// - Phase 2 asks each actor to perform their action.
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class DiscreteTimeSimulation {
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  class Actor {
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)   public:
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    virtual ~Actor() {}
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    virtual void AdvanceTime(const TimeTicks& absolute_time) = 0;
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    virtual void PerformAction() = 0;
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DiscreteTimeSimulation() {}
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Adds an |actor| to the simulation. The client of the simulation maintains
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // ownership of |actor| and must ensure its lifetime exceeds that of the
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // simulation. Actors should be added in the order you wish for them to
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // act at each tick of the simulation.
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void AddActor(Actor* actor) {
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    actors_.push_back(actor);
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Runs the simulation for, pretending |time_between_ticks| passes from one
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // tick to the next. The start time will be the current real time. The
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // simulation will stop when the simulated duration is equal to or greater
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // than |maximum_simulated_duration|.
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void RunSimulation(const TimeDelta& maximum_simulated_duration,
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     const TimeDelta& time_between_ticks) {
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    TimeTicks start_time = TimeTicks();
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    TimeTicks now = start_time;
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    while ((now - start_time) <= maximum_simulated_duration) {
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (std::vector<Actor*>::iterator it = actors_.begin();
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           it != actors_.end();
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           ++it) {
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        (*it)->AdvanceTime(now);
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (std::vector<Actor*>::iterator it = actors_.begin();
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           it != actors_.end();
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           ++it) {
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        (*it)->PerformAction();
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      now += time_between_ticks;
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<Actor*> actors_;
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(DiscreteTimeSimulation);
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Represents a web server in a simulation of a server under attack by
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// a lot of clients. Must be added to the simulation's list of actors
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// after all |Requester| objects.
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class Server : public DiscreteTimeSimulation::Actor {
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
1230f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  Server(int max_queries_per_tick, double request_drop_ratio)
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      : max_queries_per_tick_(max_queries_per_tick),
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        request_drop_ratio_(request_drop_ratio),
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        num_overloaded_ticks_remaining_(0),
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        num_current_tick_queries_(0),
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        num_overloaded_ticks_(0),
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        max_experienced_queries_per_tick_(0),
1301320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        mock_request_(context_.CreateRequest(
1311320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci            GURL(), DEFAULT_PRIORITY, NULL, NULL)) {}
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void SetDowntime(const TimeTicks& start_time, const TimeDelta& duration) {
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    start_downtime_ = start_time;
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    end_downtime_ = start_time + duration;
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void AdvanceTime(const TimeTicks& absolute_time) OVERRIDE {
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    now_ = absolute_time;
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void PerformAction() OVERRIDE {
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We are inserted at the end of the actor's list, so all Requester
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // instances have already done their bit.
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (num_current_tick_queries_ > max_experienced_queries_per_tick_)
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      max_experienced_queries_per_tick_ = num_current_tick_queries_;
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (num_current_tick_queries_ > max_queries_per_tick_) {
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // We pretend the server fails for the next several ticks after it
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // gets overloaded.
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      num_overloaded_ticks_remaining_ = 5;
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++num_overloaded_ticks_;
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else if (num_overloaded_ticks_remaining_ > 0) {
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      --num_overloaded_ticks_remaining_;
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    requests_per_tick_.push_back(num_current_tick_queries_);
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    num_current_tick_queries_ = 0;
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This is called by Requester. It returns the response code from
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // the server.
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int HandleRequest() {
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ++num_current_tick_queries_;
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!start_downtime_.is_null() &&
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        start_downtime_ < now_ && now_ < end_downtime_) {
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // For the simulation measuring the increase in perceived
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // downtime, it might be interesting to count separately the
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // queries seen by the server (assuming a front-end reverse proxy
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // is what actually serves up the 503s in this case) so that we could
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // visualize the traffic spike seen by the server when it comes up,
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // which would in many situations be ameliorated by the anti-DDoS
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // throttling.
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return 503;
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ((num_overloaded_ticks_remaining_ > 0 ||
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)         num_current_tick_queries_ > max_queries_per_tick_) &&
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        base::RandDouble() < request_drop_ratio_) {
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return 503;
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 200;
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int num_overloaded_ticks() const {
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return num_overloaded_ticks_;
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int max_experienced_queries_per_tick() const {
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return max_experienced_queries_per_tick_;
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const URLRequest& mock_request() const {
1951320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return *mock_request_.get();
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string VisualizeASCII(int terminal_width) {
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Account for | characters we place at left of graph.
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    terminal_width -= 1;
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    VerboseOut("Overloaded for %d of %d ticks.\n",
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               num_overloaded_ticks_, requests_per_tick_.size());
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    VerboseOut("Got maximum of %d requests in a tick.\n\n",
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               max_experienced_queries_per_tick_);
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    VerboseOut("Traffic graph:\n\n");
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Printing the graph like this is a bit overkill, but was very useful
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // while developing the various simulations to see if they were testing
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // the corner cases we want to simulate.
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Find the smallest number of whole ticks we need to group into a
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // column that will let all ticks fit into the column width we have.
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int num_ticks = requests_per_tick_.size();
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    double ticks_per_column_exact =
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        static_cast<double>(num_ticks) / static_cast<double>(terminal_width);
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int ticks_per_column = std::ceil(ticks_per_column_exact);
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK_GE(ticks_per_column * terminal_width, num_ticks);
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Sum up the column values.
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int num_columns = num_ticks / ticks_per_column;
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (num_ticks % ticks_per_column)
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++num_columns;
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK_LE(num_columns, terminal_width);
226c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    scoped_ptr<int[]> columns(new int[num_columns]);
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (int tx = 0; tx < num_ticks; ++tx) {
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int cx = tx / ticks_per_column;
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (tx % ticks_per_column == 0)
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        columns[cx] = 0;
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      columns[cx] += requests_per_tick_[tx];
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Find the lowest integer divisor that will let the column values
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // be represented in a graph of maximum height 50.
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int max_value = 0;
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (int cx = 0; cx < num_columns; ++cx)
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      max_value = std::max(max_value, columns[cx]);
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const int kNumRows = 50;
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    double row_divisor_exact = max_value / static_cast<double>(kNumRows);
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int row_divisor = std::ceil(row_divisor_exact);
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK_GE(row_divisor * kNumRows, max_value);
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // To show the overload line, we calculate the appropriate value.
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int overload_value = max_queries_per_tick_ * ticks_per_column;
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // When num_ticks is not a whole multiple of ticks_per_column, the last
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // column includes fewer ticks than the others. In this case, don't
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // print it so that we don't show an inconsistent value.
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int num_printed_columns = num_columns;
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (num_ticks % ticks_per_column)
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      --num_printed_columns;
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // This is a top-to-bottom traversal of rows, left-to-right per row.
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string output;
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (int rx = 0; rx < kNumRows; ++rx) {
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int range_min = (kNumRows - rx) * row_divisor;
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int range_max = range_min + row_divisor;
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (range_min == 0)
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        range_min = -1;  // Make 0 values fit in the bottom range.
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      output.append("|");
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for (int cx = 0; cx < num_printed_columns; ++cx) {
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        char block = ' ';
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Show the overload line.
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (range_min < overload_value && overload_value <= range_max)
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          block = '-';
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        // Preferentially, show the graph line.
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (range_min < columns[cx] && columns[cx] <= range_max)
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          block = '#';
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        output.append(1, block);
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      output.append("\n");
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    output.append("|");
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    output.append(num_printed_columns, '=');
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return output;
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
28203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)  const URLRequestContext& context() const { return context_; }
28303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TimeTicks now_;
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TimeTicks start_downtime_;  // Can be 0 to say "no downtime".
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TimeTicks end_downtime_;
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const int max_queries_per_tick_;
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const double request_drop_ratio_;  // Ratio of requests to 503 when failing.
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int num_overloaded_ticks_remaining_;
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int num_current_tick_queries_;
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int num_overloaded_ticks_;
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int max_experienced_queries_per_tick_;
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::vector<int> requests_per_tick_;
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TestURLRequestContext context_;
2971320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  scoped_ptr<URLRequest> mock_request_;
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(Server);
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Mock throttler entry used by Requester class.
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class MockURLRequestThrottlerEntry : public URLRequestThrottlerEntry {
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
305c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  explicit MockURLRequestThrottlerEntry(URLRequestThrottlerManager* manager)
306c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      : URLRequestThrottlerEntry(manager, std::string()),
307c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        mock_backoff_entry_(&backoff_policy_) {}
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual const BackoffEntry* GetBackoffEntry() const OVERRIDE {
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return &mock_backoff_entry_;
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual BackoffEntry* GetBackoffEntry() OVERRIDE {
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return &mock_backoff_entry_;
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual TimeTicks ImplGetTimeNow() const OVERRIDE {
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return fake_now_;
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void SetFakeNow(const TimeTicks& fake_time) {
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    fake_now_ = fake_time;
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    mock_backoff_entry_.set_fake_now(fake_time);
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TimeTicks fake_now() const {
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return fake_now_;
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) protected:
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual ~MockURLRequestThrottlerEntry() {}
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TimeTicks fake_now_;
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  MockBackoffEntry mock_backoff_entry_;
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Registry of results for a class of |Requester| objects (e.g. attackers vs.
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// regular clients).
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class RequesterResults {
341cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) public:
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  RequesterResults()
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      : num_attempts_(0), num_successful_(0), num_failed_(0), num_blocked_(0) {
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void AddSuccess() {
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ++num_attempts_;
3485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ++num_successful_;
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void AddFailure() {
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ++num_attempts_;
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ++num_failed_;
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void AddBlocked() {
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ++num_attempts_;
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    ++num_blocked_;
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int num_attempts() const { return num_attempts_; }
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int num_successful() const { return num_successful_; }
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int num_failed() const { return num_failed_; }
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int num_blocked() const { return num_blocked_; }
3655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  double GetBlockedRatio() {
3675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(num_attempts_);
3685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return static_cast<double>(num_blocked_) /
3695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        static_cast<double>(num_attempts_);
3705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  double GetSuccessRatio() {
3735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(num_attempts_);
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return static_cast<double>(num_successful_) /
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        static_cast<double>(num_attempts_);
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void PrintResults(const char* class_description) {
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (num_attempts_ == 0) {
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      VerboseOut("No data for %s\n", class_description);
3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return;
3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    VerboseOut("Requester results for %s\n", class_description);
3855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    VerboseOut("  %d attempts\n", num_attempts_);
3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    VerboseOut("  %d successes\n", num_successful_);
3875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    VerboseOut("  %d 5xx responses\n", num_failed_);
3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    VerboseOut("  %d requests blocked\n", num_blocked_);
3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    VerboseOut("  %.2f success ratio\n", GetSuccessRatio());
3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    VerboseOut("  %.2f blocked ratio\n", GetBlockedRatio());
3915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    VerboseOut("\n");
3925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int num_attempts_;
3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int num_successful_;
3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int num_failed_;
3985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int num_blocked_;
3995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Represents an Requester in a simulated DDoS situation, that periodically
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// requests a specific resource.
4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class Requester : public DiscreteTimeSimulation::Actor {
4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
4055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Requester(MockURLRequestThrottlerEntry* throttler_entry,
4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            const TimeDelta& time_between_requests,
4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            Server* server,
4085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            RequesterResults* results)
4095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      : throttler_entry_(throttler_entry),
4105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        time_between_requests_(time_between_requests),
4115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        last_attempt_was_failure_(false),
4125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        server_(server),
4135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        results_(results) {
4145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DCHECK(server_);
4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual void AdvanceTime(const TimeTicks& absolute_time) OVERRIDE {
4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (time_of_last_success_.is_null())
4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      time_of_last_success_ = absolute_time;
4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    throttler_entry_->SetFakeNow(absolute_time);
4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  virtual void PerformAction() OVERRIDE {
4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    TimeDelta effective_delay = time_between_requests_;
4265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    TimeDelta current_jitter = TimeDelta::FromMilliseconds(
4275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        request_jitter_.InMilliseconds() * base::RandDouble());
4285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (base::RandInt(0, 1)) {
4295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      effective_delay -= current_jitter;
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
4315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      effective_delay += current_jitter;
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (throttler_entry_->fake_now() - time_of_last_attempt_ >
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        effective_delay) {
43603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)      if (!throttler_entry_->ShouldRejectRequest(
43703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)              server_->mock_request(),
43803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)              server_->context().network_delegate())) {
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        int status_code = server_->HandleRequest();
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        MockURLRequestThrottlerHeaderAdapter response_headers(status_code);
441c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        throttler_entry_->UpdateWithResponse(std::string(), &response_headers);
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (status_code == 200) {
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if (results_)
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            results_->AddSuccess();
4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if (last_attempt_was_failure_) {
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            last_downtime_duration_ =
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                throttler_entry_->fake_now() - time_of_last_success_;
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          }
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          time_of_last_success_ = throttler_entry_->fake_now();
4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          last_attempt_was_failure_ = false;
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        } else {
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if (results_)
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            results_->AddFailure();
4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          last_attempt_was_failure_ = true;
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        }
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if (results_)
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          results_->AddBlocked();
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        last_attempt_was_failure_ = true;
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      time_of_last_attempt_ = throttler_entry_->fake_now();
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Adds a delay until the first request, equal to a uniformly distributed
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // value between now and now + max_delay.
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void SetStartupJitter(const TimeDelta& max_delay) {
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int delay_ms = base::RandInt(0, max_delay.InMilliseconds());
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    time_of_last_attempt_ = TimeTicks() +
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        TimeDelta::FromMilliseconds(delay_ms) - time_between_requests_;
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void SetRequestJitter(const TimeDelta& request_jitter) {
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    request_jitter_ = request_jitter;
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TimeDelta last_downtime_duration() const { return last_downtime_duration_; }
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_refptr<MockURLRequestThrottlerEntry> throttler_entry_;
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const TimeDelta time_between_requests_;
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TimeDelta request_jitter_;
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TimeTicks time_of_last_attempt_;
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TimeTicks time_of_last_success_;
4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool last_attempt_was_failure_;
4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TimeDelta last_downtime_duration_;
4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Server* const server_;
4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  RequesterResults* const results_;  // May be NULL.
4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(Requester);
4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void SimulateAttack(Server* server,
4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    RequesterResults* attacker_results,
4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    RequesterResults* client_results,
5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    bool enable_throttling) {
5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const size_t kNumAttackers = 50;
5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  const size_t kNumClients = 50;
5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DiscreteTimeSimulation simulation;
5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  URLRequestThrottlerManager manager;
5055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ScopedVector<Requester> requesters;
5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < kNumAttackers; ++i) {
5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Use a tiny time_between_requests so the attackers will ping the
5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // server at every tick of the simulation.
5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    scoped_refptr<MockURLRequestThrottlerEntry> throttler_entry(
5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        new MockURLRequestThrottlerEntry(&manager));
5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!enable_throttling)
5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      throttler_entry->DisableBackoffThrottling();
5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      Requester* attacker = new Requester(throttler_entry.get(),
5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                        TimeDelta::FromMilliseconds(1),
5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                        server,
5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                        attacker_results);
5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    attacker->SetStartupJitter(TimeDelta::FromSeconds(120));
5195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    requesters.push_back(attacker);
5205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    simulation.AddActor(attacker);
5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < kNumClients; ++i) {
5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Normal clients only make requests every 2 minutes, plus/minus 1 minute.
5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    scoped_refptr<MockURLRequestThrottlerEntry> throttler_entry(
5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        new MockURLRequestThrottlerEntry(&manager));
5265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!enable_throttling)
5275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      throttler_entry->DisableBackoffThrottling();
5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Requester* client = new Requester(throttler_entry.get(),
5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                      TimeDelta::FromMinutes(2),
5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                      server,
5325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                      client_results);
5335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    client->SetStartupJitter(TimeDelta::FromSeconds(120));
5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    client->SetRequestJitter(TimeDelta::FromMinutes(1));
5355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    requesters.push_back(client);
5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    simulation.AddActor(client);
5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  simulation.AddActor(server);
5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  simulation.RunSimulation(TimeDelta::FromMinutes(6),
5415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                           TimeDelta::FromSeconds(1));
5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST(URLRequestThrottlerSimulation, HelpsInAttack) {
5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Server unprotected_server(30, 1.0);
5465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  RequesterResults unprotected_attacker_results;
5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  RequesterResults unprotected_client_results;
5485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Server protected_server(30, 1.0);
5495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  RequesterResults protected_attacker_results;
5505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  RequesterResults protected_client_results;
5515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SimulateAttack(&unprotected_server,
5525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 &unprotected_attacker_results,
5535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 &unprotected_client_results,
5545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 false);
5555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SimulateAttack(&protected_server,
5565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 &protected_attacker_results,
5575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 &protected_client_results,
5585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 true);
5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // These assert that the DDoS protection actually benefits the
5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // server. Manual inspection of the traffic graphs will show this
5625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // even more clearly.
5635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_GT(unprotected_server.num_overloaded_ticks(),
5645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            protected_server.num_overloaded_ticks());
5655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_GT(unprotected_server.max_experienced_queries_per_tick(),
5665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            protected_server.max_experienced_queries_per_tick());
5675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // These assert that the DDoS protection actually benefits non-malicious
5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // (and non-degenerate/accidentally DDoSing) users.
5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_LT(protected_client_results.GetBlockedRatio(),
5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            protected_attacker_results.GetBlockedRatio());
5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_GT(protected_client_results.GetSuccessRatio(),
5735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            unprotected_client_results.GetSuccessRatio());
5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // The rest is just for optional manual evaluation of the results;
5765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // in particular the traffic pattern is interesting.
5775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  VerboseOut("\nUnprotected server's results:\n\n");
5795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  VerboseOut(unprotected_server.VisualizeASCII(132).c_str());
5805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  VerboseOut("\n\n");
5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  VerboseOut("Protected server's results:\n\n");
5825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  VerboseOut(protected_server.VisualizeASCII(132).c_str());
5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  VerboseOut("\n\n");
5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  unprotected_attacker_results.PrintResults(
5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "attackers attacking unprotected server.");
5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  unprotected_client_results.PrintResults(
5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "normal clients making requests to unprotected server.");
5895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  protected_attacker_results.PrintResults(
5905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "attackers attacking protected server.");
5915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  protected_client_results.PrintResults(
5925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "normal clients making requests to protected server.");
5935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
5945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
5955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Returns the downtime perceived by the client, as a ratio of the
5965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// actual downtime.
5975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)double SimulateDowntime(const TimeDelta& duration,
5985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        const TimeDelta& average_client_interval,
5995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                        bool enable_throttling) {
6005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TimeDelta time_between_ticks = duration / 200;
6015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  TimeTicks start_downtime = TimeTicks() + (duration / 2);
6025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // A server that never rejects requests, but will go down for maintenance.
6045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Server server(std::numeric_limits<int>::max(), 1.0);
6055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  server.SetDowntime(start_downtime, duration);
6065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  URLRequestThrottlerManager manager;
6085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_refptr<MockURLRequestThrottlerEntry> throttler_entry(
6095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      new MockURLRequestThrottlerEntry(&manager));
6105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!enable_throttling)
6115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    throttler_entry->DisableBackoffThrottling();
6125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Requester requester(
6145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      throttler_entry.get(), average_client_interval, &server, NULL);
6155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  requester.SetStartupJitter(duration / 3);
6165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  requester.SetRequestJitter(average_client_interval);
6175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DiscreteTimeSimulation simulation;
6195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  simulation.AddActor(&requester);
6205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  simulation.AddActor(&server);
6215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  simulation.RunSimulation(duration * 2, time_between_ticks);
6235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return static_cast<double>(
6255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      requester.last_downtime_duration().InMilliseconds()) /
6265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      static_cast<double>(duration.InMilliseconds());
6275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
6285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TEST(URLRequestThrottlerSimulation, PerceivedDowntimeRatio) {
6305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  struct Stats {
6315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Expected interval that we expect the ratio of downtime when anti-DDoS
6325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // is enabled and downtime when anti-DDoS is not enabled to fall within.
6335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    //
6345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // The expected interval depends on two things:  The exponential back-off
6355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // policy encoded in URLRequestThrottlerEntry, and the test or set of
6365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // tests that the Stats object is tracking (e.g. a test where the client
6375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // retries very rapidly on a very long downtime will tend to increase the
6385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // number).
6395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    //
6405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // To determine an appropriate new interval when parameters have changed,
6415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // run the test a few times (you may have to Ctrl-C out of it after a few
6425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // seconds) and choose an interval that the test converges quickly and
6435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // reliably to.  Then set the new interval, and run the test e.g. 20 times
6445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // in succession to make sure it never takes an obscenely long time to
6455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // converge to this interval.
6465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    double expected_min_increase;
6475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    double expected_max_increase;
6485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    size_t num_runs;
6505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    double total_ratio_unprotected;
6515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    double total_ratio_protected;
6525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    bool DidConverge(double* increase_ratio_out) {
6545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      double unprotected_ratio = total_ratio_unprotected / num_runs;
6555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      double protected_ratio = total_ratio_protected / num_runs;
6565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      double increase_ratio = protected_ratio / unprotected_ratio;
6575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (increase_ratio_out)
6585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        *increase_ratio_out = increase_ratio;
6595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return expected_min_increase <= increase_ratio &&
6605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          increase_ratio <= expected_max_increase;
6615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
6625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    void ReportTrialResult(double increase_ratio) {
6645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      VerboseOut(
6655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          "  Perceived downtime with throttling is %.4f times without.\n",
6665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          increase_ratio);
6675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      VerboseOut("  Test result after %d trials.\n", num_runs);
6685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
6695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
6705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Stats global_stats = { 1.08, 1.15 };
6725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  struct Trial {
6745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    TimeDelta duration;
6755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    TimeDelta average_client_interval;
6765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Stats stats;
6775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    void PrintTrialDescription() {
6795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      double duration_minutes =
6805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          static_cast<double>(duration.InSeconds()) / 60.0;
6815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      double interval_minutes =
6825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          static_cast<double>(average_client_interval.InSeconds()) / 60.0;
6835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      VerboseOut("Trial with %.2f min downtime, avg. interval %.2f min.\n",
6845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 duration_minutes, interval_minutes);
6855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
6865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
6875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
6885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // We don't set or check expected ratio intervals on individual
6895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // experiments as this might make the test too fragile, but we
6905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // print them out at the end for manual evaluation (we want to be
6915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // able to make claims about the expected ratios depending on the
6925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // type of behavior of the client and the downtime, e.g. the difference
6935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // in behavior between a client making requests every few minutes vs.
6945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // one that makes a request every 15 seconds).
6955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Trial trials[] = {
6965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    { TimeDelta::FromSeconds(10), TimeDelta::FromSeconds(3) },
6975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    { TimeDelta::FromSeconds(30), TimeDelta::FromSeconds(7) },
6985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    { TimeDelta::FromMinutes(5), TimeDelta::FromSeconds(30) },
6995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    { TimeDelta::FromMinutes(10), TimeDelta::FromSeconds(20) },
7005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    { TimeDelta::FromMinutes(20), TimeDelta::FromSeconds(15) },
7015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    { TimeDelta::FromMinutes(20), TimeDelta::FromSeconds(50) },
7025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    { TimeDelta::FromMinutes(30), TimeDelta::FromMinutes(2) },
7035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    { TimeDelta::FromMinutes(30), TimeDelta::FromMinutes(5) },
7045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    { TimeDelta::FromMinutes(40), TimeDelta::FromMinutes(7) },
7055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    { TimeDelta::FromMinutes(40), TimeDelta::FromMinutes(2) },
7065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    { TimeDelta::FromMinutes(40), TimeDelta::FromSeconds(15) },
7075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    { TimeDelta::FromMinutes(60), TimeDelta::FromMinutes(7) },
7085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    { TimeDelta::FromMinutes(60), TimeDelta::FromMinutes(2) },
7095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    { TimeDelta::FromMinutes(60), TimeDelta::FromSeconds(15) },
7105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    { TimeDelta::FromMinutes(80), TimeDelta::FromMinutes(20) },
7115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    { TimeDelta::FromMinutes(80), TimeDelta::FromMinutes(3) },
7125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    { TimeDelta::FromMinutes(80), TimeDelta::FromSeconds(15) },
7135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Most brutal?
7155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    { TimeDelta::FromMinutes(45), TimeDelta::FromMilliseconds(500) },
7165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
7175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // If things don't converge by the time we've done 100K trials, then
7195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // clearly one or more of the expected intervals are wrong.
7205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  while (global_stats.num_runs < 100000) {
7215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (size_t i = 0; i < ARRAYSIZE_UNSAFE(trials); ++i) {
7225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++global_stats.num_runs;
7235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ++trials[i].stats.num_runs;
7245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      double ratio_unprotected = SimulateDowntime(
7255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          trials[i].duration, trials[i].average_client_interval, false);
7265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      double ratio_protected = SimulateDowntime(
7275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          trials[i].duration, trials[i].average_client_interval, true);
7285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      global_stats.total_ratio_unprotected += ratio_unprotected;
7295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      global_stats.total_ratio_protected += ratio_protected;
7305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      trials[i].stats.total_ratio_unprotected += ratio_unprotected;
7315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      trials[i].stats.total_ratio_protected += ratio_protected;
7325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
7335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    double increase_ratio;
7355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (global_stats.DidConverge(&increase_ratio))
7365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
7375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (global_stats.num_runs > 200) {
7395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      VerboseOut("Test has not yet converged on expected interval.\n");
7405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      global_stats.ReportTrialResult(increase_ratio);
7415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
7425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  double average_increase_ratio;
7455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EXPECT_TRUE(global_stats.DidConverge(&average_increase_ratio));
7465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Print individual trial results for optional manual evaluation.
7485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  double max_increase_ratio = 0.0;
7495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(trials); ++i) {
7505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    double increase_ratio;
7515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    trials[i].stats.DidConverge(&increase_ratio);
7525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    max_increase_ratio = std::max(max_increase_ratio, increase_ratio);
7535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    trials[i].PrintTrialDescription();
7545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    trials[i].stats.ReportTrialResult(increase_ratio);
7555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
7565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  VerboseOut("Average increase ratio was %.4f\n", average_increase_ratio);
7585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  VerboseOut("Maximum increase ratio was %.4f\n", max_increase_ratio);
7595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
7605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
7615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace
7625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace net
763