1// Copyright (c) 2011 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// WebSocket live experiment task.
6// It will try the following scenario.
7//
8//  - Fetch |http_url| within |url_fetch_deadline_ms| msec.
9//    If failed, the task is aborted (no http reachability)
10//
11//  - Connect to |url| with WebSocket protocol within
12//    |websocket_onopen_deadline_ms| msec.
13//    Checks WebSocket connection can be established.
14//
15//  - Send |websocket_hello_message| on the WebSocket connection and
16//    wait it from server within |websocket_hello_echoback_deadline_ms| msec.
17//    Checks message can be sent/received on the WebSocket connection.
18//
19//  - Keep connection idle at least |websocket_idle_ms| msec.
20//    Checks WebSocket connection keep open in idle state.
21//
22//  - Wait for some message from server within
23//    |websocket_receive_push_message_deadline_ms| msec, and echo it back.
24//    Checks server can push a message after connection has been idle.
25//
26//  - Expect that |websocket_bye_message| message arrives within
27//    |websocket_bye_deadline_ms| msec from server.
28//    Checks previous message was sent to the server.
29//
30//  - Close the connection and wait |websocket_close_deadline_ms| msec
31//    for onclose.
32//    Checks WebSocket connection can be closed normally.
33
34#ifndef CHROME_BROWSER_NET_WEBSOCKET_EXPERIMENT_WEBSOCKET_EXPERIMENT_TASK_H_
35#define CHROME_BROWSER_NET_WEBSOCKET_EXPERIMENT_WEBSOCKET_EXPERIMENT_TASK_H_
36#pragma once
37
38#include <deque>
39#include <string>
40
41#include "base/basictypes.h"
42#include "base/task.h"
43#include "base/time.h"
44#include "chrome/common/net/url_fetcher.h"
45#include "googleurl/src/gurl.h"
46#include "net/base/completion_callback.h"
47#include "net/base/net_errors.h"
48#include "net/websockets/websocket.h"
49
50namespace net {
51class WebSocket;
52}  // namespace net
53
54namespace chrome_browser_net_websocket_experiment {
55
56class WebSocketExperimentTask : public URLFetcher::Delegate,
57                                public net::WebSocketDelegate {
58 public:
59  enum State {
60    STATE_NONE,
61    STATE_URL_FETCH,
62    STATE_URL_FETCH_COMPLETE,
63    STATE_WEBSOCKET_CONNECT,
64    STATE_WEBSOCKET_CONNECT_COMPLETE,
65    STATE_WEBSOCKET_SEND_HELLO,
66    STATE_WEBSOCKET_RECV_HELLO,
67    STATE_WEBSOCKET_KEEP_IDLE,
68    STATE_WEBSOCKET_KEEP_IDLE_COMPLETE,
69    STATE_WEBSOCKET_RECV_PUSH_MESSAGE,
70    STATE_WEBSOCKET_ECHO_BACK_MESSAGE,
71    STATE_WEBSOCKET_RECV_BYE,
72    STATE_WEBSOCKET_CLOSE,
73    STATE_WEBSOCKET_CLOSE_COMPLETE,
74    NUM_STATES,
75  };
76
77  class Config {
78   public:
79    Config();
80    ~Config();
81
82    GURL url;
83    std::string ws_protocol;
84    std::string ws_origin;
85    std::string ws_location;
86    net::WebSocket::ProtocolVersion protocol_version;
87
88    GURL http_url;
89
90    int64 url_fetch_deadline_ms;
91    int64 websocket_onopen_deadline_ms;
92    std::string websocket_hello_message;
93    int64 websocket_hello_echoback_deadline_ms;
94    int64 websocket_idle_ms;
95    int64 websocket_receive_push_message_deadline_ms;
96    std::string websocket_bye_message;
97    int64 websocket_bye_deadline_ms;
98    int64 websocket_close_deadline_ms;
99  };
100
101  class Context {
102   public:
103    Context() {}
104    virtual ~Context() {}
105
106    virtual URLFetcher* CreateURLFetcher(
107        const Config& config, URLFetcher::Delegate* delegate);
108    virtual net::WebSocket* CreateWebSocket(
109        const Config& config, net::WebSocketDelegate* delegate);
110
111   private:
112    DISALLOW_COPY_AND_ASSIGN(Context);
113  };
114
115  class Result {
116   public:
117    Result()
118        : last_result(net::OK),
119          last_state(STATE_NONE) {}
120    int last_result;
121    State last_state;
122
123    base::TimeDelta url_fetch;
124    base::TimeDelta websocket_connect;
125    base::TimeDelta websocket_echo;
126    base::TimeDelta websocket_idle;
127    base::TimeDelta websocket_total;
128  };
129
130  // WebSocketExperimentTask will call |callback| with the last status code
131  // when the task is finished.
132  WebSocketExperimentTask(const Config& config,
133                          net::CompletionCallback* callback);
134  virtual ~WebSocketExperimentTask();
135
136  // Initializes histograms that WebSocketExperimentTask will use to save
137  // results.  Must be called once before calling SaveResult().
138  static void InitHistogram();
139
140  // Releases histograms to store results.
141  // Must be called after all WebSocketExperimentTasks are finished.
142  static void ReleaseHistogram();
143
144  void Run();
145  void Cancel();
146  void SaveResult() const;
147
148  const Config& config() const { return config_; }
149  const Result& result() const { return result_; }
150
151  // URLFetcher::Delegate method.
152  virtual void OnURLFetchComplete(const URLFetcher* source,
153                                  const GURL& url,
154                                  const net::URLRequestStatus& status,
155                                  int response_code,
156                                  const ResponseCookies& cookies,
157                                  const std::string& data);
158
159  // net::WebSocketDelegate methods
160  virtual void OnOpen(net::WebSocket* websocket);
161  virtual void OnMessage(net::WebSocket* websocket, const std::string& msg);
162  virtual void OnError(net::WebSocket* websocket);
163  virtual void OnClose(net::WebSocket* websocket, bool was_clean);
164  virtual void OnSocketError(const net::WebSocket* websocket, int error);
165
166  void SetContext(Context* context);
167
168 private:
169  void OnTimedOut();
170
171  void DoLoop(int result);
172
173  int DoURLFetch();
174  int DoURLFetchComplete(int result);
175  int DoWebSocketConnect();
176  int DoWebSocketConnectComplete(int result);
177  int DoWebSocketSendHello();
178  int DoWebSocketReceiveHello(int result);
179  int DoWebSocketKeepIdle();
180  int DoWebSocketKeepIdleComplete(int result);
181  int DoWebSocketReceivePushMessage(int result);
182  int DoWebSocketEchoBackMessage();
183  int DoWebSocketReceiveBye(int result);
184  int DoWebSocketClose();
185  int DoWebSocketCloseComplete(int result);
186  void SetTimeout(int64 deadline_ms);
187  void RevokeTimeoutTimer();
188  void Finish(int result);
189
190  Config config_;
191  scoped_ptr<Context> context_;
192  Result result_;
193
194  ScopedRunnableMethodFactory<WebSocketExperimentTask> method_factory_;
195  net::CompletionCallback* callback_;
196  State next_state_;
197
198  scoped_ptr<URLFetcher> url_fetcher_;
199  base::TimeTicks url_fetch_start_time_;
200
201  scoped_refptr<net::WebSocket> websocket_;
202  int last_websocket_error_;
203  std::deque<std::string> received_messages_;
204  std::string push_message_;
205  base::TimeTicks websocket_connect_start_time_;
206  base::TimeTicks websocket_echo_start_time_;
207  base::TimeTicks websocket_idle_start_time_;
208
209  DISALLOW_COPY_AND_ASSIGN(WebSocketExperimentTask);
210};
211
212}  // namespace chrome_browser_net
213
214#endif  // CHROME_BROWSER_NET_WEBSOCKET_EXPERIMENT_WEBSOCKET_EXPERIMENT_TASK_H_
215