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