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