Commit 3af0c5be authored by ukai@chromium.org's avatar ukai@chromium.org

WIP: websocket live experiment

BUG=none
TEST=none

Review URL: http://codereview.chromium.org/333045

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@30328 0039d316-1c4b-4281-b951-d872f2087c98
parent 47611daa
// Copyright (c) 2009 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_task.h"
#include "base/message_loop.h"
#include "chrome/browser/net/url_request_context_getter.h"
#include "chrome/browser/profile.h"
#include "net/websockets/websocket.h"
namespace chrome_browser_net_websocket_experiment {
URLFetcher* WebSocketExperimentTask::Context::CreateURLFetcher() {
return new URLFetcher(config_.http_url, URLFetcher::HEAD, task_);
}
net::WebSocket* WebSocketExperimentTask::Context::CreateWebSocket() {
URLRequestContextGetter* getter =
Profile::GetDefaultRequestContext();
DCHECK(getter);
net::WebSocket::Request* request(
new net::WebSocket::Request(config_.url,
config_.ws_protocol,
config_.ws_origin,
config_.ws_location,
getter->GetURLRequestContext()));
return new net::WebSocket(request, task_);
}
WebSocketExperimentTask::WebSocketExperimentTask(
const Config& config,
net::CompletionCallback* callback)
: config_(config),
context_(ALLOW_THIS_IN_INITIALIZER_LIST(new Context(config, this))),
message_loop_(MessageLoopForIO::current()),
method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
callback_(callback),
next_state_(STATE_NONE) {
DCHECK(message_loop_);
}
WebSocketExperimentTask::~WebSocketExperimentTask() {
DCHECK(!websocket_);
}
void WebSocketExperimentTask::Run() {
next_state_ = STATE_URL_FETCH;
DoLoop(net::OK);
}
// URLFetcher::Delegate method.
void WebSocketExperimentTask::OnURLFetchComplete(
const URLFetcher* source,
const GURL& url,
const URLRequestStatus& status,
int response_code,
const ResponseCookies& cookies,
const std::string& data) {
result_.url_fetch = base::TimeTicks::Now() - url_fetch_start_time_;
RevokeTimeoutTimer();
int result = net::ERR_FAILED;
if (next_state_ != STATE_URL_FETCH_COMPLETE)
result = net::ERR_UNEXPECTED;
else if (response_code == 200)
result = net::OK;
DoLoop(result);
}
// net::WebSocketDelegate
void WebSocketExperimentTask::OnOpen(net::WebSocket* websocket) {
result_.websocket_connect =
base::TimeTicks::Now() - websocket_connect_start_time_;
RevokeTimeoutTimer();
if (next_state_ == STATE_WEBSOCKET_CONNECT_COMPLETE)
DoLoop(net::OK);
else
DoLoop(net::ERR_UNEXPECTED);
}
void WebSocketExperimentTask::OnMessage(
net::WebSocket* websocket, const std::string& msg) {
if (!result_.websocket_echo.ToInternalValue())
result_.websocket_echo =
base::TimeTicks::Now() - websocket_echo_start_time_;
if (!websocket_idle_start_time_.is_null() &&
!result_.websocket_idle.ToInternalValue())
result_.websocket_idle =
base::TimeTicks::Now() - websocket_idle_start_time_;
RevokeTimeoutTimer();
received_messages_.push_back(msg);
int result = net::ERR_UNEXPECTED;
switch (next_state_) {
case STATE_WEBSOCKET_RECV_HELLO:
case STATE_WEBSOCKET_RECV_PUSH_MESSAGE:
case STATE_WEBSOCKET_RECV_BYE:
result = net::OK;
break;
default:
break;
}
DoLoop(result);
}
void WebSocketExperimentTask::OnClose(net::WebSocket* websocket) {
RevokeTimeoutTimer();
int result = net::ERR_CONNECTION_CLOSED;
if (next_state_ == STATE_WEBSOCKET_CLOSE_COMPLETE)
result = net::OK;
DoLoop(result);
}
void WebSocketExperimentTask::SetContext(Context* context) {
context_.reset(context);
}
void WebSocketExperimentTask::OnTimedOut() {
RevokeTimeoutTimer();
DoLoop(net::ERR_TIMED_OUT);
}
void WebSocketExperimentTask::DoLoop(int result) {
do {
State state = next_state_;
next_state_ = STATE_NONE;
switch (state) {
case STATE_URL_FETCH:
result = DoURLFetch();
break;
case STATE_URL_FETCH_COMPLETE:
result = DoURLFetchComplete(result);
break;
case STATE_WEBSOCKET_CONNECT:
result = DoWebSocketConnect();
break;
case STATE_WEBSOCKET_CONNECT_COMPLETE:
result = DoWebSocketConnectComplete(result);
break;
case STATE_WEBSOCKET_SEND_HELLO:
result = DoWebSocketSendHello();
break;
case STATE_WEBSOCKET_RECV_HELLO:
result = DoWebSocketReceiveHello(result);
break;
case STATE_WEBSOCKET_KEEP_IDLE:
result = DoWebSocketKeepIdle();
break;
case STATE_WEBSOCKET_KEEP_IDLE_COMPLETE:
result = DoWebSocketKeepIdleComplete(result);
break;
case STATE_WEBSOCKET_RECV_PUSH_MESSAGE:
result = DoWebSocketReceivePushMessage(result);
break;
case STATE_WEBSOCKET_ECHO_BACK_MESSAGE:
result = DoWebSocketEchoBackMessage();
break;
case STATE_WEBSOCKET_RECV_BYE:
result = DoWebSocketReceiveBye(result);
break;
case STATE_WEBSOCKET_CLOSE:
result = DoWebSocketClose();
break;
case STATE_WEBSOCKET_CLOSE_COMPLETE:
result = DoWebSocketCloseComplete(result);
break;
default:
NOTREACHED();
break;
}
} while (result != net::ERR_IO_PENDING && next_state_ != STATE_NONE);
if (result != net::ERR_IO_PENDING)
Finish(result);
}
int WebSocketExperimentTask::DoURLFetch() {
next_state_ = STATE_URL_FETCH_COMPLETE;
DCHECK(!url_fetcher_.get());
url_fetcher_.reset(context_->CreateURLFetcher());
url_fetch_start_time_ = base::TimeTicks::Now();
url_fetcher_->Start();
SetTimeout(config_.url_fetch_deadline_ms);
return net::ERR_IO_PENDING;
}
int WebSocketExperimentTask::DoURLFetchComplete(int result) {
url_fetcher_.reset();
if (result < 0)
return result;
next_state_ = STATE_WEBSOCKET_CONNECT;
return net::OK;
}
int WebSocketExperimentTask::DoWebSocketConnect() {
DCHECK(!websocket_);
next_state_ = STATE_WEBSOCKET_CONNECT_COMPLETE;
websocket_ = context_->CreateWebSocket();
websocket_connect_start_time_ = base::TimeTicks::Now();
websocket_->Connect();
SetTimeout(config_.websocket_onopen_deadline_ms);
return net::ERR_IO_PENDING;
}
int WebSocketExperimentTask::DoWebSocketConnectComplete(int result) {
if (result < 0)
return result;
DCHECK(websocket_);
next_state_ = STATE_WEBSOCKET_SEND_HELLO;
return net::OK;
}
int WebSocketExperimentTask::DoWebSocketSendHello() {
DCHECK(websocket_);
next_state_ = STATE_WEBSOCKET_RECV_HELLO;
websocket_echo_start_time_ = base::TimeTicks::Now();
websocket_->Send(config_.websocket_hello_message);
SetTimeout(config_.websocket_hello_echoback_deadline_ms);
return net::ERR_IO_PENDING;
}
int WebSocketExperimentTask::DoWebSocketReceiveHello(int result) {
if (result < 0)
return result;
DCHECK(websocket_);
if (received_messages_.size() != 1)
return net::ERR_INVALID_RESPONSE;
std::string msg = received_messages_.front();
received_messages_.pop_front();
if (msg != config_.websocket_hello_message)
return net::ERR_INVALID_RESPONSE;
next_state_ = STATE_WEBSOCKET_KEEP_IDLE;
return net::OK;
}
int WebSocketExperimentTask::DoWebSocketKeepIdle() {
DCHECK(websocket_);
next_state_ = STATE_WEBSOCKET_KEEP_IDLE_COMPLETE;
websocket_idle_start_time_ = base::TimeTicks::Now();
SetTimeout(config_.websocket_idle_ms);
return net::ERR_IO_PENDING;
}
int WebSocketExperimentTask::DoWebSocketKeepIdleComplete(int result) {
if (result != net::ERR_TIMED_OUT) {
// Server sends back too early, or unexpected close?
if (result == net::OK)
result = net::ERR_UNEXPECTED;
return result;
}
DCHECK(websocket_);
next_state_ = STATE_WEBSOCKET_RECV_PUSH_MESSAGE;
SetTimeout(config_.websocket_receive_push_message_deadline_ms);
return net::ERR_IO_PENDING;
}
int WebSocketExperimentTask::DoWebSocketReceivePushMessage(int result) {
if (result < 0)
return result;
DCHECK(websocket_);
if (received_messages_.size() != 1)
return net::ERR_INVALID_RESPONSE;
push_message_ = received_messages_.front();
received_messages_.pop_front();
next_state_ = STATE_WEBSOCKET_ECHO_BACK_MESSAGE;
return net::OK;
}
int WebSocketExperimentTask::DoWebSocketEchoBackMessage() {
DCHECK(websocket_);
DCHECK(!push_message_.empty());
next_state_ = STATE_WEBSOCKET_RECV_BYE;
websocket_->Send(push_message_);
SetTimeout(config_.websocket_bye_deadline_ms);
return net::ERR_IO_PENDING;
}
int WebSocketExperimentTask::DoWebSocketReceiveBye(int result) {
if (result < 0)
return result;
DCHECK(websocket_);
if (received_messages_.size() != 1)
return net::ERR_INVALID_RESPONSE;
std::string bye = received_messages_.front();
received_messages_.pop_front();
if (bye != config_.websocket_bye_message)
return net::ERR_INVALID_RESPONSE;
next_state_ = STATE_WEBSOCKET_CLOSE;
return net::OK;
}
int WebSocketExperimentTask::DoWebSocketClose() {
DCHECK(websocket_);
next_state_ = STATE_WEBSOCKET_CLOSE_COMPLETE;
websocket_->Close();
SetTimeout(config_.websocket_close_deadline_ms);
return net::ERR_IO_PENDING;
}
int WebSocketExperimentTask::DoWebSocketCloseComplete(int result) {
websocket_ = NULL;
return result;
}
void WebSocketExperimentTask::SetTimeout(int64 deadline_ms) {
message_loop_->PostDelayedTask(
FROM_HERE,
method_factory_.NewRunnableMethod(&WebSocketExperimentTask::OnTimedOut),
deadline_ms);
}
void WebSocketExperimentTask::RevokeTimeoutTimer() {
method_factory_.RevokeAll();
}
void WebSocketExperimentTask::Finish(int result) {
callback_->Run(result);
}
} // namespace chrome_browser_net
// Copyright (c) 2009 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.
//
// WebSocket live experiment task.
// It will try the following scenario.
//
// - Fetch |http_url| within |url_fetch_deadline_ms| msec.
// If failed, the task is aborted (no http reachability)
//
// - Connect to |url| with WebSocket protocol within
// |websocket_onopen_deadline_ms| msec.
// Checks WebSocket connection can be established.
//
// - Send |websocket_hello_message| on the WebSocket connection and
// wait it from server within |websocket_hello_echoback_deadline_ms| msec.
// Checks message can be sent/received on the WebSocket connection.
//
// - Keep connection idle at least |websocket_idle_ms| msec.
// Checks WebSocket connection keep open in idle state.
//
// - Wait for some message from server within
// |websocket_receive_push_message_deadline_ms| msec, and echo it back.
// Checks server can push a message after connection has been idle.
//
// - Expect that |websocket_bye_message| message arrives within
// |websocket_bye_deadline_ms| msec from server.
// Checks previous message was sent to the server.
//
// - Close the connection and wait |websocket_close_deadline_ms| msec
// for onclose.
// Checks WebSocket connection can be closed normally.
#ifndef CHROME_BROWSER_NET_WEBSOCKET_EXPERIMENT_WEBSOCKET_EXPERIMENT_TASK_H_
#define CHROME_BROWSER_NET_WEBSOCKET_EXPERIMENT_WEBSOCKET_EXPERIMENT_TASK_H_
#include <deque>
#include <string>
#include "base/basictypes.h"
#include "base/task.h"
#include "base/time.h"
#include "chrome/browser/net/url_fetcher.h"
#include "googleurl/src/gurl.h"
#include "net/base/completion_callback.h"
#include "net/base/net_errors.h"
#include "net/websockets/websocket.h"
class MessageLoop;
namespace net {
class WebSocket;
} // namespace net
namespace chrome_browser_net_websocket_experiment {
class WebSocketExperimentTask : public URLFetcher::Delegate,
public net::WebSocketDelegate {
public:
enum State {
STATE_NONE,
STATE_URL_FETCH,
STATE_URL_FETCH_COMPLETE,
STATE_WEBSOCKET_CONNECT,
STATE_WEBSOCKET_CONNECT_COMPLETE,
STATE_WEBSOCKET_SEND_HELLO,
STATE_WEBSOCKET_RECV_HELLO,
STATE_WEBSOCKET_KEEP_IDLE,
STATE_WEBSOCKET_KEEP_IDLE_COMPLETE,
STATE_WEBSOCKET_RECV_PUSH_MESSAGE,
STATE_WEBSOCKET_ECHO_BACK_MESSAGE,
STATE_WEBSOCKET_RECV_BYE,
STATE_WEBSOCKET_CLOSE,
STATE_WEBSOCKET_CLOSE_COMPLETE,
};
class Config {
public:
Config()
: url_fetch_deadline_ms(0),
websocket_onopen_deadline_ms(0),
websocket_hello_echoback_deadline_ms(0),
websocket_idle_ms(0),
websocket_receive_push_message_deadline_ms(0),
websocket_bye_deadline_ms(0),
websocket_close_deadline_ms(0) {}
GURL url;
std::string ws_protocol;
std::string ws_origin;
std::string ws_location;
GURL http_url;
int64 url_fetch_deadline_ms;
int64 websocket_onopen_deadline_ms;
std::string websocket_hello_message;
int64 websocket_hello_echoback_deadline_ms;
int64 websocket_idle_ms;
int64 websocket_receive_push_message_deadline_ms;
std::string websocket_bye_message;
int64 websocket_bye_deadline_ms;
int64 websocket_close_deadline_ms;
};
class Context {
public:
Context(const Config& config, WebSocketExperimentTask* task)
: config_(config), task_(task) {}
virtual ~Context() {}
virtual URLFetcher* CreateURLFetcher();
virtual net::WebSocket* CreateWebSocket();
protected:
const Config& config_;
WebSocketExperimentTask* task_;
private:
DISALLOW_COPY_AND_ASSIGN(Context);
};
class Result {
public:
Result()
: last_result(net::ERR_UNEXPECTED),
last_state(STATE_NONE) {}
int last_result;
State last_state;
base::TimeDelta url_fetch;
base::TimeDelta websocket_connect;
base::TimeDelta websocket_echo;
base::TimeDelta websocket_idle;
};
// WebSocketExperimentTask will call |callback| with the last status code
// when the task is finished.
WebSocketExperimentTask(const Config& config,
net::CompletionCallback* callback);
virtual ~WebSocketExperimentTask();
void Run();
const Result& GetResult() const {
return result_;
}
// URLFetcher::Delegate method.
virtual void OnURLFetchComplete(const URLFetcher* source,
const GURL& url,
const URLRequestStatus& status,
int response_code,
const ResponseCookies& cookies,
const std::string& data);
// net::WebSocketDelegate methods
virtual void OnOpen(net::WebSocket* websocket);
virtual void OnMessage(net::WebSocket* websocket, const std::string& msg);
virtual void OnClose(net::WebSocket* websocket);
void SetContext(Context* context);
private:
void OnTimedOut();
void DoLoop(int result);
int DoURLFetch();
int DoURLFetchComplete(int result);
int DoWebSocketConnect();
int DoWebSocketConnectComplete(int result);
int DoWebSocketSendHello();
int DoWebSocketReceiveHello(int result);
int DoWebSocketKeepIdle();
int DoWebSocketKeepIdleComplete(int result);
int DoWebSocketReceivePushMessage(int result);
int DoWebSocketEchoBackMessage();
int DoWebSocketReceiveBye(int result);
int DoWebSocketClose();
int DoWebSocketCloseComplete(int result);
void SetTimeout(int64 deadline_ms);
void RevokeTimeoutTimer();
void Finish(int result);
Config config_;
scoped_ptr<Context> context_;
Result result_;
MessageLoop* message_loop_;
ScopedRunnableMethodFactory<WebSocketExperimentTask> method_factory_;
net::CompletionCallback* callback_;
State next_state_;
scoped_ptr<URLFetcher> url_fetcher_;
base::TimeTicks url_fetch_start_time_;
scoped_refptr<net::WebSocket> websocket_;
std::deque<std::string> received_messages_;
std::string push_message_;
base::TimeTicks websocket_connect_start_time_;
base::TimeTicks websocket_echo_start_time_;
base::TimeTicks websocket_idle_start_time_;
DISALLOW_COPY_AND_ASSIGN(WebSocketExperimentTask);
};
} // namespace chrome_browser_net
#endif // CHROME_BROWSER_NET_WEBSOCKET_EXPERIMENT_WEBSOCKET_EXPERIMENT_TASK_H_
...@@ -1781,6 +1781,8 @@ ...@@ -1781,6 +1781,8 @@
'browser/net/url_request_slow_http_job.h', 'browser/net/url_request_slow_http_job.h',
'browser/net/url_request_tracking.cc', 'browser/net/url_request_tracking.cc',
'browser/net/url_request_tracking.h', 'browser/net/url_request_tracking.h',
'browser/net/websocket_experiment/websocket_experiment_task.cc',
'browser/net/websocket_experiment/websocket_experiment_task.h',
'browser/notifications/balloons.h', 'browser/notifications/balloons.h',
'browser/notifications/desktop_notification_service.cc', 'browser/notifications/desktop_notification_service.cc',
'browser/notifications/desktop_notification_service.h', 'browser/notifications/desktop_notification_service.h',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment