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