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