// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/net/websocket_experiment/websocket_experiment_runner.h" #include "base/command_line.h" #include "base/compiler_specific.h" #include "base/message_loop.h" #include "base/metrics/field_trial.h" #include "base/task.h" #include "base/string_util.h" #include "chrome/common/chrome_switches.h" #include "content/browser/browser_thread.h" #include "net/base/host_resolver.h" #include "net/base/net_errors.h" #include "net/websockets/websocket.h" namespace chrome_browser_net_websocket_experiment { static const char *kExperimentHost = "websocket-experiment.chromium.org"; static const int kAlternativePort = 61985; // Hold reference while experiment is running. static scoped_refptr<WebSocketExperimentRunner> runner; /* static */ void WebSocketExperimentRunner::Start() { DCHECK(!runner.get()); // After June 30, 2011 builds, it will always be in default group. scoped_refptr<base::FieldTrial> trial( new base::FieldTrial( "WebSocketExperiment", 1000, "default", 2011, 6, 30)); int active = trial->AppendGroup("active", 5); // 0.5% in active group. bool run_experiment = (trial->group() == active); #ifndef NDEBUG const CommandLine& command_line = *CommandLine::ForCurrentProcess(); std::string experiment_host = command_line.GetSwitchValueASCII( switches::kWebSocketLiveExperimentHost); if (!experiment_host.empty()) run_experiment = true; #else run_experiment = false; #endif if (!run_experiment) return; runner = new WebSocketExperimentRunner; runner->Run(); } /* static */ void WebSocketExperimentRunner::Stop() { if (runner.get()) runner->Cancel(); runner = NULL; } WebSocketExperimentRunner::WebSocketExperimentRunner() : next_state_(STATE_NONE), task_state_(STATE_NONE), ALLOW_THIS_IN_INITIALIZER_LIST( task_callback_(this, &WebSocketExperimentRunner::OnTaskCompleted)) { WebSocketExperimentTask::InitHistogram(); InitConfig(); } WebSocketExperimentRunner::~WebSocketExperimentRunner() { DCHECK(!task_.get()); WebSocketExperimentTask::ReleaseHistogram(); } void WebSocketExperimentRunner::Run() { DCHECK_EQ(next_state_, STATE_NONE); next_state_ = STATE_RUN_WS; BrowserThread::PostDelayedTask( BrowserThread::IO, FROM_HERE, NewRunnableMethod(this, &WebSocketExperimentRunner::DoLoop), config_.initial_delay_ms); } void WebSocketExperimentRunner::Cancel() { next_state_ = STATE_NONE; BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, NewRunnableMethod(this, &WebSocketExperimentRunner::DoLoop)); } void WebSocketExperimentRunner::InitConfig() { config_.initial_delay_ms = 5 * 60 * 1000; // 5 mins config_.next_delay_ms = 12 * 60 * 60 * 1000; // 12 hours std::string experiment_host = kExperimentHost; #ifndef NDEBUG const CommandLine& command_line = *CommandLine::ForCurrentProcess(); std::string experiment_host_override = command_line.GetSwitchValueASCII( switches::kWebSocketLiveExperimentHost); if (!experiment_host_override.empty()) { experiment_host = experiment_host_override; config_.initial_delay_ms = 5 * 1000; // 5 secs. } #endif WebSocketExperimentTask::Config* config; WebSocketExperimentTask::Config task_config; task_config.protocol_version = net::WebSocket::DEFAULT_VERSION; config = &config_.ws_config[STATE_RUN_WS - STATE_RUN_WS]; *config = task_config; config->url = GURL(StringPrintf("ws://%s/live_exp", experiment_host.c_str())); config->ws_location = StringPrintf("ws://%s/live_exp", experiment_host.c_str()); config->http_url = GURL(StringPrintf("http://%s/", experiment_host.c_str())); config = &config_.ws_config[STATE_RUN_WSS - STATE_RUN_WS]; *config = task_config; config->url = GURL(StringPrintf("wss://%s/live_exp", experiment_host.c_str())); config->ws_location = StringPrintf("wss://%s/live_exp", experiment_host.c_str()); config->http_url = GURL(StringPrintf("https://%s/", experiment_host.c_str())); config = &config_.ws_config[STATE_RUN_WS_NODEFAULT_PORT - STATE_RUN_WS]; *config = task_config; config->url = GURL(StringPrintf("ws://%s:%d/live_exp", experiment_host.c_str(), kAlternativePort)); config->ws_location = StringPrintf("ws://%s:%d/live_exp", experiment_host.c_str(), kAlternativePort); config->http_url = GURL(StringPrintf("http://%s:%d/", experiment_host.c_str(), kAlternativePort)); task_config.protocol_version = net::WebSocket::DRAFT75; config = &config_.ws_config[STATE_RUN_WS_DRAFT75 - STATE_RUN_WS]; *config = task_config; config->url = GURL(StringPrintf("ws://%s/live_exp", experiment_host.c_str())); config->ws_location = StringPrintf("ws://%s/live_exp", experiment_host.c_str()); config->http_url = GURL(StringPrintf("http://%s/", experiment_host.c_str())); config = &config_.ws_config[STATE_RUN_WSS_DRAFT75 - STATE_RUN_WS]; *config = task_config; config->url = GURL(StringPrintf("wss://%s/live_exp", experiment_host.c_str())); config->ws_location = StringPrintf("wss://%s/live_exp", experiment_host.c_str()); config->http_url = GURL(StringPrintf("https://%s/", experiment_host.c_str())); config = &config_.ws_config[STATE_RUN_WS_NODEFAULT_PORT_DRAFT75 - STATE_RUN_WS]; *config = task_config; config->url = GURL(StringPrintf("ws://%s:%d/live_exp", experiment_host.c_str(), kAlternativePort)); config->ws_location = StringPrintf("ws://%s:%d/live_exp", experiment_host.c_str(), kAlternativePort); config->http_url = GURL(StringPrintf("http://%s:%d/", experiment_host.c_str(), kAlternativePort)); } void WebSocketExperimentRunner::DoLoop() { if (next_state_ == STATE_NONE) { if (task_.get()) { AddRef(); // Release in OnTaskCompleted after Cancelled. task_->Cancel(); } return; } State state = next_state_; task_state_ = STATE_NONE; next_state_ = STATE_NONE; switch (state) { case STATE_IDLE: task_.reset(); next_state_ = STATE_RUN_WS; BrowserThread::PostDelayedTask( BrowserThread::IO, FROM_HERE, NewRunnableMethod(this, &WebSocketExperimentRunner::DoLoop), config_.next_delay_ms); break; case STATE_RUN_WS: case STATE_RUN_WSS: case STATE_RUN_WS_NODEFAULT_PORT: case STATE_RUN_WS_DRAFT75: case STATE_RUN_WSS_DRAFT75: case STATE_RUN_WS_NODEFAULT_PORT_DRAFT75: task_.reset(new WebSocketExperimentTask( config_.ws_config[state - STATE_RUN_WS], &task_callback_)); task_state_ = state; if (static_cast<State>(state + 1) == NUM_STATES) next_state_ = STATE_IDLE; else next_state_ = static_cast<State>(state + 1); break; default: NOTREACHED(); break; } if (task_.get()) task_->Run(); } void WebSocketExperimentRunner::OnTaskCompleted(int result) { if (next_state_ == STATE_NONE) { task_.reset(); // Task is Canceled. DVLOG(1) << "WebSocketExperiment Task is canceled."; Release(); return; } task_->SaveResult(); task_.reset(); DoLoop(); } } // namespace chrome_browser_net_websocket_experiment