• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
GetProtocolVersionName(net::WebSocket::ProtocolVersion protocol_version)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 
CreateURLFetcher(const Config & config,URLFetcher::Delegate * delegate)35 URLFetcher* WebSocketExperimentTask::Context::CreateURLFetcher(
36     const Config& config, URLFetcher::Delegate* delegate) {
37   net::URLRequestContextGetter* getter =
38       Profile::GetDefaultRequestContext();
39   // Profile::GetDefaultRequestContext() is initialized lazily, on the UI
40   // thread. So here, where we access it from the IO thread, if the task runs
41   // 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 
CreateWebSocket(const Config & config,net::WebSocketDelegate * delegate)54 net::WebSocket* WebSocketExperimentTask::Context::CreateWebSocket(
55     const Config& config, net::WebSocketDelegate* delegate) {
56   net::URLRequestContextGetter* getter =
57       Profile::GetDefaultRequestContext();
58   // Profile::GetDefaultRequestContext() is initialized lazily, on the UI
59   // thread. So here, where we access it from the IO thread, if the task runs
60   // 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 
Config()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 
~Config()117 WebSocketExperimentTask::Config::~Config() {}
118 
WebSocketExperimentTask(const Config & config,net::CompletionCallback * callback)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 
~WebSocketExperimentTask()130 WebSocketExperimentTask::~WebSocketExperimentTask() {
131   DCHECK(!websocket_);
132 }
133 
134 /* static */
InitHistogram()135 void WebSocketExperimentTask::InitHistogram() {
136   DCHECK(!g_histogram_table);
137   g_histogram_table = new base::hash_map<std::string, Histogram*>;
138 }
139 
GetCounterNameForConfig(const WebSocketExperimentTask::Config & config,const std::string & name)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 
GetEnumsHistogramForConfig(const WebSocketExperimentTask::Config & config,const std::string & name,Histogram::Sample boundary_value)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 
GetTimesHistogramForConfig(const WebSocketExperimentTask::Config & config,const std::string & name,base::TimeDelta min,base::TimeDelta max,size_t bucket_count)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 
UpdateHistogramEnums(const WebSocketExperimentTask::Config & config,const std::string & name,Histogram::Sample sample,Histogram::Sample boundary_value)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 
UpdateHistogramTimes(const WebSocketExperimentTask::Config & config,const std::string & name,base::TimeDelta sample,base::TimeDelta min,base::TimeDelta max,size_t bucket_count)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 */
ReleaseHistogram()222 void WebSocketExperimentTask::ReleaseHistogram() {
223   DCHECK(g_histogram_table);
224   delete g_histogram_table;
225   g_histogram_table = NULL;
226 }
227 
Run()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 
Cancel()235 void WebSocketExperimentTask::Cancel() {
236   next_state_ = STATE_NONE;
237   DoLoop(net::OK);
238 }
239 
SaveResult() const240 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.
OnURLFetchComplete(const URLFetcher * source,const GURL & url,const net::URLRequestStatus & status,int response_code,const ResponseCookies & cookies,const std::string & data)286 void WebSocketExperimentTask::OnURLFetchComplete(
287     const URLFetcher* source,
288     const GURL& url,
289     const net::URLRequestStatus& status,
290     int response_code,
291     const 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
OnOpen(net::WebSocket * websocket)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 
OnMessage(net::WebSocket * websocket,const std::string & msg)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 
OnError(net::WebSocket * websocket)348 void WebSocketExperimentTask::OnError(net::WebSocket* websocket) {
349   // TODO(ukai): record error count?
350 }
351 
OnClose(net::WebSocket * websocket,bool was_clean)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 
OnSocketError(const net::WebSocket * websocket,int error)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 
SetContext(Context * context)384 void WebSocketExperimentTask::SetContext(Context* context) {
385   context_.reset(context);
386 }
387 
OnTimedOut()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 
DoLoop(int result)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 
DoURLFetch()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 
DoURLFetchComplete(int result)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 
DoWebSocketConnect()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 
DoWebSocketConnectComplete(int result)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 
DoWebSocketSendHello()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 
DoWebSocketReceiveHello(int result)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 
DoWebSocketKeepIdle()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 
DoWebSocketKeepIdleComplete(int result)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 
DoWebSocketReceivePushMessage(int result)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 
DoWebSocketEchoBackMessage()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 
DoWebSocketReceiveBye(int result)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 
DoWebSocketClose()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 
DoWebSocketCloseComplete(int result)614 int WebSocketExperimentTask::DoWebSocketCloseComplete(int result) {
615   websocket_ = NULL;
616   return result;
617 }
618 
SetTimeout(int64 deadline_ms)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 
RevokeTimeoutTimer()628 void WebSocketExperimentTask::RevokeTimeoutTimer() {
629   method_factory_.RevokeAll();
630 }
631 
Finish(int result)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
646