OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2009 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 #include "chrome/browser/net/websocket_experiment/websocket_experiment_task.h" |
| 6 |
| 7 #include "base/message_loop.h" |
| 8 #include "chrome/browser/net/url_request_context_getter.h" |
| 9 #include "chrome/browser/profile.h" |
| 10 #include "net/websockets/websocket.h" |
| 11 |
| 12 namespace chrome_browser_net_websocket_experiment { |
| 13 |
| 14 URLFetcher* WebSocketExperimentTask::Context::CreateURLFetcher() { |
| 15 return new URLFetcher(config_.http_url, URLFetcher::HEAD, task_); |
| 16 } |
| 17 |
| 18 net::WebSocket* WebSocketExperimentTask::Context::CreateWebSocket() { |
| 19 URLRequestContextGetter* getter = |
| 20 Profile::GetDefaultRequestContext(); |
| 21 DCHECK(getter); |
| 22 net::WebSocket::Request* request( |
| 23 new net::WebSocket::Request(config_.url, |
| 24 config_.ws_protocol, |
| 25 config_.ws_origin, |
| 26 config_.ws_location, |
| 27 getter->GetURLRequestContext())); |
| 28 return new net::WebSocket(request, task_); |
| 29 } |
| 30 |
| 31 WebSocketExperimentTask::WebSocketExperimentTask( |
| 32 const Config& config, |
| 33 net::CompletionCallback* callback) |
| 34 : config_(config), |
| 35 context_(ALLOW_THIS_IN_INITIALIZER_LIST(new Context(config, this))), |
| 36 message_loop_(MessageLoopForIO::current()), |
| 37 method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), |
| 38 callback_(callback), |
| 39 next_state_(STATE_NONE) { |
| 40 DCHECK(message_loop_); |
| 41 } |
| 42 |
| 43 WebSocketExperimentTask::~WebSocketExperimentTask() { |
| 44 DCHECK(!websocket_); |
| 45 } |
| 46 |
| 47 void WebSocketExperimentTask::Run() { |
| 48 next_state_ = STATE_URL_FETCH; |
| 49 DoLoop(net::OK); |
| 50 } |
| 51 |
| 52 // URLFetcher::Delegate method. |
| 53 void WebSocketExperimentTask::OnURLFetchComplete( |
| 54 const URLFetcher* source, |
| 55 const GURL& url, |
| 56 const URLRequestStatus& status, |
| 57 int response_code, |
| 58 const ResponseCookies& cookies, |
| 59 const std::string& data) { |
| 60 result_.url_fetch = base::TimeTicks::Now() - url_fetch_start_time_; |
| 61 RevokeTimeoutTimer(); |
| 62 int result = net::ERR_FAILED; |
| 63 if (next_state_ != STATE_URL_FETCH_COMPLETE) |
| 64 result = net::ERR_UNEXPECTED; |
| 65 else if (response_code == 200) |
| 66 result = net::OK; |
| 67 DoLoop(result); |
| 68 } |
| 69 |
| 70 // net::WebSocketDelegate |
| 71 void WebSocketExperimentTask::OnOpen(net::WebSocket* websocket) { |
| 72 result_.websocket_connect = |
| 73 base::TimeTicks::Now() - websocket_connect_start_time_; |
| 74 RevokeTimeoutTimer(); |
| 75 if (next_state_ == STATE_WEBSOCKET_CONNECT_COMPLETE) |
| 76 DoLoop(net::OK); |
| 77 else |
| 78 DoLoop(net::ERR_UNEXPECTED); |
| 79 } |
| 80 |
| 81 void WebSocketExperimentTask::OnMessage( |
| 82 net::WebSocket* websocket, const std::string& msg) { |
| 83 if (!result_.websocket_echo.ToInternalValue()) |
| 84 result_.websocket_echo = |
| 85 base::TimeTicks::Now() - websocket_echo_start_time_; |
| 86 if (!websocket_idle_start_time_.is_null() && |
| 87 !result_.websocket_idle.ToInternalValue()) |
| 88 result_.websocket_idle = |
| 89 base::TimeTicks::Now() - websocket_idle_start_time_; |
| 90 RevokeTimeoutTimer(); |
| 91 received_messages_.push_back(msg); |
| 92 int result = net::ERR_UNEXPECTED; |
| 93 switch (next_state_) { |
| 94 case STATE_WEBSOCKET_RECV_HELLO: |
| 95 case STATE_WEBSOCKET_RECV_PUSH_MESSAGE: |
| 96 case STATE_WEBSOCKET_RECV_BYE: |
| 97 result = net::OK; |
| 98 break; |
| 99 default: |
| 100 break; |
| 101 } |
| 102 DoLoop(result); |
| 103 } |
| 104 |
| 105 void WebSocketExperimentTask::OnClose(net::WebSocket* websocket) { |
| 106 RevokeTimeoutTimer(); |
| 107 int result = net::ERR_CONNECTION_CLOSED; |
| 108 if (next_state_ == STATE_WEBSOCKET_CLOSE_COMPLETE) |
| 109 result = net::OK; |
| 110 DoLoop(result); |
| 111 } |
| 112 |
| 113 void WebSocketExperimentTask::SetContext(Context* context) { |
| 114 context_.reset(context); |
| 115 } |
| 116 |
| 117 void WebSocketExperimentTask::OnTimedOut() { |
| 118 RevokeTimeoutTimer(); |
| 119 DoLoop(net::ERR_TIMED_OUT); |
| 120 } |
| 121 |
| 122 void WebSocketExperimentTask::DoLoop(int result) { |
| 123 do { |
| 124 State state = next_state_; |
| 125 next_state_ = STATE_NONE; |
| 126 switch (state) { |
| 127 case STATE_URL_FETCH: |
| 128 result = DoURLFetch(); |
| 129 break; |
| 130 case STATE_URL_FETCH_COMPLETE: |
| 131 result = DoURLFetchComplete(result); |
| 132 break; |
| 133 case STATE_WEBSOCKET_CONNECT: |
| 134 result = DoWebSocketConnect(); |
| 135 break; |
| 136 case STATE_WEBSOCKET_CONNECT_COMPLETE: |
| 137 result = DoWebSocketConnectComplete(result); |
| 138 break; |
| 139 case STATE_WEBSOCKET_SEND_HELLO: |
| 140 result = DoWebSocketSendHello(); |
| 141 break; |
| 142 case STATE_WEBSOCKET_RECV_HELLO: |
| 143 result = DoWebSocketReceiveHello(result); |
| 144 break; |
| 145 case STATE_WEBSOCKET_KEEP_IDLE: |
| 146 result = DoWebSocketKeepIdle(); |
| 147 break; |
| 148 case STATE_WEBSOCKET_KEEP_IDLE_COMPLETE: |
| 149 result = DoWebSocketKeepIdleComplete(result); |
| 150 break; |
| 151 case STATE_WEBSOCKET_RECV_PUSH_MESSAGE: |
| 152 result = DoWebSocketReceivePushMessage(result); |
| 153 break; |
| 154 case STATE_WEBSOCKET_ECHO_BACK_MESSAGE: |
| 155 result = DoWebSocketEchoBackMessage(); |
| 156 break; |
| 157 case STATE_WEBSOCKET_RECV_BYE: |
| 158 result = DoWebSocketReceiveBye(result); |
| 159 break; |
| 160 case STATE_WEBSOCKET_CLOSE: |
| 161 result = DoWebSocketClose(); |
| 162 break; |
| 163 case STATE_WEBSOCKET_CLOSE_COMPLETE: |
| 164 result = DoWebSocketCloseComplete(result); |
| 165 break; |
| 166 default: |
| 167 NOTREACHED(); |
| 168 break; |
| 169 } |
| 170 } while (result != net::ERR_IO_PENDING && next_state_ != STATE_NONE); |
| 171 |
| 172 if (result != net::ERR_IO_PENDING) |
| 173 Finish(result); |
| 174 } |
| 175 |
| 176 int WebSocketExperimentTask::DoURLFetch() { |
| 177 next_state_ = STATE_URL_FETCH_COMPLETE; |
| 178 DCHECK(!url_fetcher_.get()); |
| 179 |
| 180 url_fetcher_.reset(context_->CreateURLFetcher()); |
| 181 url_fetch_start_time_ = base::TimeTicks::Now(); |
| 182 url_fetcher_->Start(); |
| 183 |
| 184 SetTimeout(config_.url_fetch_deadline_ms); |
| 185 return net::ERR_IO_PENDING; |
| 186 } |
| 187 |
| 188 int WebSocketExperimentTask::DoURLFetchComplete(int result) { |
| 189 url_fetcher_.reset(); |
| 190 |
| 191 if (result < 0) |
| 192 return result; |
| 193 |
| 194 next_state_ = STATE_WEBSOCKET_CONNECT; |
| 195 return net::OK; |
| 196 } |
| 197 |
| 198 int WebSocketExperimentTask::DoWebSocketConnect() { |
| 199 DCHECK(!websocket_); |
| 200 |
| 201 next_state_ = STATE_WEBSOCKET_CONNECT_COMPLETE; |
| 202 websocket_ = context_->CreateWebSocket(); |
| 203 websocket_connect_start_time_ = base::TimeTicks::Now(); |
| 204 websocket_->Connect(); |
| 205 |
| 206 SetTimeout(config_.websocket_onopen_deadline_ms); |
| 207 return net::ERR_IO_PENDING; |
| 208 } |
| 209 |
| 210 int WebSocketExperimentTask::DoWebSocketConnectComplete(int result) { |
| 211 if (result < 0) |
| 212 return result; |
| 213 DCHECK(websocket_); |
| 214 |
| 215 next_state_ = STATE_WEBSOCKET_SEND_HELLO; |
| 216 return net::OK; |
| 217 } |
| 218 |
| 219 int WebSocketExperimentTask::DoWebSocketSendHello() { |
| 220 DCHECK(websocket_); |
| 221 |
| 222 next_state_ = STATE_WEBSOCKET_RECV_HELLO; |
| 223 |
| 224 websocket_echo_start_time_ = base::TimeTicks::Now(); |
| 225 websocket_->Send(config_.websocket_hello_message); |
| 226 SetTimeout(config_.websocket_hello_echoback_deadline_ms); |
| 227 return net::ERR_IO_PENDING; |
| 228 } |
| 229 |
| 230 int WebSocketExperimentTask::DoWebSocketReceiveHello(int result) { |
| 231 if (result < 0) |
| 232 return result; |
| 233 |
| 234 DCHECK(websocket_); |
| 235 |
| 236 if (received_messages_.size() != 1) |
| 237 return net::ERR_INVALID_RESPONSE; |
| 238 |
| 239 std::string msg = received_messages_.front(); |
| 240 received_messages_.pop_front(); |
| 241 if (msg != config_.websocket_hello_message) |
| 242 return net::ERR_INVALID_RESPONSE; |
| 243 |
| 244 next_state_ = STATE_WEBSOCKET_KEEP_IDLE; |
| 245 return net::OK; |
| 246 } |
| 247 |
| 248 int WebSocketExperimentTask::DoWebSocketKeepIdle() { |
| 249 DCHECK(websocket_); |
| 250 |
| 251 next_state_ = STATE_WEBSOCKET_KEEP_IDLE_COMPLETE; |
| 252 websocket_idle_start_time_ = base::TimeTicks::Now(); |
| 253 SetTimeout(config_.websocket_idle_ms); |
| 254 return net::ERR_IO_PENDING; |
| 255 } |
| 256 |
| 257 int WebSocketExperimentTask::DoWebSocketKeepIdleComplete(int result) { |
| 258 if (result != net::ERR_TIMED_OUT) { |
| 259 // Server sends back too early, or unexpected close? |
| 260 if (result == net::OK) |
| 261 result = net::ERR_UNEXPECTED; |
| 262 return result; |
| 263 } |
| 264 |
| 265 DCHECK(websocket_); |
| 266 |
| 267 next_state_ = STATE_WEBSOCKET_RECV_PUSH_MESSAGE; |
| 268 SetTimeout(config_.websocket_receive_push_message_deadline_ms); |
| 269 return net::ERR_IO_PENDING; |
| 270 } |
| 271 |
| 272 int WebSocketExperimentTask::DoWebSocketReceivePushMessage(int result) { |
| 273 if (result < 0) |
| 274 return result; |
| 275 |
| 276 DCHECK(websocket_); |
| 277 if (received_messages_.size() != 1) |
| 278 return net::ERR_INVALID_RESPONSE; |
| 279 |
| 280 push_message_ = received_messages_.front(); |
| 281 received_messages_.pop_front(); |
| 282 |
| 283 next_state_ = STATE_WEBSOCKET_ECHO_BACK_MESSAGE; |
| 284 return net::OK; |
| 285 } |
| 286 |
| 287 int WebSocketExperimentTask::DoWebSocketEchoBackMessage() { |
| 288 DCHECK(websocket_); |
| 289 DCHECK(!push_message_.empty()); |
| 290 |
| 291 next_state_ = STATE_WEBSOCKET_RECV_BYE; |
| 292 websocket_->Send(push_message_); |
| 293 SetTimeout(config_.websocket_bye_deadline_ms); |
| 294 return net::ERR_IO_PENDING; |
| 295 } |
| 296 |
| 297 int WebSocketExperimentTask::DoWebSocketReceiveBye(int result) { |
| 298 if (result < 0) |
| 299 return result; |
| 300 |
| 301 DCHECK(websocket_); |
| 302 |
| 303 if (received_messages_.size() != 1) |
| 304 return net::ERR_INVALID_RESPONSE; |
| 305 |
| 306 std::string bye = received_messages_.front(); |
| 307 received_messages_.pop_front(); |
| 308 |
| 309 if (bye != config_.websocket_bye_message) |
| 310 return net::ERR_INVALID_RESPONSE; |
| 311 |
| 312 next_state_ = STATE_WEBSOCKET_CLOSE; |
| 313 return net::OK; |
| 314 } |
| 315 |
| 316 int WebSocketExperimentTask::DoWebSocketClose() { |
| 317 DCHECK(websocket_); |
| 318 |
| 319 next_state_ = STATE_WEBSOCKET_CLOSE_COMPLETE; |
| 320 websocket_->Close(); |
| 321 SetTimeout(config_.websocket_close_deadline_ms); |
| 322 return net::ERR_IO_PENDING; |
| 323 } |
| 324 |
| 325 int WebSocketExperimentTask::DoWebSocketCloseComplete(int result) { |
| 326 websocket_ = NULL; |
| 327 return result; |
| 328 } |
| 329 |
| 330 void WebSocketExperimentTask::SetTimeout(int64 deadline_ms) { |
| 331 message_loop_->PostDelayedTask( |
| 332 FROM_HERE, |
| 333 method_factory_.NewRunnableMethod(&WebSocketExperimentTask::OnTimedOut), |
| 334 deadline_ms); |
| 335 } |
| 336 |
| 337 void WebSocketExperimentTask::RevokeTimeoutTimer() { |
| 338 method_factory_.RevokeAll(); |
| 339 } |
| 340 |
| 341 void WebSocketExperimentTask::Finish(int result) { |
| 342 callback_->Run(result); |
| 343 } |
| 344 |
| 345 } // namespace chrome_browser_net |
OLD | NEW |