// Copyright (c) 2011 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_
#pragma once
#include <deque>
#include <string>
#include "base/basictypes.h"
#include "base/task.h"
#include "base/time.h"
#include "chrome/common/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"
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,
NUM_STATES,
};
class Config {
public:
Config();
~Config();
GURL url;
std::string ws_protocol;
std::string ws_origin;
std::string ws_location;
net::WebSocket::ProtocolVersion protocol_version;
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() {}
virtual ~Context() {}
virtual URLFetcher* CreateURLFetcher(
const Config& config, URLFetcher::Delegate* delegate);
virtual net::WebSocket* CreateWebSocket(
const Config& config, net::WebSocketDelegate* delegate);
private:
DISALLOW_COPY_AND_ASSIGN(Context);
};
class Result {
public:
Result()
: last_result(net::OK),
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;
base::TimeDelta websocket_total;
};
// WebSocketExperimentTask will call |callback| with the last status code
// when the task is finished.
WebSocketExperimentTask(const Config& config,
net::CompletionCallback* callback);
virtual ~WebSocketExperimentTask();
// Initializes histograms that WebSocketExperimentTask will use to save
// results. Must be called once before calling SaveResult().
static void InitHistogram();
// Releases histograms to store results.
// Must be called after all WebSocketExperimentTasks are finished.
static void ReleaseHistogram();
void Run();
void Cancel();
void SaveResult() const;
const Config& config() const { return config_; }
const Result& result() const { return result_; }
// URLFetcher::Delegate method.
virtual void OnURLFetchComplete(const URLFetcher* source,
const GURL& url,
const net::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 OnError(net::WebSocket* websocket);
virtual void OnClose(net::WebSocket* websocket, bool was_clean);
virtual void OnSocketError(const net::WebSocket* websocket, int error);
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_;
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_;
int last_websocket_error_;
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_