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