websocket_experiment_task.cc revision 72a454cd3513ac24fbdd0e0cb9ad70b86a99b801
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/browser_thread.h" 10#include "chrome/browser/profiles/profile.h" 11#include "chrome/common/net/url_request_context_getter.h" 12#include "net/base/host_resolver.h" 13#include "net/base/load_flags.h" 14#include "net/base/net_errors.h" 15#include "net/websockets/websocket.h" 16 17using base::Histogram; 18using base::LinearHistogram; 19 20namespace chrome_browser_net_websocket_experiment { 21 22static 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 35URLFetcher* WebSocketExperimentTask::Context::CreateURLFetcher( 36 const Config& config, URLFetcher::Delegate* delegate) { 37 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 54net::WebSocket* WebSocketExperimentTask::Context::CreateWebSocket( 55 const Config& config, net::WebSocketDelegate* delegate) { 56 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. 75static const int kUrlFetchDeadlineSec = 10; 76static const int kWebSocketConnectDeadlineSec = 10; 77// Expects WebSocket live experiment server echoes message back within a few 78// seconds. 79static 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. 83static const int kWebSocketIdleSec = 1; 84static const int kWebSocketPushDeadlineSec = 1; 85// WebSocket live experiment server sends "bye" message soon. 86static const int kWebSocketByeDeadlineSec = 10; 87// WebSocket live experiment server closes after it receives "bye" message. 88static 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. 92static const int kWebSocketTimeSec = 10; 93static const int kTimeBucketCount = 50; 94 95// Holds Histogram objects during experiments run. 96static base::hash_map<std::string, Histogram*>* g_histogram_table; 97 98WebSocketExperimentTask::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 117WebSocketExperimentTask::Config::~Config() {} 118 119WebSocketExperimentTask::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 130WebSocketExperimentTask::~WebSocketExperimentTask() { 131 DCHECK(!websocket_); 132} 133 134/* static */ 135void WebSocketExperimentTask::InitHistogram() { 136 DCHECK(!g_histogram_table); 137 g_histogram_table = new base::hash_map<std::string, Histogram*>; 138} 139 140static 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 162static 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 counter->AddRef(); // Released in ReleaseHistogram(). 177 g_histogram_table->insert(std::make_pair(counter_name, counter)); 178 return counter; 179} 180 181static Histogram* GetTimesHistogramForConfig( 182 const WebSocketExperimentTask::Config& config, 183 const std::string& name, 184 base::TimeDelta min, 185 base::TimeDelta max, 186 size_t bucket_count) { 187 DCHECK(g_histogram_table); 188 std::string counter_name = GetCounterNameForConfig(config, name); 189 base::hash_map<std::string, Histogram*>::iterator found = 190 g_histogram_table->find(counter_name); 191 if (found != g_histogram_table->end()) { 192 return found->second; 193 } 194 Histogram* counter = Histogram::FactoryTimeGet( 195 counter_name, min, max, bucket_count, 196 Histogram::kUmaTargetedHistogramFlag); 197 counter->AddRef(); // Released in ReleaseHistogram(). 198 g_histogram_table->insert(std::make_pair(counter_name, counter)); 199 return counter; 200} 201 202static void UpdateHistogramEnums( 203 const WebSocketExperimentTask::Config& config, 204 const std::string& name, 205 Histogram::Sample sample, 206 Histogram::Sample boundary_value) { 207 Histogram* counter = GetEnumsHistogramForConfig(config, name, boundary_value); 208 counter->Add(sample); 209} 210 211static void UpdateHistogramTimes( 212 const WebSocketExperimentTask::Config& config, 213 const std::string& name, 214 base::TimeDelta sample, 215 base::TimeDelta min, 216 base::TimeDelta max, 217 size_t bucket_count) { 218 Histogram* counter = GetTimesHistogramForConfig( 219 config, name, min, max, bucket_count); 220 counter->AddTime(sample); 221} 222 223/* static */ 224void WebSocketExperimentTask::ReleaseHistogram() { 225 DCHECK(g_histogram_table); 226 for (base::hash_map<std::string, Histogram*>::iterator iter = 227 g_histogram_table->begin(); 228 iter != g_histogram_table->end(); 229 ++iter) { 230 Histogram* counter = iter->second; 231 if (counter != NULL) 232 counter->Release(); 233 iter->second = NULL; 234 } 235 delete g_histogram_table; 236 g_histogram_table = NULL; 237} 238 239void WebSocketExperimentTask::Run() { 240 DVLOG(1) << "Run WebSocket experiment for " << config_.url << " " 241 << GetProtocolVersionName(config_.protocol_version); 242 next_state_ = STATE_URL_FETCH; 243 DoLoop(net::OK); 244} 245 246void WebSocketExperimentTask::Cancel() { 247 next_state_ = STATE_NONE; 248 DoLoop(net::OK); 249} 250 251void WebSocketExperimentTask::SaveResult() const { 252 DVLOG(1) << "WebSocket experiment save result for " << config_.url 253 << " last_state=" << result_.last_state; 254 UpdateHistogramEnums(config_, "LastState", result_.last_state, NUM_STATES); 255 UpdateHistogramTimes(config_, "UrlFetch", result_.url_fetch, 256 base::TimeDelta::FromMilliseconds(1), 257 base::TimeDelta::FromSeconds(kUrlFetchDeadlineSec), 258 kTimeBucketCount); 259 260 if (result_.last_state < STATE_WEBSOCKET_CONNECT_COMPLETE) 261 return; 262 263 UpdateHistogramTimes(config_, "WebSocketConnect", result_.websocket_connect, 264 base::TimeDelta::FromMilliseconds(1), 265 base::TimeDelta::FromSeconds( 266 kWebSocketConnectDeadlineSec), 267 kTimeBucketCount); 268 269 if (result_.last_state < STATE_WEBSOCKET_RECV_HELLO) 270 return; 271 272 UpdateHistogramTimes(config_, "WebSocketEcho", result_.websocket_echo, 273 base::TimeDelta::FromMilliseconds(1), 274 base::TimeDelta::FromSeconds( 275 kWebSocketEchoDeadlineSec), 276 kTimeBucketCount); 277 278 if (result_.last_state < STATE_WEBSOCKET_KEEP_IDLE) 279 return; 280 281 UpdateHistogramTimes(config_, "WebSocketIdle", result_.websocket_idle, 282 base::TimeDelta::FromMilliseconds(1), 283 base::TimeDelta::FromSeconds( 284 kWebSocketIdleSec + kWebSocketPushDeadlineSec), 285 kTimeBucketCount); 286 287 if (result_.last_state < STATE_WEBSOCKET_CLOSE_COMPLETE) 288 return; 289 290 UpdateHistogramTimes(config_, "WebSocketTotal", result_.websocket_total, 291 base::TimeDelta::FromMilliseconds(1), 292 base::TimeDelta::FromSeconds(kWebSocketTimeSec), 293 kTimeBucketCount); 294} 295 296// URLFetcher::Delegate method. 297void WebSocketExperimentTask::OnURLFetchComplete( 298 const URLFetcher* source, 299 const GURL& url, 300 const net::URLRequestStatus& status, 301 int response_code, 302 const ResponseCookies& cookies, 303 const std::string& data) { 304 result_.url_fetch = base::TimeTicks::Now() - url_fetch_start_time_; 305 RevokeTimeoutTimer(); 306 int result = net::ERR_FAILED; 307 if (next_state_ != STATE_URL_FETCH_COMPLETE) { 308 DVLOG(1) << "unexpected state=" << next_state_ 309 << " at OnURLFetchComplete for " << config_.http_url; 310 result = net::ERR_UNEXPECTED; 311 } else if (response_code == 200 || response_code == 304) { 312 result = net::OK; 313 } 314 DoLoop(result); 315} 316 317// net::WebSocketDelegate 318void WebSocketExperimentTask::OnOpen(net::WebSocket* websocket) { 319 result_.websocket_connect = 320 base::TimeTicks::Now() - websocket_connect_start_time_; 321 RevokeTimeoutTimer(); 322 int result = net::ERR_UNEXPECTED; 323 if (next_state_ == STATE_WEBSOCKET_CONNECT_COMPLETE) 324 result = net::OK; 325 else 326 DVLOG(1) << "unexpected state=" << next_state_ 327 << " at OnOpen for " << config_.url 328 << " " << GetProtocolVersionName(config_.protocol_version); 329 DoLoop(result); 330} 331 332void WebSocketExperimentTask::OnMessage( 333 net::WebSocket* websocket, const std::string& msg) { 334 if (!result_.websocket_echo.ToInternalValue()) 335 result_.websocket_echo = 336 base::TimeTicks::Now() - websocket_echo_start_time_; 337 if (!websocket_idle_start_time_.is_null() && 338 !result_.websocket_idle.ToInternalValue()) 339 result_.websocket_idle = 340 base::TimeTicks::Now() - websocket_idle_start_time_; 341 RevokeTimeoutTimer(); 342 received_messages_.push_back(msg); 343 int result = net::ERR_UNEXPECTED; 344 switch (next_state_) { 345 case STATE_WEBSOCKET_RECV_HELLO: 346 case STATE_WEBSOCKET_RECV_PUSH_MESSAGE: 347 case STATE_WEBSOCKET_RECV_BYE: 348 result = net::OK; 349 break; 350 default: 351 DVLOG(1) << "unexpected state=" << next_state_ 352 << " at OnMessage for " << config_.url 353 << " " << GetProtocolVersionName(config_.protocol_version); 354 break; 355 } 356 DoLoop(result); 357} 358 359void WebSocketExperimentTask::OnError(net::WebSocket* websocket) { 360 // TODO(ukai): record error count? 361} 362 363void WebSocketExperimentTask::OnClose( 364 net::WebSocket* websocket, bool was_clean) { 365 RevokeTimeoutTimer(); 366 websocket_ = NULL; 367 result_.websocket_total = 368 base::TimeTicks::Now() - websocket_connect_start_time_; 369 int result = net::ERR_CONNECTION_CLOSED; 370 if (last_websocket_error_ != net::OK) 371 result = last_websocket_error_; 372 DVLOG(1) << "WebSocket onclose was_clean=" << was_clean 373 << " next_state=" << next_state_ 374 << " last_error=" << net::ErrorToString(result); 375 if (config_.protocol_version == net::WebSocket::DEFAULT_VERSION) { 376 if (next_state_ == STATE_WEBSOCKET_CLOSE_COMPLETE && was_clean) 377 result = net::OK; 378 } else { 379 // DRAFT75 doesn't report was_clean correctly. 380 if (next_state_ == STATE_WEBSOCKET_CLOSE_COMPLETE) 381 result = net::OK; 382 } 383 DoLoop(result); 384} 385 386void WebSocketExperimentTask::OnSocketError( 387 const net::WebSocket* websocket, int error) { 388 DVLOG(1) << "WebSocket socket level error=" << net::ErrorToString(error) 389 << " next_state=" << next_state_ 390 << " for " << config_.url 391 << " " << GetProtocolVersionName(config_.protocol_version); 392 last_websocket_error_ = error; 393} 394 395void WebSocketExperimentTask::SetContext(Context* context) { 396 context_.reset(context); 397} 398 399void WebSocketExperimentTask::OnTimedOut() { 400 DVLOG(1) << "OnTimedOut next_state=" << next_state_ 401 << " for " << config_.url 402 << " " << GetProtocolVersionName(config_.protocol_version); 403 RevokeTimeoutTimer(); 404 DoLoop(net::ERR_TIMED_OUT); 405} 406 407void WebSocketExperimentTask::DoLoop(int result) { 408 if (next_state_ == STATE_NONE) { 409 Finish(net::ERR_ABORTED); 410 return; 411 } 412 do { 413 State state = next_state_; 414 next_state_ = STATE_NONE; 415 switch (state) { 416 case STATE_URL_FETCH: 417 result = DoURLFetch(); 418 break; 419 case STATE_URL_FETCH_COMPLETE: 420 result = DoURLFetchComplete(result); 421 break; 422 case STATE_WEBSOCKET_CONNECT: 423 result = DoWebSocketConnect(); 424 break; 425 case STATE_WEBSOCKET_CONNECT_COMPLETE: 426 result = DoWebSocketConnectComplete(result); 427 break; 428 case STATE_WEBSOCKET_SEND_HELLO: 429 result = DoWebSocketSendHello(); 430 break; 431 case STATE_WEBSOCKET_RECV_HELLO: 432 result = DoWebSocketReceiveHello(result); 433 break; 434 case STATE_WEBSOCKET_KEEP_IDLE: 435 result = DoWebSocketKeepIdle(); 436 break; 437 case STATE_WEBSOCKET_KEEP_IDLE_COMPLETE: 438 result = DoWebSocketKeepIdleComplete(result); 439 break; 440 case STATE_WEBSOCKET_RECV_PUSH_MESSAGE: 441 result = DoWebSocketReceivePushMessage(result); 442 break; 443 case STATE_WEBSOCKET_ECHO_BACK_MESSAGE: 444 result = DoWebSocketEchoBackMessage(); 445 break; 446 case STATE_WEBSOCKET_RECV_BYE: 447 result = DoWebSocketReceiveBye(result); 448 break; 449 case STATE_WEBSOCKET_CLOSE: 450 result = DoWebSocketClose(); 451 break; 452 case STATE_WEBSOCKET_CLOSE_COMPLETE: 453 result = DoWebSocketCloseComplete(result); 454 break; 455 default: 456 NOTREACHED(); 457 break; 458 } 459 result_.last_state = state; 460 } while (result != net::ERR_IO_PENDING && next_state_ != STATE_NONE); 461 462 if (result != net::ERR_IO_PENDING) 463 Finish(result); 464} 465 466int WebSocketExperimentTask::DoURLFetch() { 467 DCHECK(!url_fetcher_.get()); 468 469 url_fetcher_.reset(context_->CreateURLFetcher(config_, this)); 470 if (!url_fetcher_.get()) { 471 // Request context is not ready. 472 next_state_ = STATE_NONE; 473 return net::ERR_UNEXPECTED; 474 } 475 476 next_state_ = STATE_URL_FETCH_COMPLETE; 477 SetTimeout(config_.url_fetch_deadline_ms); 478 url_fetch_start_time_ = base::TimeTicks::Now(); 479 url_fetcher_->Start(); 480 return net::ERR_IO_PENDING; 481} 482 483int WebSocketExperimentTask::DoURLFetchComplete(int result) { 484 url_fetcher_.reset(); 485 486 if (result < 0) 487 return result; 488 489 next_state_ = STATE_WEBSOCKET_CONNECT; 490 return net::OK; 491} 492 493int WebSocketExperimentTask::DoWebSocketConnect() { 494 DCHECK(!websocket_); 495 496 websocket_ = context_->CreateWebSocket(config_, this); 497 if (!websocket_) { 498 // Request context is not ready. 499 next_state_ = STATE_NONE; 500 return net::ERR_UNEXPECTED; 501 } 502 next_state_ = STATE_WEBSOCKET_CONNECT_COMPLETE; 503 websocket_connect_start_time_ = base::TimeTicks::Now(); 504 websocket_->Connect(); 505 506 SetTimeout(config_.websocket_onopen_deadline_ms); 507 return net::ERR_IO_PENDING; 508} 509 510int WebSocketExperimentTask::DoWebSocketConnectComplete(int result) { 511 if (result < 0) 512 return result; 513 DCHECK(websocket_); 514 515 next_state_ = STATE_WEBSOCKET_SEND_HELLO; 516 return net::OK; 517} 518 519int WebSocketExperimentTask::DoWebSocketSendHello() { 520 DCHECK(websocket_); 521 522 next_state_ = STATE_WEBSOCKET_RECV_HELLO; 523 524 websocket_echo_start_time_ = base::TimeTicks::Now(); 525 websocket_->Send(config_.websocket_hello_message); 526 SetTimeout(config_.websocket_hello_echoback_deadline_ms); 527 return net::ERR_IO_PENDING; 528} 529 530int WebSocketExperimentTask::DoWebSocketReceiveHello(int result) { 531 if (result < 0) 532 return result; 533 534 DCHECK(websocket_); 535 536 if (received_messages_.size() != 1) 537 return net::ERR_INVALID_RESPONSE; 538 539 std::string msg = received_messages_.front(); 540 received_messages_.pop_front(); 541 if (msg != config_.websocket_hello_message) 542 return net::ERR_INVALID_RESPONSE; 543 544 next_state_ = STATE_WEBSOCKET_KEEP_IDLE; 545 return net::OK; 546} 547 548int WebSocketExperimentTask::DoWebSocketKeepIdle() { 549 DCHECK(websocket_); 550 551 next_state_ = STATE_WEBSOCKET_KEEP_IDLE_COMPLETE; 552 websocket_idle_start_time_ = base::TimeTicks::Now(); 553 SetTimeout(config_.websocket_idle_ms); 554 return net::ERR_IO_PENDING; 555} 556 557int WebSocketExperimentTask::DoWebSocketKeepIdleComplete(int result) { 558 if (result != net::ERR_TIMED_OUT) { 559 // Server sends back too early, or unexpected close? 560 if (result == net::OK) 561 result = net::ERR_UNEXPECTED; 562 return result; 563 } 564 565 DCHECK(websocket_); 566 567 next_state_ = STATE_WEBSOCKET_RECV_PUSH_MESSAGE; 568 SetTimeout(config_.websocket_receive_push_message_deadline_ms); 569 return net::ERR_IO_PENDING; 570} 571 572int WebSocketExperimentTask::DoWebSocketReceivePushMessage(int result) { 573 if (result < 0) 574 return result; 575 576 DCHECK(websocket_); 577 if (received_messages_.size() != 1) 578 return net::ERR_INVALID_RESPONSE; 579 580 push_message_ = received_messages_.front(); 581 received_messages_.pop_front(); 582 583 next_state_ = STATE_WEBSOCKET_ECHO_BACK_MESSAGE; 584 return net::OK; 585} 586 587int WebSocketExperimentTask::DoWebSocketEchoBackMessage() { 588 DCHECK(websocket_); 589 DCHECK(!push_message_.empty()); 590 591 next_state_ = STATE_WEBSOCKET_RECV_BYE; 592 websocket_->Send(push_message_); 593 SetTimeout(config_.websocket_bye_deadline_ms); 594 return net::ERR_IO_PENDING; 595} 596 597int WebSocketExperimentTask::DoWebSocketReceiveBye(int result) { 598 if (result < 0) 599 return result; 600 601 DCHECK(websocket_); 602 603 if (received_messages_.size() != 1) 604 return net::ERR_INVALID_RESPONSE; 605 606 std::string bye = received_messages_.front(); 607 received_messages_.pop_front(); 608 609 if (bye != config_.websocket_bye_message) 610 return net::ERR_INVALID_RESPONSE; 611 612 next_state_ = STATE_WEBSOCKET_CLOSE; 613 return net::OK; 614} 615 616int WebSocketExperimentTask::DoWebSocketClose() { 617 DCHECK(websocket_); 618 619 next_state_ = STATE_WEBSOCKET_CLOSE_COMPLETE; 620 websocket_->Close(); 621 SetTimeout(config_.websocket_close_deadline_ms); 622 return net::ERR_IO_PENDING; 623} 624 625int WebSocketExperimentTask::DoWebSocketCloseComplete(int result) { 626 websocket_ = NULL; 627 return result; 628} 629 630void WebSocketExperimentTask::SetTimeout(int64 deadline_ms) { 631 bool r = BrowserThread::PostDelayedTask( 632 BrowserThread::IO, 633 FROM_HERE, 634 method_factory_.NewRunnableMethod(&WebSocketExperimentTask::OnTimedOut), 635 deadline_ms); 636 DCHECK(r) << "No IO thread running?"; 637} 638 639void WebSocketExperimentTask::RevokeTimeoutTimer() { 640 method_factory_.RevokeAll(); 641} 642 643void WebSocketExperimentTask::Finish(int result) { 644 url_fetcher_.reset(); 645 scoped_refptr<net::WebSocket> websocket = websocket_; 646 websocket_ = NULL; 647 if (websocket) 648 websocket->DetachDelegate(); 649 DVLOG(1) << "Finish WebSocket experiment for " << config_.url 650 << " " << GetProtocolVersionName(config_.protocol_version) 651 << " next_state=" << next_state_ 652 << " result=" << net::ErrorToString(result); 653 callback_->Run(result); // will release this. 654} 655 656} // namespace chrome_browser_net 657