1 // Copyright (c) 2012 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 "net/proxy/multi_threaded_proxy_resolver.h" 6 7 #include "base/message_loop/message_loop.h" 8 #include "base/stl_util.h" 9 #include "base/strings/string_util.h" 10 #include "base/strings/stringprintf.h" 11 #include "base/strings/utf_string_conversions.h" 12 #include "base/synchronization/waitable_event.h" 13 #include "base/threading/platform_thread.h" 14 #include "net/base/net_errors.h" 15 #include "net/base/net_log.h" 16 #include "net/base/net_log_unittest.h" 17 #include "net/base/test_completion_callback.h" 18 #include "net/proxy/proxy_info.h" 19 #include "testing/gtest/include/gtest/gtest.h" 20 #include "url/gurl.h" 21 22 using base::ASCIIToUTF16; 23 24 namespace net { 25 26 namespace { 27 28 // A synchronous mock ProxyResolver implementation, which can be used in 29 // conjunction with MultiThreadedProxyResolver. 30 // - returns a single-item proxy list with the query's host. 31 class MockProxyResolver : public ProxyResolver { 32 public: 33 MockProxyResolver() 34 : ProxyResolver(true /*expects_pac_bytes*/), 35 wrong_loop_(base::MessageLoop::current()), 36 request_count_(0) {} 37 38 // ProxyResolver implementation. 39 virtual int GetProxyForURL(const GURL& query_url, 40 ProxyInfo* results, 41 const CompletionCallback& callback, 42 RequestHandle* request, 43 const BoundNetLog& net_log) OVERRIDE { 44 if (resolve_latency_ != base::TimeDelta()) 45 base::PlatformThread::Sleep(resolve_latency_); 46 47 CheckIsOnWorkerThread(); 48 49 EXPECT_TRUE(callback.is_null()); 50 EXPECT_TRUE(request == NULL); 51 52 // Write something into |net_log| (doesn't really have any meaning.) 53 net_log.BeginEvent(NetLog::TYPE_PAC_JAVASCRIPT_ALERT); 54 55 results->UseNamedProxy(query_url.host()); 56 57 // Return a success code which represents the request's order. 58 return request_count_++; 59 } 60 61 virtual void CancelRequest(RequestHandle request) OVERRIDE { 62 NOTREACHED(); 63 } 64 65 virtual LoadState GetLoadState(RequestHandle request) const OVERRIDE { 66 NOTREACHED(); 67 return LOAD_STATE_IDLE; 68 } 69 70 virtual void CancelSetPacScript() OVERRIDE { 71 NOTREACHED(); 72 } 73 74 virtual int SetPacScript( 75 const scoped_refptr<ProxyResolverScriptData>& script_data, 76 const CompletionCallback& callback) OVERRIDE { 77 CheckIsOnWorkerThread(); 78 last_script_data_ = script_data; 79 return OK; 80 } 81 82 int request_count() const { return request_count_; } 83 84 const ProxyResolverScriptData* last_script_data() const { 85 return last_script_data_.get(); 86 } 87 88 void SetResolveLatency(base::TimeDelta latency) { 89 resolve_latency_ = latency; 90 } 91 92 private: 93 void CheckIsOnWorkerThread() { 94 // We should be running on the worker thread -- while we don't know the 95 // message loop of MultiThreadedProxyResolver's worker thread, we do 96 // know that it is going to be distinct from the loop running the 97 // test, so at least make sure it isn't the main loop. 98 EXPECT_NE(base::MessageLoop::current(), wrong_loop_); 99 } 100 101 base::MessageLoop* wrong_loop_; 102 int request_count_; 103 scoped_refptr<ProxyResolverScriptData> last_script_data_; 104 base::TimeDelta resolve_latency_; 105 }; 106 107 108 // A mock synchronous ProxyResolver which can be set to block upon reaching 109 // GetProxyForURL(). 110 // TODO(eroman): WaitUntilBlocked() *must* be called before calling Unblock(), 111 // otherwise there will be a race on |should_block_| since it is 112 // read without any synchronization. 113 class BlockableProxyResolver : public MockProxyResolver { 114 public: 115 BlockableProxyResolver() 116 : should_block_(false), 117 unblocked_(true, true), 118 blocked_(true, false) { 119 } 120 121 void Block() { 122 should_block_ = true; 123 unblocked_.Reset(); 124 } 125 126 void Unblock() { 127 should_block_ = false; 128 blocked_.Reset(); 129 unblocked_.Signal(); 130 } 131 132 void WaitUntilBlocked() { 133 blocked_.Wait(); 134 } 135 136 virtual int GetProxyForURL(const GURL& query_url, 137 ProxyInfo* results, 138 const CompletionCallback& callback, 139 RequestHandle* request, 140 const BoundNetLog& net_log) OVERRIDE { 141 if (should_block_) { 142 blocked_.Signal(); 143 unblocked_.Wait(); 144 } 145 146 return MockProxyResolver::GetProxyForURL( 147 query_url, results, callback, request, net_log); 148 } 149 150 private: 151 bool should_block_; 152 base::WaitableEvent unblocked_; 153 base::WaitableEvent blocked_; 154 }; 155 156 // ForwardingProxyResolver forwards all requests to |impl|. 157 class ForwardingProxyResolver : public ProxyResolver { 158 public: 159 explicit ForwardingProxyResolver(ProxyResolver* impl) 160 : ProxyResolver(impl->expects_pac_bytes()), 161 impl_(impl) {} 162 163 virtual int GetProxyForURL(const GURL& query_url, 164 ProxyInfo* results, 165 const CompletionCallback& callback, 166 RequestHandle* request, 167 const BoundNetLog& net_log) OVERRIDE { 168 return impl_->GetProxyForURL( 169 query_url, results, callback, request, net_log); 170 } 171 172 virtual void CancelRequest(RequestHandle request) OVERRIDE { 173 impl_->CancelRequest(request); 174 } 175 176 virtual LoadState GetLoadState(RequestHandle request) const OVERRIDE { 177 NOTREACHED(); 178 return LOAD_STATE_IDLE; 179 } 180 181 virtual void CancelSetPacScript() OVERRIDE { 182 impl_->CancelSetPacScript(); 183 } 184 185 virtual int SetPacScript( 186 const scoped_refptr<ProxyResolverScriptData>& script_data, 187 const CompletionCallback& callback) OVERRIDE { 188 return impl_->SetPacScript(script_data, callback); 189 } 190 191 private: 192 ProxyResolver* impl_; 193 }; 194 195 // This factory returns ProxyResolvers that forward all requests to 196 // |resolver|. 197 class ForwardingProxyResolverFactory : public ProxyResolverFactory { 198 public: 199 explicit ForwardingProxyResolverFactory(ProxyResolver* resolver) 200 : ProxyResolverFactory(resolver->expects_pac_bytes()), 201 resolver_(resolver) {} 202 203 virtual ProxyResolver* CreateProxyResolver() OVERRIDE { 204 return new ForwardingProxyResolver(resolver_); 205 } 206 207 private: 208 ProxyResolver* resolver_; 209 }; 210 211 // This factory returns new instances of BlockableProxyResolver. 212 class BlockableProxyResolverFactory : public ProxyResolverFactory { 213 public: 214 BlockableProxyResolverFactory() : ProxyResolverFactory(true) {} 215 216 virtual ~BlockableProxyResolverFactory() { 217 STLDeleteElements(&resolvers_); 218 } 219 220 virtual ProxyResolver* CreateProxyResolver() OVERRIDE { 221 BlockableProxyResolver* resolver = new BlockableProxyResolver; 222 resolvers_.push_back(resolver); 223 return new ForwardingProxyResolver(resolver); 224 } 225 226 std::vector<BlockableProxyResolver*> resolvers() { 227 return resolvers_; 228 } 229 230 private: 231 std::vector<BlockableProxyResolver*> resolvers_; 232 }; 233 234 TEST(MultiThreadedProxyResolverTest, SingleThread_Basic) { 235 const size_t kNumThreads = 1u; 236 scoped_ptr<MockProxyResolver> mock(new MockProxyResolver); 237 MultiThreadedProxyResolver resolver( 238 new ForwardingProxyResolverFactory(mock.get()), kNumThreads); 239 240 int rv; 241 242 EXPECT_TRUE(resolver.expects_pac_bytes()); 243 244 // Call SetPacScriptByData() -- verify that it reaches the synchronous 245 // resolver. 246 TestCompletionCallback set_script_callback; 247 rv = resolver.SetPacScript( 248 ProxyResolverScriptData::FromUTF8("pac script bytes"), 249 set_script_callback.callback()); 250 EXPECT_EQ(ERR_IO_PENDING, rv); 251 EXPECT_EQ(OK, set_script_callback.WaitForResult()); 252 EXPECT_EQ(ASCIIToUTF16("pac script bytes"), 253 mock->last_script_data()->utf16()); 254 255 // Start request 0. 256 TestCompletionCallback callback0; 257 CapturingBoundNetLog log0; 258 ProxyInfo results0; 259 rv = resolver.GetProxyForURL(GURL("http://request0"), &results0, 260 callback0.callback(), NULL, log0.bound()); 261 EXPECT_EQ(ERR_IO_PENDING, rv); 262 263 // Wait for request 0 to finish. 264 rv = callback0.WaitForResult(); 265 EXPECT_EQ(0, rv); 266 EXPECT_EQ("PROXY request0:80", results0.ToPacString()); 267 268 // The mock proxy resolver should have written 1 log entry. And 269 // on completion, this should have been copied into |log0|. 270 // We also have 1 log entry that was emitted by the 271 // MultiThreadedProxyResolver. 272 CapturingNetLog::CapturedEntryList entries0; 273 log0.GetEntries(&entries0); 274 275 ASSERT_EQ(2u, entries0.size()); 276 EXPECT_EQ(NetLog::TYPE_SUBMITTED_TO_RESOLVER_THREAD, entries0[0].type); 277 278 // Start 3 more requests (request1 to request3). 279 280 TestCompletionCallback callback1; 281 ProxyInfo results1; 282 rv = resolver.GetProxyForURL(GURL("http://request1"), &results1, 283 callback1.callback(), NULL, BoundNetLog()); 284 EXPECT_EQ(ERR_IO_PENDING, rv); 285 286 TestCompletionCallback callback2; 287 ProxyInfo results2; 288 rv = resolver.GetProxyForURL(GURL("http://request2"), &results2, 289 callback2.callback(), NULL, BoundNetLog()); 290 EXPECT_EQ(ERR_IO_PENDING, rv); 291 292 TestCompletionCallback callback3; 293 ProxyInfo results3; 294 rv = resolver.GetProxyForURL(GURL("http://request3"), &results3, 295 callback3.callback(), NULL, BoundNetLog()); 296 EXPECT_EQ(ERR_IO_PENDING, rv); 297 298 // Wait for the requests to finish (they must finish in the order they were 299 // started, which is what we check for from their magic return value) 300 301 rv = callback1.WaitForResult(); 302 EXPECT_EQ(1, rv); 303 EXPECT_EQ("PROXY request1:80", results1.ToPacString()); 304 305 rv = callback2.WaitForResult(); 306 EXPECT_EQ(2, rv); 307 EXPECT_EQ("PROXY request2:80", results2.ToPacString()); 308 309 rv = callback3.WaitForResult(); 310 EXPECT_EQ(3, rv); 311 EXPECT_EQ("PROXY request3:80", results3.ToPacString()); 312 } 313 314 // Tests that the NetLog is updated to include the time the request was waiting 315 // to be scheduled to a thread. 316 TEST(MultiThreadedProxyResolverTest, 317 SingleThread_UpdatesNetLogWithThreadWait) { 318 const size_t kNumThreads = 1u; 319 scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver); 320 MultiThreadedProxyResolver resolver( 321 new ForwardingProxyResolverFactory(mock.get()), kNumThreads); 322 323 int rv; 324 325 // Initialize the resolver. 326 TestCompletionCallback init_callback; 327 rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("foo"), 328 init_callback.callback()); 329 EXPECT_EQ(OK, init_callback.WaitForResult()); 330 331 // Block the proxy resolver, so no request can complete. 332 mock->Block(); 333 334 // Start request 0. 335 ProxyResolver::RequestHandle request0; 336 TestCompletionCallback callback0; 337 ProxyInfo results0; 338 CapturingBoundNetLog log0; 339 rv = resolver.GetProxyForURL(GURL("http://request0"), &results0, 340 callback0.callback(), &request0, log0.bound()); 341 EXPECT_EQ(ERR_IO_PENDING, rv); 342 343 // Start 2 more requests (request1 and request2). 344 345 TestCompletionCallback callback1; 346 ProxyInfo results1; 347 CapturingBoundNetLog log1; 348 rv = resolver.GetProxyForURL(GURL("http://request1"), &results1, 349 callback1.callback(), NULL, log1.bound()); 350 EXPECT_EQ(ERR_IO_PENDING, rv); 351 352 ProxyResolver::RequestHandle request2; 353 TestCompletionCallback callback2; 354 ProxyInfo results2; 355 CapturingBoundNetLog log2; 356 rv = resolver.GetProxyForURL(GURL("http://request2"), &results2, 357 callback2.callback(), &request2, log2.bound()); 358 EXPECT_EQ(ERR_IO_PENDING, rv); 359 360 // Unblock the worker thread so the requests can continue running. 361 mock->WaitUntilBlocked(); 362 mock->Unblock(); 363 364 // Check that request 0 completed as expected. 365 // The NetLog has 1 entry that came from the MultiThreadedProxyResolver, and 366 // 1 entry from the mock proxy resolver. 367 EXPECT_EQ(0, callback0.WaitForResult()); 368 EXPECT_EQ("PROXY request0:80", results0.ToPacString()); 369 370 CapturingNetLog::CapturedEntryList entries0; 371 log0.GetEntries(&entries0); 372 373 ASSERT_EQ(2u, entries0.size()); 374 EXPECT_EQ(NetLog::TYPE_SUBMITTED_TO_RESOLVER_THREAD, 375 entries0[0].type); 376 377 // Check that request 1 completed as expected. 378 EXPECT_EQ(1, callback1.WaitForResult()); 379 EXPECT_EQ("PROXY request1:80", results1.ToPacString()); 380 381 CapturingNetLog::CapturedEntryList entries1; 382 log1.GetEntries(&entries1); 383 384 ASSERT_EQ(4u, entries1.size()); 385 EXPECT_TRUE(LogContainsBeginEvent( 386 entries1, 0, 387 NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD)); 388 EXPECT_TRUE(LogContainsEndEvent( 389 entries1, 1, 390 NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD)); 391 392 // Check that request 2 completed as expected. 393 EXPECT_EQ(2, callback2.WaitForResult()); 394 EXPECT_EQ("PROXY request2:80", results2.ToPacString()); 395 396 CapturingNetLog::CapturedEntryList entries2; 397 log2.GetEntries(&entries2); 398 399 ASSERT_EQ(4u, entries2.size()); 400 EXPECT_TRUE(LogContainsBeginEvent( 401 entries2, 0, 402 NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD)); 403 EXPECT_TRUE(LogContainsEndEvent( 404 entries2, 1, 405 NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD)); 406 } 407 408 // Cancel a request which is in progress, and then cancel a request which 409 // is pending. 410 TEST(MultiThreadedProxyResolverTest, SingleThread_CancelRequest) { 411 const size_t kNumThreads = 1u; 412 scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver); 413 MultiThreadedProxyResolver resolver( 414 new ForwardingProxyResolverFactory(mock.get()), 415 kNumThreads); 416 417 int rv; 418 419 // Initialize the resolver. 420 TestCompletionCallback init_callback; 421 rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("foo"), 422 init_callback.callback()); 423 EXPECT_EQ(OK, init_callback.WaitForResult()); 424 425 // Block the proxy resolver, so no request can complete. 426 mock->Block(); 427 428 // Start request 0. 429 ProxyResolver::RequestHandle request0; 430 TestCompletionCallback callback0; 431 ProxyInfo results0; 432 rv = resolver.GetProxyForURL(GURL("http://request0"), &results0, 433 callback0.callback(), &request0, BoundNetLog()); 434 EXPECT_EQ(ERR_IO_PENDING, rv); 435 436 // Wait until requests 0 reaches the worker thread. 437 mock->WaitUntilBlocked(); 438 439 // Start 3 more requests (request1 : request3). 440 441 TestCompletionCallback callback1; 442 ProxyInfo results1; 443 rv = resolver.GetProxyForURL(GURL("http://request1"), &results1, 444 callback1.callback(), NULL, BoundNetLog()); 445 EXPECT_EQ(ERR_IO_PENDING, rv); 446 447 ProxyResolver::RequestHandle request2; 448 TestCompletionCallback callback2; 449 ProxyInfo results2; 450 rv = resolver.GetProxyForURL(GURL("http://request2"), &results2, 451 callback2.callback(), &request2, BoundNetLog()); 452 EXPECT_EQ(ERR_IO_PENDING, rv); 453 454 TestCompletionCallback callback3; 455 ProxyInfo results3; 456 rv = resolver.GetProxyForURL(GURL("http://request3"), &results3, 457 callback3.callback(), NULL, BoundNetLog()); 458 EXPECT_EQ(ERR_IO_PENDING, rv); 459 460 // Cancel request0 (inprogress) and request2 (pending). 461 resolver.CancelRequest(request0); 462 resolver.CancelRequest(request2); 463 464 // Unblock the worker thread so the requests can continue running. 465 mock->Unblock(); 466 467 // Wait for requests 1 and 3 to finish. 468 469 rv = callback1.WaitForResult(); 470 EXPECT_EQ(1, rv); 471 EXPECT_EQ("PROXY request1:80", results1.ToPacString()); 472 473 rv = callback3.WaitForResult(); 474 // Note that since request2 was cancelled before reaching the resolver, 475 // the request count is 2 and not 3 here. 476 EXPECT_EQ(2, rv); 477 EXPECT_EQ("PROXY request3:80", results3.ToPacString()); 478 479 // Requests 0 and 2 which were cancelled, hence their completion callbacks 480 // were never summoned. 481 EXPECT_FALSE(callback0.have_result()); 482 EXPECT_FALSE(callback2.have_result()); 483 } 484 485 // Test that deleting MultiThreadedProxyResolver while requests are 486 // outstanding cancels them (and doesn't leak anything). 487 TEST(MultiThreadedProxyResolverTest, SingleThread_CancelRequestByDeleting) { 488 const size_t kNumThreads = 1u; 489 scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver); 490 scoped_ptr<MultiThreadedProxyResolver> resolver( 491 new MultiThreadedProxyResolver( 492 new ForwardingProxyResolverFactory(mock.get()), kNumThreads)); 493 494 int rv; 495 496 // Initialize the resolver. 497 TestCompletionCallback init_callback; 498 rv = resolver->SetPacScript(ProxyResolverScriptData::FromUTF8("foo"), 499 init_callback.callback()); 500 EXPECT_EQ(OK, init_callback.WaitForResult()); 501 502 // Block the proxy resolver, so no request can complete. 503 mock->Block(); 504 505 // Start 3 requests. 506 507 TestCompletionCallback callback0; 508 ProxyInfo results0; 509 rv = resolver->GetProxyForURL(GURL("http://request0"), &results0, 510 callback0.callback(), NULL, BoundNetLog()); 511 EXPECT_EQ(ERR_IO_PENDING, rv); 512 513 TestCompletionCallback callback1; 514 ProxyInfo results1; 515 rv = resolver->GetProxyForURL(GURL("http://request1"), &results1, 516 callback1.callback(), NULL, BoundNetLog()); 517 EXPECT_EQ(ERR_IO_PENDING, rv); 518 519 TestCompletionCallback callback2; 520 ProxyInfo results2; 521 rv = resolver->GetProxyForURL(GURL("http://request2"), &results2, 522 callback2.callback(), NULL, BoundNetLog()); 523 EXPECT_EQ(ERR_IO_PENDING, rv); 524 525 // Wait until request 0 reaches the worker thread. 526 mock->WaitUntilBlocked(); 527 528 // Add some latency, to improve the chance that when 529 // MultiThreadedProxyResolver is deleted below we are still running inside 530 // of the worker thread. The test will pass regardless, so this race doesn't 531 // cause flakiness. However the destruction during execution is a more 532 // interesting case to test. 533 mock->SetResolveLatency(base::TimeDelta::FromMilliseconds(100)); 534 535 // Unblock the worker thread and delete the underlying 536 // MultiThreadedProxyResolver immediately. 537 mock->Unblock(); 538 resolver.reset(); 539 540 // Give any posted tasks a chance to run (in case there is badness). 541 base::MessageLoop::current()->RunUntilIdle(); 542 543 // Check that none of the outstanding requests were completed. 544 EXPECT_FALSE(callback0.have_result()); 545 EXPECT_FALSE(callback1.have_result()); 546 EXPECT_FALSE(callback2.have_result()); 547 } 548 549 // Cancel an outstanding call to SetPacScriptByData(). 550 TEST(MultiThreadedProxyResolverTest, SingleThread_CancelSetPacScript) { 551 const size_t kNumThreads = 1u; 552 scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver); 553 MultiThreadedProxyResolver resolver( 554 new ForwardingProxyResolverFactory(mock.get()), kNumThreads); 555 556 int rv; 557 558 TestCompletionCallback set_pac_script_callback; 559 rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("data"), 560 set_pac_script_callback.callback()); 561 EXPECT_EQ(ERR_IO_PENDING, rv); 562 563 // Cancel the SetPacScriptByData request. 564 resolver.CancelSetPacScript(); 565 566 // Start another SetPacScript request 567 TestCompletionCallback set_pac_script_callback2; 568 rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("data2"), 569 set_pac_script_callback2.callback()); 570 EXPECT_EQ(ERR_IO_PENDING, rv); 571 572 // Wait for the initialization to complete. 573 574 rv = set_pac_script_callback2.WaitForResult(); 575 EXPECT_EQ(0, rv); 576 EXPECT_EQ(ASCIIToUTF16("data2"), mock->last_script_data()->utf16()); 577 578 // The first SetPacScript callback should never have been completed. 579 EXPECT_FALSE(set_pac_script_callback.have_result()); 580 } 581 582 // Tests setting the PAC script once, lazily creating new threads, and 583 // cancelling requests. 584 TEST(MultiThreadedProxyResolverTest, ThreeThreads_Basic) { 585 const size_t kNumThreads = 3u; 586 BlockableProxyResolverFactory* factory = new BlockableProxyResolverFactory; 587 MultiThreadedProxyResolver resolver(factory, kNumThreads); 588 589 int rv; 590 591 EXPECT_TRUE(resolver.expects_pac_bytes()); 592 593 // Call SetPacScriptByData() -- verify that it reaches the synchronous 594 // resolver. 595 TestCompletionCallback set_script_callback; 596 rv = resolver.SetPacScript( 597 ProxyResolverScriptData::FromUTF8("pac script bytes"), 598 set_script_callback.callback()); 599 EXPECT_EQ(ERR_IO_PENDING, rv); 600 EXPECT_EQ(OK, set_script_callback.WaitForResult()); 601 // One thread has been provisioned (i.e. one ProxyResolver was created). 602 ASSERT_EQ(1u, factory->resolvers().size()); 603 EXPECT_EQ(ASCIIToUTF16("pac script bytes"), 604 factory->resolvers()[0]->last_script_data()->utf16()); 605 606 const int kNumRequests = 9; 607 TestCompletionCallback callback[kNumRequests]; 608 ProxyInfo results[kNumRequests]; 609 ProxyResolver::RequestHandle request[kNumRequests]; 610 611 // Start request 0 -- this should run on thread 0 as there is nothing else 612 // going on right now. 613 rv = resolver.GetProxyForURL( 614 GURL("http://request0"), &results[0], callback[0].callback(), &request[0], 615 BoundNetLog()); 616 EXPECT_EQ(ERR_IO_PENDING, rv); 617 618 // Wait for request 0 to finish. 619 rv = callback[0].WaitForResult(); 620 EXPECT_EQ(0, rv); 621 EXPECT_EQ("PROXY request0:80", results[0].ToPacString()); 622 ASSERT_EQ(1u, factory->resolvers().size()); 623 EXPECT_EQ(1, factory->resolvers()[0]->request_count()); 624 625 base::MessageLoop::current()->RunUntilIdle(); 626 627 // We now start 8 requests in parallel -- this will cause the maximum of 628 // three threads to be provisioned (an additional two from what we already 629 // have). 630 631 for (int i = 1; i < kNumRequests; ++i) { 632 rv = resolver.GetProxyForURL( 633 GURL(base::StringPrintf("http://request%d", i)), &results[i], 634 callback[i].callback(), &request[i], BoundNetLog()); 635 EXPECT_EQ(ERR_IO_PENDING, rv); 636 } 637 638 // We should now have a total of 3 threads, each with its own ProxyResolver 639 // that will get initialized with the same data. (We check this later since 640 // the assignment happens on the worker threads and may not have occurred 641 // yet.) 642 ASSERT_EQ(3u, factory->resolvers().size()); 643 644 // Cancel 3 of the 8 oustanding requests. 645 resolver.CancelRequest(request[1]); 646 resolver.CancelRequest(request[3]); 647 resolver.CancelRequest(request[6]); 648 649 // Wait for the remaining requests to complete. 650 int kNonCancelledRequests[] = {2, 4, 5, 7, 8}; 651 for (size_t i = 0; i < arraysize(kNonCancelledRequests); ++i) { 652 int request_index = kNonCancelledRequests[i]; 653 EXPECT_GE(callback[request_index].WaitForResult(), 0); 654 } 655 656 // Check that the cancelled requests never invoked their callback. 657 EXPECT_FALSE(callback[1].have_result()); 658 EXPECT_FALSE(callback[3].have_result()); 659 EXPECT_FALSE(callback[6].have_result()); 660 661 // We call SetPacScript again, solely to stop the current worker threads. 662 // (That way we can test to see the values observed by the synchronous 663 // resolvers in a non-racy manner). 664 TestCompletionCallback set_script_callback2; 665 rv = resolver.SetPacScript(ProxyResolverScriptData::FromUTF8("xyz"), 666 set_script_callback2.callback()); 667 EXPECT_EQ(ERR_IO_PENDING, rv); 668 EXPECT_EQ(OK, set_script_callback2.WaitForResult()); 669 ASSERT_EQ(4u, factory->resolvers().size()); 670 671 for (int i = 0; i < 3; ++i) { 672 EXPECT_EQ( 673 ASCIIToUTF16("pac script bytes"), 674 factory->resolvers()[i]->last_script_data()->utf16()) << "i=" << i; 675 } 676 677 EXPECT_EQ(ASCIIToUTF16("xyz"), 678 factory->resolvers()[3]->last_script_data()->utf16()); 679 680 // We don't know the exact ordering that requests ran on threads with, 681 // but we do know the total count that should have reached the threads. 682 // 8 total were submitted, and three were cancelled. Of the three that 683 // were cancelled, one of them (request 1) was cancelled after it had 684 // already been posted to the worker thread. So the resolvers will 685 // have seen 6 total (and 1 from the run prior). 686 ASSERT_EQ(4u, factory->resolvers().size()); 687 int total_count = 0; 688 for (int i = 0; i < 3; ++i) { 689 total_count += factory->resolvers()[i]->request_count(); 690 } 691 EXPECT_EQ(7, total_count); 692 } 693 694 // Tests using two threads. The first request hangs the first thread. Checks 695 // that other requests are able to complete while this first request remains 696 // stalled. 697 TEST(MultiThreadedProxyResolverTest, OneThreadBlocked) { 698 const size_t kNumThreads = 2u; 699 BlockableProxyResolverFactory* factory = new BlockableProxyResolverFactory; 700 MultiThreadedProxyResolver resolver(factory, kNumThreads); 701 702 int rv; 703 704 EXPECT_TRUE(resolver.expects_pac_bytes()); 705 706 // Initialize the resolver. 707 TestCompletionCallback set_script_callback; 708 rv = resolver.SetPacScript( 709 ProxyResolverScriptData::FromUTF8("pac script bytes"), 710 set_script_callback.callback()); 711 EXPECT_EQ(ERR_IO_PENDING, rv); 712 EXPECT_EQ(OK, set_script_callback.WaitForResult()); 713 // One thread has been provisioned (i.e. one ProxyResolver was created). 714 ASSERT_EQ(1u, factory->resolvers().size()); 715 EXPECT_EQ(ASCIIToUTF16("pac script bytes"), 716 factory->resolvers()[0]->last_script_data()->utf16()); 717 718 const int kNumRequests = 4; 719 TestCompletionCallback callback[kNumRequests]; 720 ProxyInfo results[kNumRequests]; 721 ProxyResolver::RequestHandle request[kNumRequests]; 722 723 // Start a request that will block the first thread. 724 725 factory->resolvers()[0]->Block(); 726 727 rv = resolver.GetProxyForURL( 728 GURL("http://request0"), &results[0], callback[0].callback(), &request[0], 729 BoundNetLog()); 730 731 EXPECT_EQ(ERR_IO_PENDING, rv); 732 factory->resolvers()[0]->WaitUntilBlocked(); 733 734 // Start 3 more requests -- they should all be serviced by thread #2 735 // since thread #1 is blocked. 736 737 for (int i = 1; i < kNumRequests; ++i) { 738 rv = resolver.GetProxyForURL( 739 GURL(base::StringPrintf("http://request%d", i)), 740 &results[i], callback[i].callback(), &request[i], BoundNetLog()); 741 EXPECT_EQ(ERR_IO_PENDING, rv); 742 } 743 744 // Wait for the three requests to complete (they should complete in FIFO 745 // order). 746 for (int i = 1; i < kNumRequests; ++i) { 747 EXPECT_EQ(i - 1, callback[i].WaitForResult()); 748 } 749 750 // Unblock the first thread. 751 factory->resolvers()[0]->Unblock(); 752 EXPECT_EQ(0, callback[0].WaitForResult()); 753 754 // All in all, the first thread should have seen just 1 request. And the 755 // second thread 3 requests. 756 ASSERT_EQ(2u, factory->resolvers().size()); 757 EXPECT_EQ(1, factory->resolvers()[0]->request_count()); 758 EXPECT_EQ(3, factory->resolvers()[1]->request_count()); 759 } 760 761 } // namespace 762 763 } // namespace net 764