| 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 "base/string_util.h" | |
| 6 #include "base/waitable_event.h" | |
| 7 #include "googleurl/src/gurl.h" | |
| 8 #include "net/base/net_log.h" | |
| 9 #include "net/base/net_log_unittest.h" | |
| 10 #include "net/base/net_errors.h" | |
| 11 #include "net/base/test_completion_callback.h" | |
| 12 #include "net/proxy/proxy_info.h" | |
| 13 #include "net/proxy/single_threaded_proxy_resolver.h" | |
| 14 #include "testing/gtest/include/gtest/gtest.h" | |
| 15 | |
| 16 namespace net { | |
| 17 namespace { | |
| 18 | |
| 19 // A synchronous mock ProxyResolver implementation, which can be used in | |
| 20 // conjunction with SingleThreadedProxyResolver. | |
| 21 // - returns a single-item proxy list with the query's host. | |
| 22 class MockProxyResolver : public ProxyResolver { | |
| 23 public: | |
| 24 MockProxyResolver() | |
| 25 : ProxyResolver(true /*expects_pac_bytes*/), | |
| 26 wrong_loop_(MessageLoop::current()), | |
| 27 request_count_(0), | |
| 28 purge_count_(0), | |
| 29 resolve_latency_ms_(0) {} | |
| 30 | |
| 31 // ProxyResolver implementation: | |
| 32 virtual int GetProxyForURL(const GURL& query_url, | |
| 33 ProxyInfo* results, | |
| 34 CompletionCallback* callback, | |
| 35 RequestHandle* request, | |
| 36 const BoundNetLog& net_log) { | |
| 37 if (resolve_latency_ms_) | |
| 38 PlatformThread::Sleep(resolve_latency_ms_); | |
| 39 | |
| 40 CheckIsOnWorkerThread(); | |
| 41 | |
| 42 EXPECT_TRUE(callback == NULL); | |
| 43 EXPECT_TRUE(request == NULL); | |
| 44 | |
| 45 // Write something into |net_log| (doesn't really have any meaning.) | |
| 46 net_log.BeginEvent(NetLog::TYPE_PROXY_RESOLVER_V8_DNS_RESOLVE, NULL); | |
| 47 | |
| 48 results->UseNamedProxy(query_url.host()); | |
| 49 | |
| 50 // Return a success code which represents the request's order. | |
| 51 return request_count_++; | |
| 52 } | |
| 53 | |
| 54 virtual void CancelRequest(RequestHandle request) { | |
| 55 NOTREACHED(); | |
| 56 } | |
| 57 | |
| 58 virtual int SetPacScript(const GURL& pac_url, | |
| 59 const string16& text, | |
| 60 CompletionCallback* callback) { | |
| 61 CheckIsOnWorkerThread(); | |
| 62 last_pac_script_ = text; | |
| 63 return OK; | |
| 64 } | |
| 65 | |
| 66 virtual void PurgeMemory() { | |
| 67 CheckIsOnWorkerThread(); | |
| 68 ++purge_count_; | |
| 69 } | |
| 70 | |
| 71 int purge_count() const { return purge_count_; } | |
| 72 | |
| 73 const string16& last_pac_script() const { return last_pac_script_; } | |
| 74 | |
| 75 void SetResolveLatency(int latency_ms) { | |
| 76 resolve_latency_ms_ = latency_ms; | |
| 77 } | |
| 78 | |
| 79 private: | |
| 80 void CheckIsOnWorkerThread() { | |
| 81 // We should be running on the worker thread -- while we don't know the | |
| 82 // message loop of SingleThreadedProxyResolver's worker thread, we do | |
| 83 // 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. | |
| 85 EXPECT_NE(MessageLoop::current(), wrong_loop_); | |
| 86 } | |
| 87 | |
| 88 MessageLoop* wrong_loop_; | |
| 89 int request_count_; | |
| 90 int purge_count_; | |
| 91 string16 last_pac_script_; | |
| 92 int resolve_latency_ms_; | |
| 93 }; | |
| 94 | |
| 95 | |
| 96 // A mock synchronous ProxyResolver which can be set to block upon reaching | |
| 97 // GetProxyForURL(). | |
| 98 // TODO(eroman): WaitUntilBlocked() *must* be called before calling Unblock(), | |
| 99 // otherwise there will be a race on |should_block_| since it is | |
| 100 // read without any synchronization. | |
| 101 class BlockableProxyResolver : public MockProxyResolver { | |
| 102 public: | |
| 103 BlockableProxyResolver() | |
| 104 : should_block_(false), | |
| 105 unblocked_(true, true), | |
| 106 blocked_(true, false) { | |
| 107 } | |
| 108 | |
| 109 void Block() { | |
| 110 should_block_ = true; | |
| 111 unblocked_.Reset(); | |
| 112 } | |
| 113 | |
| 114 void Unblock() { | |
| 115 should_block_ = false; | |
| 116 blocked_.Reset(); | |
| 117 unblocked_.Signal(); | |
| 118 } | |
| 119 | |
| 120 void WaitUntilBlocked() { | |
| 121 blocked_.Wait(); | |
| 122 } | |
| 123 | |
| 124 virtual int GetProxyForURL(const GURL& query_url, | |
| 125 ProxyInfo* results, | |
| 126 CompletionCallback* callback, | |
| 127 RequestHandle* request, | |
| 128 const BoundNetLog& net_log) { | |
| 129 if (should_block_) { | |
| 130 blocked_.Signal(); | |
| 131 unblocked_.Wait(); | |
| 132 } | |
| 133 | |
| 134 return MockProxyResolver::GetProxyForURL( | |
| 135 query_url, results, callback, request, net_log); | |
| 136 } | |
| 137 | |
| 138 private: | |
| 139 bool should_block_; | |
| 140 base::WaitableEvent unblocked_; | |
| 141 base::WaitableEvent blocked_; | |
| 142 }; | |
| 143 | |
| 144 TEST(SingleThreadedProxyResolverTest, Basic) { | |
| 145 MockProxyResolver* mock = new MockProxyResolver; | |
| 146 SingleThreadedProxyResolver resolver(mock); | |
| 147 | |
| 148 int rv; | |
| 149 | |
| 150 EXPECT_TRUE(resolver.expects_pac_bytes()); | |
| 151 | |
| 152 // Call SetPacScriptByData() -- verify that it reaches the synchronous | |
| 153 // resolver. | |
| 154 TestCompletionCallback set_script_callback; | |
| 155 rv = resolver.SetPacScriptByData(ASCIIToUTF16("pac script bytes"), | |
| 156 &set_script_callback); | |
| 157 EXPECT_EQ(ERR_IO_PENDING, rv); | |
| 158 EXPECT_EQ(OK, set_script_callback.WaitForResult()); | |
| 159 EXPECT_EQ(ASCIIToUTF16("pac script bytes"), mock->last_pac_script()); | |
| 160 | |
| 161 // Start request 0. | |
| 162 TestCompletionCallback callback0; | |
| 163 CapturingBoundNetLog log0(CapturingNetLog::kUnbounded); | |
| 164 ProxyInfo results0; | |
| 165 rv = resolver.GetProxyForURL( | |
| 166 GURL("http://request0"), &results0, &callback0, NULL, log0.bound()); | |
| 167 EXPECT_EQ(ERR_IO_PENDING, rv); | |
| 168 | |
| 169 // Wait for request 0 to finish. | |
| 170 rv = callback0.WaitForResult(); | |
| 171 EXPECT_EQ(0, rv); | |
| 172 EXPECT_EQ("PROXY request0:80", results0.ToPacString()); | |
| 173 | |
| 174 // The mock proxy resolver should have written 1 log entry. And | |
| 175 // on completion, this should have been copied into |log0|. | |
| 176 EXPECT_EQ(1u, log0.entries().size()); | |
| 177 | |
| 178 // Start 3 more requests (request1 to request3). | |
| 179 | |
| 180 TestCompletionCallback callback1; | |
| 181 ProxyInfo results1; | |
| 182 rv = resolver.GetProxyForURL( | |
| 183 GURL("http://request1"), &results1, &callback1, NULL, BoundNetLog()); | |
| 184 EXPECT_EQ(ERR_IO_PENDING, rv); | |
| 185 | |
| 186 TestCompletionCallback callback2; | |
| 187 ProxyInfo results2; | |
| 188 rv = resolver.GetProxyForURL( | |
| 189 GURL("http://request2"), &results2, &callback2, NULL, BoundNetLog()); | |
| 190 EXPECT_EQ(ERR_IO_PENDING, rv); | |
| 191 | |
| 192 TestCompletionCallback callback3; | |
| 193 ProxyInfo results3; | |
| 194 rv = resolver.GetProxyForURL( | |
| 195 GURL("http://request3"), &results3, &callback3, NULL, BoundNetLog()); | |
| 196 EXPECT_EQ(ERR_IO_PENDING, rv); | |
| 197 | |
| 198 // Wait for the requests to finish (they must finish in the order they were | |
| 199 // started, which is what we check for from their magic return value) | |
| 200 | |
| 201 rv = callback1.WaitForResult(); | |
| 202 EXPECT_EQ(1, rv); | |
| 203 EXPECT_EQ("PROXY request1:80", results1.ToPacString()); | |
| 204 | |
| 205 rv = callback2.WaitForResult(); | |
| 206 EXPECT_EQ(2, rv); | |
| 207 EXPECT_EQ("PROXY request2:80", results2.ToPacString()); | |
| 208 | |
| 209 rv = callback3.WaitForResult(); | |
| 210 EXPECT_EQ(3, rv); | |
| 211 EXPECT_EQ("PROXY request3:80", results3.ToPacString()); | |
| 212 | |
| 213 // Ensure that PurgeMemory() reaches the wrapped resolver and happens on the | |
| 214 // right thread. | |
| 215 EXPECT_EQ(0, mock->purge_count()); | |
| 216 resolver.PurgeMemory(); | |
| 217 // There is no way to get a callback directly when PurgeMemory() completes, so | |
| 218 // 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. | |
| 220 TestCompletionCallback dummy_callback; | |
| 221 rv = resolver.SetPacScriptByData(ASCIIToUTF16("dummy"), &dummy_callback); | |
| 222 EXPECT_EQ(OK, dummy_callback.WaitForResult()); | |
| 223 EXPECT_EQ(1, mock->purge_count()); | |
| 224 } | |
| 225 | |
| 226 // Tests that the NetLog is updated to include the time the request was waiting | |
| 227 // to be scheduled to a thread. | |
| 228 TEST(SingleThreadedProxyResolverTest, UpdatesNetLogWithThreadWait) { | |
| 229 BlockableProxyResolver* mock = new BlockableProxyResolver; | |
| 230 SingleThreadedProxyResolver resolver(mock); | |
| 231 | |
| 232 int rv; | |
| 233 | |
| 234 // Block the proxy resolver, so no request can complete. | |
| 235 mock->Block(); | |
| 236 | |
| 237 // Start request 0. | |
| 238 ProxyResolver::RequestHandle request0; | |
| 239 TestCompletionCallback callback0; | |
| 240 ProxyInfo results0; | |
| 241 CapturingBoundNetLog log0(CapturingNetLog::kUnbounded); | |
| 242 rv = resolver.GetProxyForURL( | |
| 243 GURL("http://request0"), &results0, &callback0, &request0, log0.bound()); | |
| 244 EXPECT_EQ(ERR_IO_PENDING, rv); | |
| 245 | |
| 246 // Start 2 more requests (request1 and request2). | |
| 247 | |
| 248 TestCompletionCallback callback1; | |
| 249 ProxyInfo results1; | |
| 250 CapturingBoundNetLog log1(CapturingNetLog::kUnbounded); | |
| 251 rv = resolver.GetProxyForURL( | |
| 252 GURL("http://request1"), &results1, &callback1, NULL, log1.bound()); | |
| 253 EXPECT_EQ(ERR_IO_PENDING, rv); | |
| 254 | |
| 255 ProxyResolver::RequestHandle request2; | |
| 256 TestCompletionCallback callback2; | |
| 257 ProxyInfo results2; | |
| 258 CapturingBoundNetLog log2(CapturingNetLog::kUnbounded); | |
| 259 rv = resolver.GetProxyForURL( | |
| 260 GURL("http://request2"), &results2, &callback2, &request2, log2.bound()); | |
| 261 EXPECT_EQ(ERR_IO_PENDING, rv); | |
| 262 | |
| 263 // Unblock the worker thread so the requests can continue running. | |
| 264 mock->WaitUntilBlocked(); | |
| 265 mock->Unblock(); | |
| 266 | |
| 267 // Check that request 0 completed as expected. | |
| 268 // The NetLog only has 1 entry (that came from the mock proxy resolver.) | |
| 269 EXPECT_EQ(0, callback0.WaitForResult()); | |
| 270 EXPECT_EQ("PROXY request0:80", results0.ToPacString()); | |
| 271 ASSERT_EQ(1u, log0.entries().size()); | |
| 272 | |
| 273 // Check that request 1 completed as expected. | |
| 274 EXPECT_EQ(1, callback1.WaitForResult()); | |
| 275 EXPECT_EQ("PROXY request1:80", results1.ToPacString()); | |
| 276 ASSERT_EQ(3u, log1.entries().size()); | |
| 277 EXPECT_TRUE(LogContainsBeginEvent( | |
| 278 log1.entries(), 0, | |
| 279 NetLog::TYPE_WAITING_FOR_SINGLE_PROXY_RESOLVER_THREAD)); | |
| 280 EXPECT_TRUE(LogContainsEndEvent( | |
| 281 log1.entries(), 1, | |
| 282 NetLog::TYPE_WAITING_FOR_SINGLE_PROXY_RESOLVER_THREAD)); | |
| 283 | |
| 284 // Check that request 2 completed as expected. | |
| 285 EXPECT_EQ(2, callback2.WaitForResult()); | |
| 286 EXPECT_EQ("PROXY request2:80", results2.ToPacString()); | |
| 287 ASSERT_EQ(3u, log2.entries().size()); | |
| 288 EXPECT_TRUE(LogContainsBeginEvent( | |
| 289 log2.entries(), 0, | |
| 290 NetLog::TYPE_WAITING_FOR_SINGLE_PROXY_RESOLVER_THREAD)); | |
| 291 EXPECT_TRUE(LogContainsEndEvent( | |
| 292 log2.entries(), 1, | |
| 293 NetLog::TYPE_WAITING_FOR_SINGLE_PROXY_RESOLVER_THREAD)); | |
| 294 } | |
| 295 | |
| 296 // Cancel a request which is in progress, and then cancel a request which | |
| 297 // is pending. | |
| 298 TEST(SingleThreadedProxyResolverTest, CancelRequest) { | |
| 299 BlockableProxyResolver* mock = new BlockableProxyResolver; | |
| 300 SingleThreadedProxyResolver resolver(mock); | |
| 301 | |
| 302 int rv; | |
| 303 | |
| 304 // Block the proxy resolver, so no request can complete. | |
| 305 mock->Block(); | |
| 306 | |
| 307 // Start request 0. | |
| 308 ProxyResolver::RequestHandle request0; | |
| 309 TestCompletionCallback callback0; | |
| 310 ProxyInfo results0; | |
| 311 rv = resolver.GetProxyForURL( | |
| 312 GURL("http://request0"), &results0, &callback0, &request0, BoundNetLog()); | |
| 313 EXPECT_EQ(ERR_IO_PENDING, rv); | |
| 314 | |
| 315 // Wait until requests 0 reaches the worker thread. | |
| 316 mock->WaitUntilBlocked(); | |
| 317 | |
| 318 // Start 3 more requests (request1 : request3). | |
| 319 | |
| 320 TestCompletionCallback callback1; | |
| 321 ProxyInfo results1; | |
| 322 rv = resolver.GetProxyForURL( | |
| 323 GURL("http://request1"), &results1, &callback1, NULL, BoundNetLog()); | |
| 324 EXPECT_EQ(ERR_IO_PENDING, rv); | |
| 325 | |
| 326 ProxyResolver::RequestHandle request2; | |
| 327 TestCompletionCallback callback2; | |
| 328 ProxyInfo results2; | |
| 329 rv = resolver.GetProxyForURL( | |
| 330 GURL("http://request2"), &results2, &callback2, &request2, BoundNetLog()); | |
| 331 EXPECT_EQ(ERR_IO_PENDING, rv); | |
| 332 | |
| 333 TestCompletionCallback callback3; | |
| 334 ProxyInfo results3; | |
| 335 rv = resolver.GetProxyForURL( | |
| 336 GURL("http://request3"), &results3, &callback3, NULL, BoundNetLog()); | |
| 337 EXPECT_EQ(ERR_IO_PENDING, rv); | |
| 338 | |
| 339 // Cancel request0 (inprogress) and request2 (pending). | |
| 340 resolver.CancelRequest(request0); | |
| 341 resolver.CancelRequest(request2); | |
| 342 | |
| 343 // Unblock the worker thread so the requests can continue running. | |
| 344 mock->Unblock(); | |
| 345 | |
| 346 // Wait for requests 1 and 3 to finish. | |
| 347 | |
| 348 rv = callback1.WaitForResult(); | |
| 349 EXPECT_EQ(1, rv); | |
| 350 EXPECT_EQ("PROXY request1:80", results1.ToPacString()); | |
| 351 | |
| 352 rv = callback3.WaitForResult(); | |
| 353 // Note that since request2 was cancelled before reaching the resolver, | |
| 354 // the request count is 2 and not 3 here. | |
| 355 EXPECT_EQ(2, rv); | |
| 356 EXPECT_EQ("PROXY request3:80", results3.ToPacString()); | |
| 357 | |
| 358 // Requests 0 and 2 which were cancelled, hence their completion callbacks | |
| 359 // were never summoned. | |
| 360 EXPECT_FALSE(callback0.have_result()); | |
| 361 EXPECT_FALSE(callback2.have_result()); | |
| 362 } | |
| 363 | |
| 364 // Test that deleting SingleThreadedProxyResolver while requests are | |
| 365 // outstanding cancels them (and doesn't leak anything). | |
| 366 TEST(SingleThreadedProxyResolverTest, CancelRequestByDeleting) { | |
| 367 BlockableProxyResolver* mock = new BlockableProxyResolver; | |
| 368 scoped_ptr<SingleThreadedProxyResolver> resolver( | |
| 369 new SingleThreadedProxyResolver(mock)); | |
| 370 | |
| 371 int rv; | |
| 372 | |
| 373 // Block the proxy resolver, so no request can complete. | |
| 374 mock->Block(); | |
| 375 | |
| 376 // Start 3 requests. | |
| 377 | |
| 378 TestCompletionCallback callback0; | |
| 379 ProxyInfo results0; | |
| 380 rv = resolver->GetProxyForURL( | |
| 381 GURL("http://request0"), &results0, &callback0, NULL, BoundNetLog()); | |
| 382 EXPECT_EQ(ERR_IO_PENDING, rv); | |
| 383 | |
| 384 TestCompletionCallback callback1; | |
| 385 ProxyInfo results1; | |
| 386 rv = resolver->GetProxyForURL( | |
| 387 GURL("http://request1"), &results1, &callback1, NULL, BoundNetLog()); | |
| 388 EXPECT_EQ(ERR_IO_PENDING, rv); | |
| 389 | |
| 390 TestCompletionCallback callback2; | |
| 391 ProxyInfo results2; | |
| 392 rv = resolver->GetProxyForURL( | |
| 393 GURL("http://request2"), &results2, &callback2, NULL, BoundNetLog()); | |
| 394 EXPECT_EQ(ERR_IO_PENDING, rv); | |
| 395 | |
| 396 // Wait until request 0 reaches the worker thread. | |
| 397 mock->WaitUntilBlocked(); | |
| 398 | |
| 399 // Add some latency, to improve the chance that when | |
| 400 // SingleThreadedProxyResolver is deleted below we are still running inside | |
| 401 // 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 | |
| 403 // interesting case to test. | |
| 404 mock->SetResolveLatency(100); | |
| 405 | |
| 406 // Unblock the worker thread and delete the underlying | |
| 407 // SingleThreadedProxyResolver immediately. | |
| 408 mock->Unblock(); | |
| 409 resolver.reset(); | |
| 410 | |
| 411 // Give any posted tasks a chance to run (in case there is badness). | |
| 412 MessageLoop::current()->RunAllPending(); | |
| 413 | |
| 414 // Check that none of the outstanding requests were completed. | |
| 415 EXPECT_FALSE(callback0.have_result()); | |
| 416 EXPECT_FALSE(callback1.have_result()); | |
| 417 EXPECT_FALSE(callback2.have_result()); | |
| 418 } | |
| 419 | |
| 420 // Cancel an outstanding call to SetPacScriptByData(). | |
| 421 TEST(SingleThreadedProxyResolverTest, CancelSetPacScript) { | |
| 422 BlockableProxyResolver* mock = new BlockableProxyResolver; | |
| 423 SingleThreadedProxyResolver resolver(mock); | |
| 424 | |
| 425 int rv; | |
| 426 | |
| 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; | |
| 442 rv = resolver.SetPacScriptByData(ASCIIToUTF16("data"), | |
| 443 &set_pac_script_callback); | |
| 444 EXPECT_EQ(ERR_IO_PENDING, rv); | |
| 445 | |
| 446 // Cancel the SetPacScriptByData request (it can't have finished yet, | |
| 447 // since the single-thread is currently blocked). | |
| 448 resolver.CancelSetPacScript(); | |
| 449 | |
| 450 // Start 1 more request. | |
| 451 | |
| 452 TestCompletionCallback callback1; | |
| 453 ProxyInfo results1; | |
| 454 rv = resolver.GetProxyForURL( | |
| 455 GURL("http://request1"), &results1, &callback1, NULL, BoundNetLog()); | |
| 456 EXPECT_EQ(ERR_IO_PENDING, rv); | |
| 457 | |
| 458 // Unblock the worker thread so the requests can continue running. | |
| 459 mock->Unblock(); | |
| 460 | |
| 461 // Wait for requests 0 and 1 to finish. | |
| 462 | |
| 463 rv = callback0.WaitForResult(); | |
| 464 EXPECT_EQ(0, rv); | |
| 465 EXPECT_EQ("PROXY request0:80", results0.ToPacString()); | |
| 466 | |
| 467 rv = callback1.WaitForResult(); | |
| 468 EXPECT_EQ(1, rv); | |
| 469 EXPECT_EQ("PROXY request1:80", results1.ToPacString()); | |
| 470 | |
| 471 // The SetPacScript callback should never have been completed. | |
| 472 EXPECT_FALSE(set_pac_script_callback.have_result()); | |
| 473 } | |
| 474 | |
| 475 } // namespace | |
| 476 } // namespace net | |
| OLD | NEW |