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