OLD | NEW |
| (Empty) |
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 #include "chrome/browser/net/websocket_experiment/websocket_experiment_task.h" | |
6 | |
7 #include "base/hash_tables.h" | |
8 #include "base/metrics/histogram.h" | |
9 #include "chrome/browser/profiles/profile.h" | |
10 #include "content/browser/browser_thread.h" | |
11 #include "net/base/host_resolver.h" | |
12 #include "net/base/load_flags.h" | |
13 #include "net/base/net_errors.h" | |
14 #include "net/url_request/url_request_context_getter.h" | |
15 #include "net/websockets/websocket.h" | |
16 | |
17 using base::Histogram; | |
18 using base::LinearHistogram; | |
19 | |
20 namespace chrome_browser_net_websocket_experiment { | |
21 | |
22 static std::string GetProtocolVersionName( | |
23 net::WebSocket::ProtocolVersion protocol_version) { | |
24 switch (protocol_version) { | |
25 case net::WebSocket::DEFAULT_VERSION: | |
26 return "default protocol"; | |
27 case net::WebSocket::DRAFT75: | |
28 return "draft 75 protocol"; | |
29 default: | |
30 NOTREACHED(); | |
31 } | |
32 return ""; | |
33 } | |
34 | |
35 URLFetcher* WebSocketExperimentTask::Context::CreateURLFetcher( | |
36 const Config& config, URLFetcher::Delegate* delegate) { | |
37 net::URLRequestContextGetter* getter = | |
38 Profile::Deprecated::GetDefaultRequestContext(); | |
39 // Profile::Deprecated::GetDefaultRequestContext() is initialized lazily, on | |
40 // the UI thread. So here, where we access it from the IO thread, if the task | |
41 // runs before it has gotten lazily initialized yet. | |
42 if (!getter) | |
43 return NULL; | |
44 URLFetcher* fetcher = | |
45 new URLFetcher(config.http_url, URLFetcher::GET, delegate); | |
46 fetcher->set_request_context(getter); | |
47 fetcher->set_load_flags( | |
48 net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE | | |
49 net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SEND_AUTH_DATA | | |
50 net::LOAD_IGNORE_CERT_AUTHORITY_INVALID); | |
51 return fetcher; | |
52 } | |
53 | |
54 net::WebSocket* WebSocketExperimentTask::Context::CreateWebSocket( | |
55 const Config& config, net::WebSocketDelegate* delegate) { | |
56 net::URLRequestContextGetter* getter = | |
57 Profile::Deprecated::GetDefaultRequestContext(); | |
58 // Profile::Deprecated::GetDefaultRequestContext() is initialized lazily, on | |
59 // the UI thread. So here, where we access it from the IO thread, if the task | |
60 // runs before it has gotten lazily initialized yet. | |
61 if (!getter) | |
62 return NULL; | |
63 net::WebSocket::Request* request( | |
64 new net::WebSocket::Request(config.url, | |
65 config.ws_protocol, | |
66 config.ws_origin, | |
67 config.ws_location, | |
68 config.protocol_version, | |
69 getter->GetURLRequestContext())); | |
70 return new net::WebSocket(request, delegate); | |
71 } | |
72 | |
73 // Expects URL Fetch and WebSocket connection handshake will finish in | |
74 // a few seconds. | |
75 static const int kUrlFetchDeadlineSec = 10; | |
76 static const int kWebSocketConnectDeadlineSec = 10; | |
77 // Expects WebSocket live experiment server echoes message back within a few | |
78 // seconds. | |
79 static const int kWebSocketEchoDeadlineSec = 5; | |
80 // WebSocket live experiment server keeps idle for 1.5 seconds and sends | |
81 // a message. So, expects idle for at least 1 second and expects message | |
82 // arrives within 1 second after that. | |
83 static const int kWebSocketIdleSec = 1; | |
84 static const int kWebSocketPushDeadlineSec = 1; | |
85 // WebSocket live experiment server sends "bye" message soon. | |
86 static const int kWebSocketByeDeadlineSec = 10; | |
87 // WebSocket live experiment server closes after it receives "bye" message. | |
88 static const int kWebSocketCloseDeadlineSec = 5; | |
89 | |
90 // All of above are expected within a few seconds. We'd like to see time | |
91 // distribution between 0 to 10 seconds. | |
92 static const int kWebSocketTimeSec = 10; | |
93 static const int kTimeBucketCount = 50; | |
94 | |
95 // Holds Histogram objects during experiments run. | |
96 static base::hash_map<std::string, Histogram*>* g_histogram_table; | |
97 | |
98 WebSocketExperimentTask::Config::Config() | |
99 : ws_protocol("google-websocket-liveexperiment"), | |
100 ws_origin("http://dev.chromium.org/"), | |
101 protocol_version(net::WebSocket::DEFAULT_VERSION), | |
102 url_fetch_deadline_ms(kUrlFetchDeadlineSec * 1000), | |
103 websocket_onopen_deadline_ms(kWebSocketConnectDeadlineSec * 1000), | |
104 websocket_hello_message("Hello"), | |
105 websocket_hello_echoback_deadline_ms(kWebSocketEchoDeadlineSec * 1000), | |
106 // Note: websocket live experiment server is configured to wait 1.5 sec | |
107 // in websocket_experiment_def.txt on server. So, client expects idle | |
108 // at least 1 sec and expects a message arrival within next 1 sec. | |
109 websocket_idle_ms(kWebSocketIdleSec * 1000), | |
110 websocket_receive_push_message_deadline_ms( | |
111 kWebSocketPushDeadlineSec * 1000), | |
112 websocket_bye_message("Bye"), | |
113 websocket_bye_deadline_ms(kWebSocketByeDeadlineSec * 1000), | |
114 websocket_close_deadline_ms(kWebSocketCloseDeadlineSec * 1000) { | |
115 } | |
116 | |
117 WebSocketExperimentTask::Config::~Config() {} | |
118 | |
119 WebSocketExperimentTask::WebSocketExperimentTask( | |
120 const Config& config, | |
121 net::CompletionCallback* callback) | |
122 : config_(config), | |
123 context_(ALLOW_THIS_IN_INITIALIZER_LIST(new Context())), | |
124 method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), | |
125 callback_(callback), | |
126 next_state_(STATE_NONE), | |
127 last_websocket_error_(net::OK) { | |
128 } | |
129 | |
130 WebSocketExperimentTask::~WebSocketExperimentTask() { | |
131 DCHECK(!websocket_); | |
132 } | |
133 | |
134 /* static */ | |
135 void WebSocketExperimentTask::InitHistogram() { | |
136 DCHECK(!g_histogram_table); | |
137 g_histogram_table = new base::hash_map<std::string, Histogram*>; | |
138 } | |
139 | |
140 static std::string GetCounterNameForConfig( | |
141 const WebSocketExperimentTask::Config& config, const std::string& name) { | |
142 std::string protocol_version = ""; | |
143 switch (config.protocol_version) { | |
144 case net::WebSocket::DEFAULT_VERSION: | |
145 protocol_version = "Draft76"; | |
146 break; | |
147 case net::WebSocket::DRAFT75: | |
148 protocol_version = ""; | |
149 break; | |
150 default: | |
151 NOTREACHED(); | |
152 } | |
153 if (config.url.SchemeIs("wss")) { | |
154 return "WebSocketExperiment.Secure" + protocol_version + "." + name; | |
155 } else if (config.url.has_port() && config.url.IntPort() != 80) { | |
156 return "WebSocketExperiment.NoDefaultPort" + protocol_version + "." + name; | |
157 } else { | |
158 return "WebSocketExperiment.Basic" + protocol_version + "." + name; | |
159 } | |
160 } | |
161 | |
162 static Histogram* GetEnumsHistogramForConfig( | |
163 const WebSocketExperimentTask::Config& config, | |
164 const std::string& name, | |
165 Histogram::Sample boundary_value) { | |
166 DCHECK(g_histogram_table); | |
167 std::string counter_name = GetCounterNameForConfig(config, name); | |
168 base::hash_map<std::string, Histogram*>::iterator found = | |
169 g_histogram_table->find(counter_name); | |
170 if (found != g_histogram_table->end()) { | |
171 return found->second; | |
172 } | |
173 Histogram* counter = LinearHistogram::FactoryGet( | |
174 counter_name, 1, boundary_value, boundary_value + 1, | |
175 Histogram::kUmaTargetedHistogramFlag); | |
176 g_histogram_table->insert(std::make_pair(counter_name, counter)); | |
177 return counter; | |
178 } | |
179 | |
180 static Histogram* GetTimesHistogramForConfig( | |
181 const WebSocketExperimentTask::Config& config, | |
182 const std::string& name, | |
183 base::TimeDelta min, | |
184 base::TimeDelta max, | |
185 size_t bucket_count) { | |
186 DCHECK(g_histogram_table); | |
187 std::string counter_name = GetCounterNameForConfig(config, name); | |
188 base::hash_map<std::string, Histogram*>::iterator found = | |
189 g_histogram_table->find(counter_name); | |
190 if (found != g_histogram_table->end()) { | |
191 return found->second; | |
192 } | |
193 Histogram* counter = Histogram::FactoryTimeGet( | |
194 counter_name, min, max, bucket_count, | |
195 Histogram::kUmaTargetedHistogramFlag); | |
196 g_histogram_table->insert(std::make_pair(counter_name, counter)); | |
197 return counter; | |
198 } | |
199 | |
200 static void UpdateHistogramEnums( | |
201 const WebSocketExperimentTask::Config& config, | |
202 const std::string& name, | |
203 Histogram::Sample sample, | |
204 Histogram::Sample boundary_value) { | |
205 Histogram* counter = GetEnumsHistogramForConfig(config, name, boundary_value); | |
206 counter->Add(sample); | |
207 } | |
208 | |
209 static void UpdateHistogramTimes( | |
210 const WebSocketExperimentTask::Config& config, | |
211 const std::string& name, | |
212 base::TimeDelta sample, | |
213 base::TimeDelta min, | |
214 base::TimeDelta max, | |
215 size_t bucket_count) { | |
216 Histogram* counter = GetTimesHistogramForConfig( | |
217 config, name, min, max, bucket_count); | |
218 counter->AddTime(sample); | |
219 } | |
220 | |
221 /* static */ | |
222 void WebSocketExperimentTask::ReleaseHistogram() { | |
223 DCHECK(g_histogram_table); | |
224 delete g_histogram_table; | |
225 g_histogram_table = NULL; | |
226 } | |
227 | |
228 void WebSocketExperimentTask::Run() { | |
229 DVLOG(1) << "Run WebSocket experiment for " << config_.url << " " | |
230 << GetProtocolVersionName(config_.protocol_version); | |
231 next_state_ = STATE_URL_FETCH; | |
232 DoLoop(net::OK); | |
233 } | |
234 | |
235 void WebSocketExperimentTask::Cancel() { | |
236 next_state_ = STATE_NONE; | |
237 DoLoop(net::OK); | |
238 } | |
239 | |
240 void WebSocketExperimentTask::SaveResult() const { | |
241 DVLOG(1) << "WebSocket experiment save result for " << config_.url | |
242 << " last_state=" << result_.last_state; | |
243 UpdateHistogramEnums(config_, "LastState", result_.last_state, NUM_STATES); | |
244 UpdateHistogramTimes(config_, "UrlFetch", result_.url_fetch, | |
245 base::TimeDelta::FromMilliseconds(1), | |
246 base::TimeDelta::FromSeconds(kUrlFetchDeadlineSec), | |
247 kTimeBucketCount); | |
248 | |
249 if (result_.last_state < STATE_WEBSOCKET_CONNECT_COMPLETE) | |
250 return; | |
251 | |
252 UpdateHistogramTimes(config_, "WebSocketConnect", result_.websocket_connect, | |
253 base::TimeDelta::FromMilliseconds(1), | |
254 base::TimeDelta::FromSeconds( | |
255 kWebSocketConnectDeadlineSec), | |
256 kTimeBucketCount); | |
257 | |
258 if (result_.last_state < STATE_WEBSOCKET_RECV_HELLO) | |
259 return; | |
260 | |
261 UpdateHistogramTimes(config_, "WebSocketEcho", result_.websocket_echo, | |
262 base::TimeDelta::FromMilliseconds(1), | |
263 base::TimeDelta::FromSeconds( | |
264 kWebSocketEchoDeadlineSec), | |
265 kTimeBucketCount); | |
266 | |
267 if (result_.last_state < STATE_WEBSOCKET_KEEP_IDLE) | |
268 return; | |
269 | |
270 UpdateHistogramTimes(config_, "WebSocketIdle", result_.websocket_idle, | |
271 base::TimeDelta::FromMilliseconds(1), | |
272 base::TimeDelta::FromSeconds( | |
273 kWebSocketIdleSec + kWebSocketPushDeadlineSec), | |
274 kTimeBucketCount); | |
275 | |
276 if (result_.last_state < STATE_WEBSOCKET_CLOSE_COMPLETE) | |
277 return; | |
278 | |
279 UpdateHistogramTimes(config_, "WebSocketTotal", result_.websocket_total, | |
280 base::TimeDelta::FromMilliseconds(1), | |
281 base::TimeDelta::FromSeconds(kWebSocketTimeSec), | |
282 kTimeBucketCount); | |
283 } | |
284 | |
285 // URLFetcher::Delegate method. | |
286 void WebSocketExperimentTask::OnURLFetchComplete( | |
287 const URLFetcher* source, | |
288 const GURL& url, | |
289 const net::URLRequestStatus& status, | |
290 int response_code, | |
291 const net::ResponseCookies& cookies, | |
292 const std::string& data) { | |
293 result_.url_fetch = base::TimeTicks::Now() - url_fetch_start_time_; | |
294 RevokeTimeoutTimer(); | |
295 int result = net::ERR_FAILED; | |
296 if (next_state_ != STATE_URL_FETCH_COMPLETE) { | |
297 DVLOG(1) << "unexpected state=" << next_state_ | |
298 << " at OnURLFetchComplete for " << config_.http_url; | |
299 result = net::ERR_UNEXPECTED; | |
300 } else if (response_code == 200 || response_code == 304) { | |
301 result = net::OK; | |
302 } | |
303 DoLoop(result); | |
304 } | |
305 | |
306 // net::WebSocketDelegate | |
307 void WebSocketExperimentTask::OnOpen(net::WebSocket* websocket) { | |
308 result_.websocket_connect = | |
309 base::TimeTicks::Now() - websocket_connect_start_time_; | |
310 RevokeTimeoutTimer(); | |
311 int result = net::ERR_UNEXPECTED; | |
312 if (next_state_ == STATE_WEBSOCKET_CONNECT_COMPLETE) | |
313 result = net::OK; | |
314 else | |
315 DVLOG(1) << "unexpected state=" << next_state_ | |
316 << " at OnOpen for " << config_.url | |
317 << " " << GetProtocolVersionName(config_.protocol_version); | |
318 DoLoop(result); | |
319 } | |
320 | |
321 void WebSocketExperimentTask::OnMessage( | |
322 net::WebSocket* websocket, const std::string& msg) { | |
323 if (!result_.websocket_echo.ToInternalValue()) | |
324 result_.websocket_echo = | |
325 base::TimeTicks::Now() - websocket_echo_start_time_; | |
326 if (!websocket_idle_start_time_.is_null() && | |
327 !result_.websocket_idle.ToInternalValue()) | |
328 result_.websocket_idle = | |
329 base::TimeTicks::Now() - websocket_idle_start_time_; | |
330 RevokeTimeoutTimer(); | |
331 received_messages_.push_back(msg); | |
332 int result = net::ERR_UNEXPECTED; | |
333 switch (next_state_) { | |
334 case STATE_WEBSOCKET_RECV_HELLO: | |
335 case STATE_WEBSOCKET_RECV_PUSH_MESSAGE: | |
336 case STATE_WEBSOCKET_RECV_BYE: | |
337 result = net::OK; | |
338 break; | |
339 default: | |
340 DVLOG(1) << "unexpected state=" << next_state_ | |
341 << " at OnMessage for " << config_.url | |
342 << " " << GetProtocolVersionName(config_.protocol_version); | |
343 break; | |
344 } | |
345 DoLoop(result); | |
346 } | |
347 | |
348 void WebSocketExperimentTask::OnError(net::WebSocket* websocket) { | |
349 // TODO(ukai): record error count? | |
350 } | |
351 | |
352 void WebSocketExperimentTask::OnClose( | |
353 net::WebSocket* websocket, bool was_clean) { | |
354 RevokeTimeoutTimer(); | |
355 websocket_ = NULL; | |
356 result_.websocket_total = | |
357 base::TimeTicks::Now() - websocket_connect_start_time_; | |
358 int result = net::ERR_CONNECTION_CLOSED; | |
359 if (last_websocket_error_ != net::OK) | |
360 result = last_websocket_error_; | |
361 DVLOG(1) << "WebSocket onclose was_clean=" << was_clean | |
362 << " next_state=" << next_state_ | |
363 << " last_error=" << net::ErrorToString(result); | |
364 if (config_.protocol_version == net::WebSocket::DEFAULT_VERSION) { | |
365 if (next_state_ == STATE_WEBSOCKET_CLOSE_COMPLETE && was_clean) | |
366 result = net::OK; | |
367 } else { | |
368 // DRAFT75 doesn't report was_clean correctly. | |
369 if (next_state_ == STATE_WEBSOCKET_CLOSE_COMPLETE) | |
370 result = net::OK; | |
371 } | |
372 DoLoop(result); | |
373 } | |
374 | |
375 void WebSocketExperimentTask::OnSocketError( | |
376 const net::WebSocket* websocket, int error) { | |
377 DVLOG(1) << "WebSocket socket level error=" << net::ErrorToString(error) | |
378 << " next_state=" << next_state_ | |
379 << " for " << config_.url | |
380 << " " << GetProtocolVersionName(config_.protocol_version); | |
381 last_websocket_error_ = error; | |
382 } | |
383 | |
384 void WebSocketExperimentTask::SetContext(Context* context) { | |
385 context_.reset(context); | |
386 } | |
387 | |
388 void WebSocketExperimentTask::OnTimedOut() { | |
389 DVLOG(1) << "OnTimedOut next_state=" << next_state_ | |
390 << " for " << config_.url | |
391 << " " << GetProtocolVersionName(config_.protocol_version); | |
392 RevokeTimeoutTimer(); | |
393 DoLoop(net::ERR_TIMED_OUT); | |
394 } | |
395 | |
396 void WebSocketExperimentTask::DoLoop(int result) { | |
397 if (next_state_ == STATE_NONE) { | |
398 Finish(net::ERR_ABORTED); | |
399 return; | |
400 } | |
401 do { | |
402 State state = next_state_; | |
403 next_state_ = STATE_NONE; | |
404 switch (state) { | |
405 case STATE_URL_FETCH: | |
406 result = DoURLFetch(); | |
407 break; | |
408 case STATE_URL_FETCH_COMPLETE: | |
409 result = DoURLFetchComplete(result); | |
410 break; | |
411 case STATE_WEBSOCKET_CONNECT: | |
412 result = DoWebSocketConnect(); | |
413 break; | |
414 case STATE_WEBSOCKET_CONNECT_COMPLETE: | |
415 result = DoWebSocketConnectComplete(result); | |
416 break; | |
417 case STATE_WEBSOCKET_SEND_HELLO: | |
418 result = DoWebSocketSendHello(); | |
419 break; | |
420 case STATE_WEBSOCKET_RECV_HELLO: | |
421 result = DoWebSocketReceiveHello(result); | |
422 break; | |
423 case STATE_WEBSOCKET_KEEP_IDLE: | |
424 result = DoWebSocketKeepIdle(); | |
425 break; | |
426 case STATE_WEBSOCKET_KEEP_IDLE_COMPLETE: | |
427 result = DoWebSocketKeepIdleComplete(result); | |
428 break; | |
429 case STATE_WEBSOCKET_RECV_PUSH_MESSAGE: | |
430 result = DoWebSocketReceivePushMessage(result); | |
431 break; | |
432 case STATE_WEBSOCKET_ECHO_BACK_MESSAGE: | |
433 result = DoWebSocketEchoBackMessage(); | |
434 break; | |
435 case STATE_WEBSOCKET_RECV_BYE: | |
436 result = DoWebSocketReceiveBye(result); | |
437 break; | |
438 case STATE_WEBSOCKET_CLOSE: | |
439 result = DoWebSocketClose(); | |
440 break; | |
441 case STATE_WEBSOCKET_CLOSE_COMPLETE: | |
442 result = DoWebSocketCloseComplete(result); | |
443 break; | |
444 default: | |
445 NOTREACHED(); | |
446 break; | |
447 } | |
448 result_.last_state = state; | |
449 } while (result != net::ERR_IO_PENDING && next_state_ != STATE_NONE); | |
450 | |
451 if (result != net::ERR_IO_PENDING) | |
452 Finish(result); | |
453 } | |
454 | |
455 int WebSocketExperimentTask::DoURLFetch() { | |
456 DCHECK(!url_fetcher_.get()); | |
457 | |
458 url_fetcher_.reset(context_->CreateURLFetcher(config_, this)); | |
459 if (!url_fetcher_.get()) { | |
460 // Request context is not ready. | |
461 next_state_ = STATE_NONE; | |
462 return net::ERR_UNEXPECTED; | |
463 } | |
464 | |
465 next_state_ = STATE_URL_FETCH_COMPLETE; | |
466 SetTimeout(config_.url_fetch_deadline_ms); | |
467 url_fetch_start_time_ = base::TimeTicks::Now(); | |
468 url_fetcher_->Start(); | |
469 return net::ERR_IO_PENDING; | |
470 } | |
471 | |
472 int WebSocketExperimentTask::DoURLFetchComplete(int result) { | |
473 url_fetcher_.reset(); | |
474 | |
475 if (result < 0) | |
476 return result; | |
477 | |
478 next_state_ = STATE_WEBSOCKET_CONNECT; | |
479 return net::OK; | |
480 } | |
481 | |
482 int WebSocketExperimentTask::DoWebSocketConnect() { | |
483 DCHECK(!websocket_); | |
484 | |
485 websocket_ = context_->CreateWebSocket(config_, this); | |
486 if (!websocket_) { | |
487 // Request context is not ready. | |
488 next_state_ = STATE_NONE; | |
489 return net::ERR_UNEXPECTED; | |
490 } | |
491 next_state_ = STATE_WEBSOCKET_CONNECT_COMPLETE; | |
492 websocket_connect_start_time_ = base::TimeTicks::Now(); | |
493 websocket_->Connect(); | |
494 | |
495 SetTimeout(config_.websocket_onopen_deadline_ms); | |
496 return net::ERR_IO_PENDING; | |
497 } | |
498 | |
499 int WebSocketExperimentTask::DoWebSocketConnectComplete(int result) { | |
500 if (result < 0) | |
501 return result; | |
502 DCHECK(websocket_); | |
503 | |
504 next_state_ = STATE_WEBSOCKET_SEND_HELLO; | |
505 return net::OK; | |
506 } | |
507 | |
508 int WebSocketExperimentTask::DoWebSocketSendHello() { | |
509 DCHECK(websocket_); | |
510 | |
511 next_state_ = STATE_WEBSOCKET_RECV_HELLO; | |
512 | |
513 websocket_echo_start_time_ = base::TimeTicks::Now(); | |
514 websocket_->Send(config_.websocket_hello_message); | |
515 SetTimeout(config_.websocket_hello_echoback_deadline_ms); | |
516 return net::ERR_IO_PENDING; | |
517 } | |
518 | |
519 int WebSocketExperimentTask::DoWebSocketReceiveHello(int result) { | |
520 if (result < 0) | |
521 return result; | |
522 | |
523 DCHECK(websocket_); | |
524 | |
525 if (received_messages_.size() != 1) | |
526 return net::ERR_INVALID_RESPONSE; | |
527 | |
528 std::string msg = received_messages_.front(); | |
529 received_messages_.pop_front(); | |
530 if (msg != config_.websocket_hello_message) | |
531 return net::ERR_INVALID_RESPONSE; | |
532 | |
533 next_state_ = STATE_WEBSOCKET_KEEP_IDLE; | |
534 return net::OK; | |
535 } | |
536 | |
537 int WebSocketExperimentTask::DoWebSocketKeepIdle() { | |
538 DCHECK(websocket_); | |
539 | |
540 next_state_ = STATE_WEBSOCKET_KEEP_IDLE_COMPLETE; | |
541 websocket_idle_start_time_ = base::TimeTicks::Now(); | |
542 SetTimeout(config_.websocket_idle_ms); | |
543 return net::ERR_IO_PENDING; | |
544 } | |
545 | |
546 int WebSocketExperimentTask::DoWebSocketKeepIdleComplete(int result) { | |
547 if (result != net::ERR_TIMED_OUT) { | |
548 // Server sends back too early, or unexpected close? | |
549 if (result == net::OK) | |
550 result = net::ERR_UNEXPECTED; | |
551 return result; | |
552 } | |
553 | |
554 DCHECK(websocket_); | |
555 | |
556 next_state_ = STATE_WEBSOCKET_RECV_PUSH_MESSAGE; | |
557 SetTimeout(config_.websocket_receive_push_message_deadline_ms); | |
558 return net::ERR_IO_PENDING; | |
559 } | |
560 | |
561 int WebSocketExperimentTask::DoWebSocketReceivePushMessage(int result) { | |
562 if (result < 0) | |
563 return result; | |
564 | |
565 DCHECK(websocket_); | |
566 if (received_messages_.size() != 1) | |
567 return net::ERR_INVALID_RESPONSE; | |
568 | |
569 push_message_ = received_messages_.front(); | |
570 received_messages_.pop_front(); | |
571 | |
572 next_state_ = STATE_WEBSOCKET_ECHO_BACK_MESSAGE; | |
573 return net::OK; | |
574 } | |
575 | |
576 int WebSocketExperimentTask::DoWebSocketEchoBackMessage() { | |
577 DCHECK(websocket_); | |
578 DCHECK(!push_message_.empty()); | |
579 | |
580 next_state_ = STATE_WEBSOCKET_RECV_BYE; | |
581 websocket_->Send(push_message_); | |
582 SetTimeout(config_.websocket_bye_deadline_ms); | |
583 return net::ERR_IO_PENDING; | |
584 } | |
585 | |
586 int WebSocketExperimentTask::DoWebSocketReceiveBye(int result) { | |
587 if (result < 0) | |
588 return result; | |
589 | |
590 DCHECK(websocket_); | |
591 | |
592 if (received_messages_.size() != 1) | |
593 return net::ERR_INVALID_RESPONSE; | |
594 | |
595 std::string bye = received_messages_.front(); | |
596 received_messages_.pop_front(); | |
597 | |
598 if (bye != config_.websocket_bye_message) | |
599 return net::ERR_INVALID_RESPONSE; | |
600 | |
601 next_state_ = STATE_WEBSOCKET_CLOSE; | |
602 return net::OK; | |
603 } | |
604 | |
605 int WebSocketExperimentTask::DoWebSocketClose() { | |
606 DCHECK(websocket_); | |
607 | |
608 next_state_ = STATE_WEBSOCKET_CLOSE_COMPLETE; | |
609 websocket_->Close(); | |
610 SetTimeout(config_.websocket_close_deadline_ms); | |
611 return net::ERR_IO_PENDING; | |
612 } | |
613 | |
614 int WebSocketExperimentTask::DoWebSocketCloseComplete(int result) { | |
615 websocket_ = NULL; | |
616 return result; | |
617 } | |
618 | |
619 void WebSocketExperimentTask::SetTimeout(int64 deadline_ms) { | |
620 bool r = BrowserThread::PostDelayedTask( | |
621 BrowserThread::IO, | |
622 FROM_HERE, | |
623 method_factory_.NewRunnableMethod(&WebSocketExperimentTask::OnTimedOut), | |
624 deadline_ms); | |
625 DCHECK(r) << "No IO thread running?"; | |
626 } | |
627 | |
628 void WebSocketExperimentTask::RevokeTimeoutTimer() { | |
629 method_factory_.RevokeAll(); | |
630 } | |
631 | |
632 void WebSocketExperimentTask::Finish(int result) { | |
633 url_fetcher_.reset(); | |
634 scoped_refptr<net::WebSocket> websocket = websocket_; | |
635 websocket_ = NULL; | |
636 if (websocket) | |
637 websocket->DetachDelegate(); | |
638 DVLOG(1) << "Finish WebSocket experiment for " << config_.url | |
639 << " " << GetProtocolVersionName(config_.protocol_version) | |
640 << " next_state=" << next_state_ | |
641 << " result=" << net::ErrorToString(result); | |
642 callback_->Run(result); // will release this. | |
643 } | |
644 | |
645 } // namespace chrome_browser_net | |
OLD | NEW |