Home | History | Annotate | Download | only in websocket_experiment
      1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/net/websocket_experiment/websocket_experiment_runner.h"
      6 
      7 #include "base/command_line.h"
      8 #include "base/compiler_specific.h"
      9 #include "base/message_loop.h"
     10 #include "base/metrics/field_trial.h"
     11 #include "base/task.h"
     12 #include "base/string_util.h"
     13 #include "chrome/common/chrome_switches.h"
     14 #include "content/browser/browser_thread.h"
     15 #include "net/base/host_resolver.h"
     16 #include "net/base/net_errors.h"
     17 #include "net/websockets/websocket.h"
     18 
     19 namespace chrome_browser_net_websocket_experiment {
     20 
     21 static const char *kExperimentHost = "websocket-experiment.chromium.org";
     22 static const int kAlternativePort = 61985;
     23 
     24 // Hold reference while experiment is running.
     25 static scoped_refptr<WebSocketExperimentRunner> runner;
     26 
     27 /* static */
     28 void WebSocketExperimentRunner::Start() {
     29   DCHECK(!runner.get());
     30 
     31   // After June 30, 2011 builds, it will always be in default group.
     32   scoped_refptr<base::FieldTrial> trial(
     33       new base::FieldTrial(
     34           "WebSocketExperiment", 1000, "default", 2011, 6, 30));
     35   int active = trial->AppendGroup("active", 5);  // 0.5% in active group.
     36 
     37   bool run_experiment = (trial->group() == active);
     38 #ifndef NDEBUG
     39   const CommandLine& command_line = *CommandLine::ForCurrentProcess();
     40   std::string experiment_host = command_line.GetSwitchValueASCII(
     41       switches::kWebSocketLiveExperimentHost);
     42   if (!experiment_host.empty())
     43     run_experiment = true;
     44 #else
     45   run_experiment = false;
     46 #endif
     47   if (!run_experiment)
     48     return;
     49 
     50   runner = new WebSocketExperimentRunner;
     51   runner->Run();
     52 }
     53 
     54 /* static */
     55 void WebSocketExperimentRunner::Stop() {
     56   if (runner.get())
     57     runner->Cancel();
     58   runner = NULL;
     59 }
     60 
     61 WebSocketExperimentRunner::WebSocketExperimentRunner()
     62     : next_state_(STATE_NONE),
     63       task_state_(STATE_NONE),
     64       ALLOW_THIS_IN_INITIALIZER_LIST(
     65           task_callback_(this, &WebSocketExperimentRunner::OnTaskCompleted)) {
     66   WebSocketExperimentTask::InitHistogram();
     67   InitConfig();
     68 }
     69 
     70 WebSocketExperimentRunner::~WebSocketExperimentRunner() {
     71   DCHECK(!task_.get());
     72   WebSocketExperimentTask::ReleaseHistogram();
     73 }
     74 
     75 void WebSocketExperimentRunner::Run() {
     76   DCHECK_EQ(next_state_, STATE_NONE);
     77   next_state_ = STATE_RUN_WS;
     78   BrowserThread::PostDelayedTask(
     79       BrowserThread::IO,
     80       FROM_HERE,
     81       NewRunnableMethod(this, &WebSocketExperimentRunner::DoLoop),
     82       config_.initial_delay_ms);
     83 }
     84 
     85 void WebSocketExperimentRunner::Cancel() {
     86   next_state_ = STATE_NONE;
     87   BrowserThread::PostTask(
     88       BrowserThread::IO,
     89       FROM_HERE,
     90       NewRunnableMethod(this, &WebSocketExperimentRunner::DoLoop));
     91 }
     92 
     93 void WebSocketExperimentRunner::InitConfig() {
     94   config_.initial_delay_ms = 5 * 60 * 1000;  // 5 mins
     95   config_.next_delay_ms = 12 * 60 * 60 * 1000;  // 12 hours
     96 
     97   std::string experiment_host = kExperimentHost;
     98 #ifndef NDEBUG
     99   const CommandLine& command_line = *CommandLine::ForCurrentProcess();
    100   std::string experiment_host_override = command_line.GetSwitchValueASCII(
    101       switches::kWebSocketLiveExperimentHost);
    102   if (!experiment_host_override.empty()) {
    103     experiment_host = experiment_host_override;
    104     config_.initial_delay_ms = 5 * 1000;  // 5 secs.
    105   }
    106 #endif
    107 
    108   WebSocketExperimentTask::Config* config;
    109   WebSocketExperimentTask::Config task_config;
    110 
    111   task_config.protocol_version = net::WebSocket::DEFAULT_VERSION;
    112   config = &config_.ws_config[STATE_RUN_WS - STATE_RUN_WS];
    113   *config = task_config;
    114   config->url =
    115       GURL(StringPrintf("ws://%s/live_exp", experiment_host.c_str()));
    116   config->ws_location =
    117       StringPrintf("ws://%s/live_exp", experiment_host.c_str());
    118   config->http_url =
    119       GURL(StringPrintf("http://%s/", experiment_host.c_str()));
    120 
    121   config = &config_.ws_config[STATE_RUN_WSS - STATE_RUN_WS];
    122   *config = task_config;
    123   config->url =
    124       GURL(StringPrintf("wss://%s/live_exp", experiment_host.c_str()));
    125   config->ws_location =
    126       StringPrintf("wss://%s/live_exp", experiment_host.c_str());
    127   config->http_url =
    128       GURL(StringPrintf("https://%s/", experiment_host.c_str()));
    129 
    130   config = &config_.ws_config[STATE_RUN_WS_NODEFAULT_PORT -
    131                               STATE_RUN_WS];
    132   *config = task_config;
    133   config->url =
    134       GURL(StringPrintf("ws://%s:%d/live_exp",
    135                         experiment_host.c_str(), kAlternativePort));
    136   config->ws_location =
    137       StringPrintf("ws://%s:%d/live_exp",
    138                    experiment_host.c_str(), kAlternativePort);
    139   config->http_url =
    140       GURL(StringPrintf("http://%s:%d/",
    141                         experiment_host.c_str(), kAlternativePort));
    142 
    143   task_config.protocol_version = net::WebSocket::DRAFT75;
    144   config = &config_.ws_config[STATE_RUN_WS_DRAFT75 - STATE_RUN_WS];
    145   *config = task_config;
    146   config->url =
    147       GURL(StringPrintf("ws://%s/live_exp", experiment_host.c_str()));
    148   config->ws_location =
    149       StringPrintf("ws://%s/live_exp", experiment_host.c_str());
    150   config->http_url =
    151       GURL(StringPrintf("http://%s/", experiment_host.c_str()));
    152 
    153   config = &config_.ws_config[STATE_RUN_WSS_DRAFT75 - STATE_RUN_WS];
    154   *config = task_config;
    155   config->url =
    156       GURL(StringPrintf("wss://%s/live_exp", experiment_host.c_str()));
    157   config->ws_location =
    158       StringPrintf("wss://%s/live_exp", experiment_host.c_str());
    159   config->http_url =
    160       GURL(StringPrintf("https://%s/", experiment_host.c_str()));
    161 
    162   config = &config_.ws_config[STATE_RUN_WS_NODEFAULT_PORT_DRAFT75 -
    163                               STATE_RUN_WS];
    164   *config = task_config;
    165   config->url =
    166       GURL(StringPrintf("ws://%s:%d/live_exp",
    167                         experiment_host.c_str(), kAlternativePort));
    168   config->ws_location =
    169       StringPrintf("ws://%s:%d/live_exp",
    170                    experiment_host.c_str(), kAlternativePort);
    171   config->http_url =
    172       GURL(StringPrintf("http://%s:%d/",
    173                         experiment_host.c_str(), kAlternativePort));
    174 
    175 }
    176 
    177 void WebSocketExperimentRunner::DoLoop() {
    178   if (next_state_ == STATE_NONE) {
    179     if (task_.get()) {
    180       AddRef();  // Release in OnTaskCompleted after Cancelled.
    181       task_->Cancel();
    182     }
    183     return;
    184   }
    185 
    186   State state = next_state_;
    187   task_state_ = STATE_NONE;
    188   next_state_ = STATE_NONE;
    189 
    190   switch (state) {
    191     case STATE_IDLE:
    192       task_.reset();
    193       next_state_ = STATE_RUN_WS;
    194       BrowserThread::PostDelayedTask(
    195           BrowserThread::IO,
    196           FROM_HERE,
    197           NewRunnableMethod(this, &WebSocketExperimentRunner::DoLoop),
    198           config_.next_delay_ms);
    199       break;
    200     case STATE_RUN_WS:
    201     case STATE_RUN_WSS:
    202     case STATE_RUN_WS_NODEFAULT_PORT:
    203     case STATE_RUN_WS_DRAFT75:
    204     case STATE_RUN_WSS_DRAFT75:
    205     case STATE_RUN_WS_NODEFAULT_PORT_DRAFT75:
    206       task_.reset(new WebSocketExperimentTask(
    207           config_.ws_config[state - STATE_RUN_WS], &task_callback_));
    208       task_state_ = state;
    209       if (static_cast<State>(state + 1) == NUM_STATES)
    210         next_state_ = STATE_IDLE;
    211       else
    212         next_state_ = static_cast<State>(state + 1);
    213       break;
    214     default:
    215       NOTREACHED();
    216       break;
    217   }
    218   if (task_.get())
    219     task_->Run();
    220 }
    221 
    222 void WebSocketExperimentRunner::OnTaskCompleted(int result) {
    223   if (next_state_ == STATE_NONE) {
    224     task_.reset();
    225     // Task is Canceled.
    226     DVLOG(1) << "WebSocketExperiment Task is canceled.";
    227     Release();
    228     return;
    229   }
    230   task_->SaveResult();
    231   task_.reset();
    232 
    233   DoLoop();
    234 }
    235 
    236 }  // namespace chrome_browser_net_websocket_experiment
    237