OLD | NEW |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
| 5 #include "net/proxy/multi_threaded_proxy_resolver.h" |
| 6 |
| 7 #include "base/stl_util-inl.h" |
5 #include "base/string_util.h" | 8 #include "base/string_util.h" |
6 #include "base/waitable_event.h" | 9 #include "base/waitable_event.h" |
7 #include "googleurl/src/gurl.h" | 10 #include "googleurl/src/gurl.h" |
8 #include "net/base/net_log.h" | 11 #include "net/base/net_log.h" |
9 #include "net/base/net_log_unittest.h" | 12 #include "net/base/net_log_unittest.h" |
10 #include "net/base/net_errors.h" | 13 #include "net/base/net_errors.h" |
11 #include "net/base/test_completion_callback.h" | 14 #include "net/base/test_completion_callback.h" |
12 #include "net/proxy/proxy_info.h" | 15 #include "net/proxy/proxy_info.h" |
13 #include "net/proxy/single_threaded_proxy_resolver.h" | |
14 #include "testing/gtest/include/gtest/gtest.h" | 16 #include "testing/gtest/include/gtest/gtest.h" |
15 | 17 |
16 namespace net { | 18 namespace net { |
| 19 |
17 namespace { | 20 namespace { |
18 | 21 |
19 // A synchronous mock ProxyResolver implementation, which can be used in | 22 // A synchronous mock ProxyResolver implementation, which can be used in |
20 // conjunction with SingleThreadedProxyResolver. | 23 // conjunction with MultiThreadedProxyResolver. |
21 // - returns a single-item proxy list with the query's host. | 24 // - returns a single-item proxy list with the query's host. |
22 class MockProxyResolver : public ProxyResolver { | 25 class MockProxyResolver : public ProxyResolver { |
23 public: | 26 public: |
24 MockProxyResolver() | 27 MockProxyResolver() |
25 : ProxyResolver(true /*expects_pac_bytes*/), | 28 : ProxyResolver(true /*expects_pac_bytes*/), |
26 wrong_loop_(MessageLoop::current()), | 29 wrong_loop_(MessageLoop::current()), |
27 request_count_(0), | 30 request_count_(0), |
28 purge_count_(0), | 31 purge_count_(0), |
29 resolve_latency_ms_(0) {} | 32 resolve_latency_ms_(0) {} |
30 | 33 |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
62 last_pac_script_ = text; | 65 last_pac_script_ = text; |
63 return OK; | 66 return OK; |
64 } | 67 } |
65 | 68 |
66 virtual void PurgeMemory() { | 69 virtual void PurgeMemory() { |
67 CheckIsOnWorkerThread(); | 70 CheckIsOnWorkerThread(); |
68 ++purge_count_; | 71 ++purge_count_; |
69 } | 72 } |
70 | 73 |
71 int purge_count() const { return purge_count_; } | 74 int purge_count() const { return purge_count_; } |
| 75 int request_count() const { return request_count_; } |
72 | 76 |
73 const string16& last_pac_script() const { return last_pac_script_; } | 77 const string16& last_pac_script() const { return last_pac_script_; } |
74 | 78 |
75 void SetResolveLatency(int latency_ms) { | 79 void SetResolveLatency(int latency_ms) { |
76 resolve_latency_ms_ = latency_ms; | 80 resolve_latency_ms_ = latency_ms; |
77 } | 81 } |
78 | 82 |
79 private: | 83 private: |
80 void CheckIsOnWorkerThread() { | 84 void CheckIsOnWorkerThread() { |
81 // We should be running on the worker thread -- while we don't know the | 85 // We should be running on the worker thread -- while we don't know the |
82 // message loop of SingleThreadedProxyResolver's worker thread, we do | 86 // message loop of MultiThreadedProxyResolver's worker thread, we do |
83 // know that it is going to be distinct from the loop running the | 87 // know that it is going to be distinct from the loop running the |
84 // test, so at least make sure it isn't the main loop. | 88 // test, so at least make sure it isn't the main loop. |
85 EXPECT_NE(MessageLoop::current(), wrong_loop_); | 89 EXPECT_NE(MessageLoop::current(), wrong_loop_); |
86 } | 90 } |
87 | 91 |
88 MessageLoop* wrong_loop_; | 92 MessageLoop* wrong_loop_; |
89 int request_count_; | 93 int request_count_; |
90 int purge_count_; | 94 int purge_count_; |
91 string16 last_pac_script_; | 95 string16 last_pac_script_; |
92 int resolve_latency_ms_; | 96 int resolve_latency_ms_; |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
134 return MockProxyResolver::GetProxyForURL( | 138 return MockProxyResolver::GetProxyForURL( |
135 query_url, results, callback, request, net_log); | 139 query_url, results, callback, request, net_log); |
136 } | 140 } |
137 | 141 |
138 private: | 142 private: |
139 bool should_block_; | 143 bool should_block_; |
140 base::WaitableEvent unblocked_; | 144 base::WaitableEvent unblocked_; |
141 base::WaitableEvent blocked_; | 145 base::WaitableEvent blocked_; |
142 }; | 146 }; |
143 | 147 |
144 TEST(SingleThreadedProxyResolverTest, Basic) { | 148 // ForwardingProxyResolver forwards all requests to |impl|. |
145 MockProxyResolver* mock = new MockProxyResolver; | 149 class ForwardingProxyResolver : public ProxyResolver { |
146 SingleThreadedProxyResolver resolver(mock); | 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); |
147 | 229 |
148 int rv; | 230 int rv; |
149 | 231 |
150 EXPECT_TRUE(resolver.expects_pac_bytes()); | 232 EXPECT_TRUE(resolver.expects_pac_bytes()); |
151 | 233 |
152 // Call SetPacScriptByData() -- verify that it reaches the synchronous | 234 // Call SetPacScriptByData() -- verify that it reaches the synchronous |
153 // resolver. | 235 // resolver. |
154 TestCompletionCallback set_script_callback; | 236 TestCompletionCallback set_script_callback; |
155 rv = resolver.SetPacScriptByData(ASCIIToUTF16("pac script bytes"), | 237 rv = resolver.SetPacScriptByData(ASCIIToUTF16("pac script bytes"), |
156 &set_script_callback); | 238 &set_script_callback); |
157 EXPECT_EQ(ERR_IO_PENDING, rv); | 239 EXPECT_EQ(ERR_IO_PENDING, rv); |
158 EXPECT_EQ(OK, set_script_callback.WaitForResult()); | 240 EXPECT_EQ(OK, set_script_callback.WaitForResult()); |
159 EXPECT_EQ(ASCIIToUTF16("pac script bytes"), mock->last_pac_script()); | 241 EXPECT_EQ(ASCIIToUTF16("pac script bytes"), mock->last_pac_script()); |
160 | 242 |
161 // Start request 0. | 243 // Start request 0. |
162 TestCompletionCallback callback0; | 244 TestCompletionCallback callback0; |
163 CapturingBoundNetLog log0(CapturingNetLog::kUnbounded); | 245 CapturingBoundNetLog log0(CapturingNetLog::kUnbounded); |
164 ProxyInfo results0; | 246 ProxyInfo results0; |
165 rv = resolver.GetProxyForURL( | 247 rv = resolver.GetProxyForURL( |
166 GURL("http://request0"), &results0, &callback0, NULL, log0.bound()); | 248 GURL("http://request0"), &results0, &callback0, NULL, log0.bound()); |
167 EXPECT_EQ(ERR_IO_PENDING, rv); | 249 EXPECT_EQ(ERR_IO_PENDING, rv); |
168 | 250 |
169 // Wait for request 0 to finish. | 251 // Wait for request 0 to finish. |
170 rv = callback0.WaitForResult(); | 252 rv = callback0.WaitForResult(); |
171 EXPECT_EQ(0, rv); | 253 EXPECT_EQ(0, rv); |
172 EXPECT_EQ("PROXY request0:80", results0.ToPacString()); | 254 EXPECT_EQ("PROXY request0:80", results0.ToPacString()); |
173 | 255 |
174 // The mock proxy resolver should have written 1 log entry. And | 256 // The mock proxy resolver should have written 1 log entry. And |
175 // on completion, this should have been copied into |log0|. | 257 // on completion, this should have been copied into |log0|. |
176 EXPECT_EQ(1u, log0.entries().size()); | 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); |
177 | 263 |
178 // Start 3 more requests (request1 to request3). | 264 // Start 3 more requests (request1 to request3). |
179 | 265 |
180 TestCompletionCallback callback1; | 266 TestCompletionCallback callback1; |
181 ProxyInfo results1; | 267 ProxyInfo results1; |
182 rv = resolver.GetProxyForURL( | 268 rv = resolver.GetProxyForURL( |
183 GURL("http://request1"), &results1, &callback1, NULL, BoundNetLog()); | 269 GURL("http://request1"), &results1, &callback1, NULL, BoundNetLog()); |
184 EXPECT_EQ(ERR_IO_PENDING, rv); | 270 EXPECT_EQ(ERR_IO_PENDING, rv); |
185 | 271 |
186 TestCompletionCallback callback2; | 272 TestCompletionCallback callback2; |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
218 // we queue up a dummy request after the PurgeMemory() call and wait until it | 304 // we queue up a dummy request after the PurgeMemory() call and wait until it |
219 // finishes to ensure PurgeMemory() has had a chance to run. | 305 // finishes to ensure PurgeMemory() has had a chance to run. |
220 TestCompletionCallback dummy_callback; | 306 TestCompletionCallback dummy_callback; |
221 rv = resolver.SetPacScriptByData(ASCIIToUTF16("dummy"), &dummy_callback); | 307 rv = resolver.SetPacScriptByData(ASCIIToUTF16("dummy"), &dummy_callback); |
222 EXPECT_EQ(OK, dummy_callback.WaitForResult()); | 308 EXPECT_EQ(OK, dummy_callback.WaitForResult()); |
223 EXPECT_EQ(1, mock->purge_count()); | 309 EXPECT_EQ(1, mock->purge_count()); |
224 } | 310 } |
225 | 311 |
226 // Tests that the NetLog is updated to include the time the request was waiting | 312 // Tests that the NetLog is updated to include the time the request was waiting |
227 // to be scheduled to a thread. | 313 // to be scheduled to a thread. |
228 TEST(SingleThreadedProxyResolverTest, UpdatesNetLogWithThreadWait) { | 314 TEST(MultiThreadedProxyResolverTest, |
229 BlockableProxyResolver* mock = new BlockableProxyResolver; | 315 SingleThread_UpdatesNetLogWithThreadWait) { |
230 SingleThreadedProxyResolver resolver(mock); | 316 const size_t kNumThreads = 1u; |
| 317 scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver); |
| 318 MultiThreadedProxyResolver resolver( |
| 319 new ForwardingProxyResolverFactory(mock.get()), kNumThreads); |
231 | 320 |
232 int rv; | 321 int rv; |
233 | 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 |
234 // Block the proxy resolver, so no request can complete. | 328 // Block the proxy resolver, so no request can complete. |
235 mock->Block(); | 329 mock->Block(); |
236 | 330 |
237 // Start request 0. | 331 // Start request 0. |
238 ProxyResolver::RequestHandle request0; | 332 ProxyResolver::RequestHandle request0; |
239 TestCompletionCallback callback0; | 333 TestCompletionCallback callback0; |
240 ProxyInfo results0; | 334 ProxyInfo results0; |
241 CapturingBoundNetLog log0(CapturingNetLog::kUnbounded); | 335 CapturingBoundNetLog log0(CapturingNetLog::kUnbounded); |
242 rv = resolver.GetProxyForURL( | 336 rv = resolver.GetProxyForURL( |
243 GURL("http://request0"), &results0, &callback0, &request0, log0.bound()); | 337 GURL("http://request0"), &results0, &callback0, &request0, log0.bound()); |
(...skipping 14 matching lines...) Expand all Loading... |
258 CapturingBoundNetLog log2(CapturingNetLog::kUnbounded); | 352 CapturingBoundNetLog log2(CapturingNetLog::kUnbounded); |
259 rv = resolver.GetProxyForURL( | 353 rv = resolver.GetProxyForURL( |
260 GURL("http://request2"), &results2, &callback2, &request2, log2.bound()); | 354 GURL("http://request2"), &results2, &callback2, &request2, log2.bound()); |
261 EXPECT_EQ(ERR_IO_PENDING, rv); | 355 EXPECT_EQ(ERR_IO_PENDING, rv); |
262 | 356 |
263 // Unblock the worker thread so the requests can continue running. | 357 // Unblock the worker thread so the requests can continue running. |
264 mock->WaitUntilBlocked(); | 358 mock->WaitUntilBlocked(); |
265 mock->Unblock(); | 359 mock->Unblock(); |
266 | 360 |
267 // Check that request 0 completed as expected. | 361 // Check that request 0 completed as expected. |
268 // The NetLog only has 1 entry (that came from the mock proxy resolver.) | 362 // The NetLog has 1 entry that came from the MultiThreadedProxyResolver, and |
| 363 // 1 entry from the mock proxy resolver. |
269 EXPECT_EQ(0, callback0.WaitForResult()); | 364 EXPECT_EQ(0, callback0.WaitForResult()); |
270 EXPECT_EQ("PROXY request0:80", results0.ToPacString()); | 365 EXPECT_EQ("PROXY request0:80", results0.ToPacString()); |
271 ASSERT_EQ(1u, log0.entries().size()); | 366 ASSERT_EQ(2u, log0.entries().size()); |
| 367 EXPECT_EQ(NetLog::TYPE_SUBMITTED_TO_RESOLVER_THREAD, |
| 368 log0.entries()[0].type); |
272 | 369 |
273 // Check that request 1 completed as expected. | 370 // Check that request 1 completed as expected. |
274 EXPECT_EQ(1, callback1.WaitForResult()); | 371 EXPECT_EQ(1, callback1.WaitForResult()); |
275 EXPECT_EQ("PROXY request1:80", results1.ToPacString()); | 372 EXPECT_EQ("PROXY request1:80", results1.ToPacString()); |
276 ASSERT_EQ(3u, log1.entries().size()); | 373 ASSERT_EQ(4u, log1.entries().size()); |
277 EXPECT_TRUE(LogContainsBeginEvent( | 374 EXPECT_TRUE(LogContainsBeginEvent( |
278 log1.entries(), 0, | 375 log1.entries(), 0, |
279 NetLog::TYPE_WAITING_FOR_SINGLE_PROXY_RESOLVER_THREAD)); | 376 NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD)); |
280 EXPECT_TRUE(LogContainsEndEvent( | 377 EXPECT_TRUE(LogContainsEndEvent( |
281 log1.entries(), 1, | 378 log1.entries(), 1, |
282 NetLog::TYPE_WAITING_FOR_SINGLE_PROXY_RESOLVER_THREAD)); | 379 NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD)); |
283 | 380 |
284 // Check that request 2 completed as expected. | 381 // Check that request 2 completed as expected. |
285 EXPECT_EQ(2, callback2.WaitForResult()); | 382 EXPECT_EQ(2, callback2.WaitForResult()); |
286 EXPECT_EQ("PROXY request2:80", results2.ToPacString()); | 383 EXPECT_EQ("PROXY request2:80", results2.ToPacString()); |
287 ASSERT_EQ(3u, log2.entries().size()); | 384 ASSERT_EQ(4u, log2.entries().size()); |
288 EXPECT_TRUE(LogContainsBeginEvent( | 385 EXPECT_TRUE(LogContainsBeginEvent( |
289 log2.entries(), 0, | 386 log2.entries(), 0, |
290 NetLog::TYPE_WAITING_FOR_SINGLE_PROXY_RESOLVER_THREAD)); | 387 NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD)); |
291 EXPECT_TRUE(LogContainsEndEvent( | 388 EXPECT_TRUE(LogContainsEndEvent( |
292 log2.entries(), 1, | 389 log2.entries(), 1, |
293 NetLog::TYPE_WAITING_FOR_SINGLE_PROXY_RESOLVER_THREAD)); | 390 NetLog::TYPE_WAITING_FOR_PROXY_RESOLVER_THREAD)); |
294 } | 391 } |
295 | 392 |
296 // Cancel a request which is in progress, and then cancel a request which | 393 // Cancel a request which is in progress, and then cancel a request which |
297 // is pending. | 394 // is pending. |
298 TEST(SingleThreadedProxyResolverTest, CancelRequest) { | 395 TEST(MultiThreadedProxyResolverTest, SingleThread_CancelRequest) { |
299 BlockableProxyResolver* mock = new BlockableProxyResolver; | 396 const size_t kNumThreads = 1u; |
300 SingleThreadedProxyResolver resolver(mock); | 397 scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver); |
| 398 MultiThreadedProxyResolver resolver( |
| 399 new ForwardingProxyResolverFactory(mock.get()), |
| 400 kNumThreads); |
301 | 401 |
302 int rv; | 402 int rv; |
303 | 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 |
304 // Block the proxy resolver, so no request can complete. | 409 // Block the proxy resolver, so no request can complete. |
305 mock->Block(); | 410 mock->Block(); |
306 | 411 |
307 // Start request 0. | 412 // Start request 0. |
308 ProxyResolver::RequestHandle request0; | 413 ProxyResolver::RequestHandle request0; |
309 TestCompletionCallback callback0; | 414 TestCompletionCallback callback0; |
310 ProxyInfo results0; | 415 ProxyInfo results0; |
311 rv = resolver.GetProxyForURL( | 416 rv = resolver.GetProxyForURL( |
312 GURL("http://request0"), &results0, &callback0, &request0, BoundNetLog()); | 417 GURL("http://request0"), &results0, &callback0, &request0, BoundNetLog()); |
313 EXPECT_EQ(ERR_IO_PENDING, rv); | 418 EXPECT_EQ(ERR_IO_PENDING, rv); |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
354 // the request count is 2 and not 3 here. | 459 // the request count is 2 and not 3 here. |
355 EXPECT_EQ(2, rv); | 460 EXPECT_EQ(2, rv); |
356 EXPECT_EQ("PROXY request3:80", results3.ToPacString()); | 461 EXPECT_EQ("PROXY request3:80", results3.ToPacString()); |
357 | 462 |
358 // Requests 0 and 2 which were cancelled, hence their completion callbacks | 463 // Requests 0 and 2 which were cancelled, hence their completion callbacks |
359 // were never summoned. | 464 // were never summoned. |
360 EXPECT_FALSE(callback0.have_result()); | 465 EXPECT_FALSE(callback0.have_result()); |
361 EXPECT_FALSE(callback2.have_result()); | 466 EXPECT_FALSE(callback2.have_result()); |
362 } | 467 } |
363 | 468 |
364 // Test that deleting SingleThreadedProxyResolver while requests are | 469 // Test that deleting MultiThreadedProxyResolver while requests are |
365 // outstanding cancels them (and doesn't leak anything). | 470 // outstanding cancels them (and doesn't leak anything). |
366 TEST(SingleThreadedProxyResolverTest, CancelRequestByDeleting) { | 471 TEST(MultiThreadedProxyResolverTest, SingleThread_CancelRequestByDeleting) { |
367 BlockableProxyResolver* mock = new BlockableProxyResolver; | 472 const size_t kNumThreads = 1u; |
368 scoped_ptr<SingleThreadedProxyResolver> resolver( | 473 scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver); |
369 new SingleThreadedProxyResolver(mock)); | 474 scoped_ptr<MultiThreadedProxyResolver> resolver( |
| 475 new MultiThreadedProxyResolver( |
| 476 new ForwardingProxyResolverFactory(mock.get()), kNumThreads)); |
370 | 477 |
371 int rv; | 478 int rv; |
372 | 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 |
373 // Block the proxy resolver, so no request can complete. | 485 // Block the proxy resolver, so no request can complete. |
374 mock->Block(); | 486 mock->Block(); |
375 | 487 |
376 // Start 3 requests. | 488 // Start 3 requests. |
377 | 489 |
378 TestCompletionCallback callback0; | 490 TestCompletionCallback callback0; |
379 ProxyInfo results0; | 491 ProxyInfo results0; |
380 rv = resolver->GetProxyForURL( | 492 rv = resolver->GetProxyForURL( |
381 GURL("http://request0"), &results0, &callback0, NULL, BoundNetLog()); | 493 GURL("http://request0"), &results0, &callback0, NULL, BoundNetLog()); |
382 EXPECT_EQ(ERR_IO_PENDING, rv); | 494 EXPECT_EQ(ERR_IO_PENDING, rv); |
383 | 495 |
384 TestCompletionCallback callback1; | 496 TestCompletionCallback callback1; |
385 ProxyInfo results1; | 497 ProxyInfo results1; |
386 rv = resolver->GetProxyForURL( | 498 rv = resolver->GetProxyForURL( |
387 GURL("http://request1"), &results1, &callback1, NULL, BoundNetLog()); | 499 GURL("http://request1"), &results1, &callback1, NULL, BoundNetLog()); |
388 EXPECT_EQ(ERR_IO_PENDING, rv); | 500 EXPECT_EQ(ERR_IO_PENDING, rv); |
389 | 501 |
390 TestCompletionCallback callback2; | 502 TestCompletionCallback callback2; |
391 ProxyInfo results2; | 503 ProxyInfo results2; |
392 rv = resolver->GetProxyForURL( | 504 rv = resolver->GetProxyForURL( |
393 GURL("http://request2"), &results2, &callback2, NULL, BoundNetLog()); | 505 GURL("http://request2"), &results2, &callback2, NULL, BoundNetLog()); |
394 EXPECT_EQ(ERR_IO_PENDING, rv); | 506 EXPECT_EQ(ERR_IO_PENDING, rv); |
395 | 507 |
396 // Wait until request 0 reaches the worker thread. | 508 // Wait until request 0 reaches the worker thread. |
397 mock->WaitUntilBlocked(); | 509 mock->WaitUntilBlocked(); |
398 | 510 |
399 // Add some latency, to improve the chance that when | 511 // Add some latency, to improve the chance that when |
400 // SingleThreadedProxyResolver is deleted below we are still running inside | 512 // MultiThreadedProxyResolver is deleted below we are still running inside |
401 // of the worker thread. The test will pass regardless, so this race doesn't | 513 // of the worker thread. The test will pass regardless, so this race doesn't |
402 // cause flakiness. However the destruction during execution is a more | 514 // cause flakiness. However the destruction during execution is a more |
403 // interesting case to test. | 515 // interesting case to test. |
404 mock->SetResolveLatency(100); | 516 mock->SetResolveLatency(100); |
405 | 517 |
406 // Unblock the worker thread and delete the underlying | 518 // Unblock the worker thread and delete the underlying |
407 // SingleThreadedProxyResolver immediately. | 519 // MultiThreadedProxyResolver immediately. |
408 mock->Unblock(); | 520 mock->Unblock(); |
409 resolver.reset(); | 521 resolver.reset(); |
410 | 522 |
411 // Give any posted tasks a chance to run (in case there is badness). | 523 // Give any posted tasks a chance to run (in case there is badness). |
412 MessageLoop::current()->RunAllPending(); | 524 MessageLoop::current()->RunAllPending(); |
413 | 525 |
414 // Check that none of the outstanding requests were completed. | 526 // Check that none of the outstanding requests were completed. |
415 EXPECT_FALSE(callback0.have_result()); | 527 EXPECT_FALSE(callback0.have_result()); |
416 EXPECT_FALSE(callback1.have_result()); | 528 EXPECT_FALSE(callback1.have_result()); |
417 EXPECT_FALSE(callback2.have_result()); | 529 EXPECT_FALSE(callback2.have_result()); |
418 } | 530 } |
419 | 531 |
420 // Cancel an outstanding call to SetPacScriptByData(). | 532 // Cancel an outstanding call to SetPacScriptByData(). |
421 TEST(SingleThreadedProxyResolverTest, CancelSetPacScript) { | 533 TEST(MultiThreadedProxyResolverTest, SingleThread_CancelSetPacScript) { |
422 BlockableProxyResolver* mock = new BlockableProxyResolver; | 534 const size_t kNumThreads = 1u; |
423 SingleThreadedProxyResolver resolver(mock); | 535 scoped_ptr<BlockableProxyResolver> mock(new BlockableProxyResolver); |
| 536 MultiThreadedProxyResolver resolver( |
| 537 new ForwardingProxyResolverFactory(mock.get()), kNumThreads); |
424 | 538 |
425 int rv; | 539 int rv; |
426 | 540 |
427 // Block the proxy resolver, so no request can complete. | |
428 mock->Block(); | |
429 | |
430 // Start request 0. | |
431 ProxyResolver::RequestHandle request0; | |
432 TestCompletionCallback callback0; | |
433 ProxyInfo results0; | |
434 rv = resolver.GetProxyForURL( | |
435 GURL("http://request0"), &results0, &callback0, &request0, BoundNetLog()); | |
436 EXPECT_EQ(ERR_IO_PENDING, rv); | |
437 | |
438 // Wait until requests 0 reaches the worker thread. | |
439 mock->WaitUntilBlocked(); | |
440 | |
441 TestCompletionCallback set_pac_script_callback; | 541 TestCompletionCallback set_pac_script_callback; |
442 rv = resolver.SetPacScriptByData(ASCIIToUTF16("data"), | 542 rv = resolver.SetPacScriptByData(ASCIIToUTF16("data"), |
443 &set_pac_script_callback); | 543 &set_pac_script_callback); |
444 EXPECT_EQ(ERR_IO_PENDING, rv); | 544 EXPECT_EQ(ERR_IO_PENDING, rv); |
445 | 545 |
446 // Cancel the SetPacScriptByData request (it can't have finished yet, | 546 // Cancel the SetPacScriptByData request. |
447 // since the single-thread is currently blocked). | |
448 resolver.CancelSetPacScript(); | 547 resolver.CancelSetPacScript(); |
449 | 548 |
450 // Start 1 more request. | 549 // Start another SetPacScript request |
451 | 550 TestCompletionCallback set_pac_script_callback2; |
452 TestCompletionCallback callback1; | 551 rv = resolver.SetPacScriptByData(ASCIIToUTF16("data2"), |
453 ProxyInfo results1; | 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. |
454 rv = resolver.GetProxyForURL( | 595 rv = resolver.GetProxyForURL( |
455 GURL("http://request1"), &results1, &callback1, NULL, BoundNetLog()); | 596 GURL("http://request0"), &results[0], &callback[0], &request[0], |
456 EXPECT_EQ(ERR_IO_PENDING, rv); | 597 BoundNetLog()); |
457 | 598 EXPECT_EQ(ERR_IO_PENDING, rv); |
458 // Unblock the worker thread so the requests can continue running. | 599 |
459 mock->Unblock(); | 600 // Wait for request 0 to finish. |
460 | 601 rv = callback[0].WaitForResult(); |
461 // Wait for requests 0 and 1 to finish. | |
462 | |
463 rv = callback0.WaitForResult(); | |
464 EXPECT_EQ(0, rv); | 602 EXPECT_EQ(0, rv); |
465 EXPECT_EQ("PROXY request0:80", results0.ToPacString()); | 603 EXPECT_EQ("PROXY request0:80", results[0].ToPacString()); |
466 | 604 ASSERT_EQ(1u, factory->resolvers().size()); |
467 rv = callback1.WaitForResult(); | 605 EXPECT_EQ(1, factory->resolvers()[0]->request_count()); |
468 EXPECT_EQ(1, rv); | 606 |
469 EXPECT_EQ("PROXY request1:80", results1.ToPacString()); | 607 MessageLoop::current()->RunAllPending(); |
470 | 608 |
471 // The SetPacScript callback should never have been completed. | 609 // We now start 8 requests in parallel -- this will cause the maximum of |
472 EXPECT_FALSE(set_pac_script_callback.have_result()); | 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()); |
473 } | 738 } |
474 | 739 |
475 } // namespace | 740 } // namespace |
| 741 |
476 } // namespace net | 742 } // namespace net |
OLD | NEW |