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 "chrome/browser/net/resolve_proxy_msg_helper.h" |
| 6 |
| 7 #include "base/waitable_event.h" |
| 8 #include "net/base/net_errors.h" |
| 9 #include "testing/gtest/include/gtest/gtest.h" |
| 10 |
| 11 // This ProxyConfigService always returns "http://pac" as the PAC url to use. |
| 12 class MockProxyConfigService: public net::ProxyConfigService { |
| 13 public: |
| 14 virtual int GetProxyConfig(net::ProxyConfig* results) { |
| 15 results->pac_url = GURL("http://pac"); |
| 16 return net::OK; |
| 17 } |
| 18 }; |
| 19 |
| 20 // This PAC resolver always returns the hostname of the query URL as the |
| 21 // proxy to use. The Block() method will make GetProxyForURL() hang until |
| 22 // Unblock() is called. |
| 23 class MockProxyResolver : public net::ProxyResolver { |
| 24 public: |
| 25 explicit MockProxyResolver() : event_(false, false), is_blocked_(false) { |
| 26 } |
| 27 |
| 28 virtual int GetProxyForURL(const GURL& query_url, |
| 29 const GURL& /*pac_url*/, |
| 30 net::ProxyInfo* results) { |
| 31 if (is_blocked_) |
| 32 event_.Wait(); |
| 33 results->UseNamedProxy(query_url.host()); |
| 34 return net::OK; |
| 35 } |
| 36 |
| 37 void Block() { |
| 38 is_blocked_ = true; |
| 39 event_.Reset(); |
| 40 } |
| 41 |
| 42 void Unblock() { |
| 43 is_blocked_ = false; |
| 44 event_.Signal(); |
| 45 } |
| 46 |
| 47 private: |
| 48 base::WaitableEvent event_; |
| 49 bool is_blocked_; |
| 50 }; |
| 51 |
| 52 // This struct holds the values that were passed to |
| 53 // Delegate::OnResolveProxyCompleted(). The caller should use WaitUntilDone() |
| 54 // to block until the result has been populated. |
| 55 struct ResultFuture { |
| 56 public: |
| 57 ResultFuture() |
| 58 : reply_msg(NULL), |
| 59 error_code(0), |
| 60 started_(false, false), |
| 61 completed_(false, false) { |
| 62 } |
| 63 |
| 64 // Wait until the request has completed. In other words we have invoked: |
| 65 // ResolveProxyMsgHelper::Delegate::OnResolveProxyCompleted. |
| 66 void WaitUntilDone() { |
| 67 completed_.Wait(); |
| 68 } |
| 69 |
| 70 // Wait until the request has been sent to ResolveProxyMsgHelper. |
| 71 void WaitUntilStarted() { |
| 72 started_.Wait(); |
| 73 } |
| 74 |
| 75 bool TimedWaitUntilDone(const base::TimeDelta& max_time) { |
| 76 return completed_.TimedWait(max_time); |
| 77 } |
| 78 |
| 79 // These fields are only valid after returning from WaitUntilDone(). |
| 80 IPC::Message* reply_msg; |
| 81 int error_code; |
| 82 std::string proxy_list; |
| 83 |
| 84 private: |
| 85 friend class AsyncRequestRunner; |
| 86 base::WaitableEvent started_; |
| 87 base::WaitableEvent completed_; |
| 88 }; |
| 89 |
| 90 // This class lives on the io thread. It starts async requests using the |
| 91 // class under test (ResolveProxyMsgHelper), and signals the result future on |
| 92 // completion. |
| 93 class AsyncRequestRunner : public ResolveProxyMsgHelper::Delegate { |
| 94 public: |
| 95 AsyncRequestRunner(net::ProxyService* proxy_service) { |
| 96 resolve_proxy_msg_helper_.reset( |
| 97 new ResolveProxyMsgHelper(this, proxy_service)); |
| 98 } |
| 99 |
| 100 void Start(ResultFuture* future, const GURL& url, IPC::Message* reply_msg) { |
| 101 futures_.push_back(future); |
| 102 resolve_proxy_msg_helper_->Start(url, reply_msg); |
| 103 |
| 104 // Notify of request start. |
| 105 future->started_.Signal(); |
| 106 } |
| 107 |
| 108 virtual void OnResolveProxyCompleted(IPC::Message* reply_msg, |
| 109 int error_code, |
| 110 const std::string& proxy_list) { |
| 111 // Update the result future for this request (top of queue), and signal it. |
| 112 ResultFuture* future = futures_.front(); |
| 113 futures_.pop_front(); |
| 114 |
| 115 future->reply_msg = reply_msg; |
| 116 future->error_code = error_code; |
| 117 future->proxy_list = proxy_list; |
| 118 |
| 119 // Notify of request completion. |
| 120 future->completed_.Signal(); |
| 121 } |
| 122 |
| 123 private: |
| 124 std::deque<ResultFuture*> futures_; |
| 125 scoped_ptr<ResolveProxyMsgHelper> resolve_proxy_msg_helper_; |
| 126 }; |
| 127 |
| 128 // Helper class to start async requests on an io thread, and return a |
| 129 // result future. The caller then uses ResultFuture::WaitUntilDone() to |
| 130 // get at the results. It "bridges" the originating thread with the helper |
| 131 // io thread. |
| 132 class RunnerBridge { |
| 133 public: |
| 134 RunnerBridge() : io_thread_("io_thread"), done_(true, false) { |
| 135 // Start an io thread where we will run the async requests. |
| 136 base::Thread::Options options; |
| 137 options.message_loop_type = MessageLoop::TYPE_IO; |
| 138 io_thread_.StartWithOptions(options); |
| 139 |
| 140 // Construct the state that lives on io thread. |
| 141 io_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod( |
| 142 this, &RunnerBridge::DoConstruct)); |
| 143 done_.Wait(); |
| 144 } |
| 145 |
| 146 // Start an async request on the io thread. |
| 147 ResultFuture* Start(const GURL& url, IPC::Message* reply_msg) { |
| 148 ResultFuture* future = new ResultFuture(); |
| 149 |
| 150 io_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod( |
| 151 async_runner_, &AsyncRequestRunner::Start, future, url, reply_msg)); |
| 152 |
| 153 return future; |
| 154 } |
| 155 |
| 156 void DestroyAsyncRunner() { |
| 157 io_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod( |
| 158 this, &RunnerBridge::DoDestroyAsyncRunner)); |
| 159 done_.Wait(); |
| 160 } |
| 161 |
| 162 ~RunnerBridge() { |
| 163 io_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod( |
| 164 this, &RunnerBridge::DoDestroy)); |
| 165 done_.Wait(); |
| 166 } |
| 167 |
| 168 MockProxyResolver* proxy_resolver() { |
| 169 return proxy_resolver_; |
| 170 } |
| 171 |
| 172 // Called from io thread. |
| 173 void DoConstruct() { |
| 174 proxy_resolver_ = new MockProxyResolver(); |
| 175 proxy_service_ = new net::ProxyService(new MockProxyConfigService(), |
| 176 proxy_resolver_); |
| 177 async_runner_ = new AsyncRequestRunner(proxy_service_); |
| 178 done_.Signal(); |
| 179 } |
| 180 |
| 181 // Called from io thread. |
| 182 void DoDestroy() { |
| 183 delete async_runner_; |
| 184 delete proxy_service_; |
| 185 done_.Signal(); |
| 186 } |
| 187 |
| 188 // Called from io thread. |
| 189 void DoDestroyAsyncRunner() { |
| 190 delete async_runner_; |
| 191 async_runner_ = NULL; |
| 192 done_.Signal(); |
| 193 } |
| 194 |
| 195 private: |
| 196 base::Thread io_thread_; |
| 197 base::WaitableEvent done_; |
| 198 |
| 199 net::ProxyService* proxy_service_; |
| 200 MockProxyResolver* proxy_resolver_; // Owned by proxy_service_. |
| 201 |
| 202 AsyncRequestRunner* async_runner_; |
| 203 }; |
| 204 |
| 205 // Avoid the need to have an AddRef / Release |
| 206 template<> |
| 207 void RunnableMethodTraits<RunnerBridge>::RetainCallee(RunnerBridge*) {} |
| 208 template<> |
| 209 void RunnableMethodTraits<RunnerBridge>::ReleaseCallee(RunnerBridge*) {} |
| 210 |
| 211 template<> |
| 212 void RunnableMethodTraits<AsyncRequestRunner>::RetainCallee(AsyncRequestRunner*)
{} |
| 213 template<> |
| 214 void RunnableMethodTraits<AsyncRequestRunner>::ReleaseCallee(AsyncRequestRunner*
) {} |
| 215 |
| 216 |
| 217 // Issue three sequential requests -- each should succeed. |
| 218 TEST(ResolveProxyMsgHelperTest, Sequential) { |
| 219 RunnerBridge runner; |
| 220 |
| 221 GURL url1("http://www.google1.com/"); |
| 222 GURL url2("http://www.google2.com/"); |
| 223 GURL url3("http://www.google3.com/"); |
| 224 |
| 225 scoped_ptr<IPC::Message> msg1(new IPC::Message()); |
| 226 scoped_ptr<IPC::Message> msg2(new IPC::Message()); |
| 227 scoped_ptr<IPC::Message> msg3(new IPC::Message()); |
| 228 |
| 229 // Execute each request sequentially (so there are never 2 requests |
| 230 // outstanding at the same time). |
| 231 |
| 232 scoped_ptr<ResultFuture> result1(runner.Start(url1, msg1.get())); |
| 233 result1->WaitUntilDone(); |
| 234 |
| 235 scoped_ptr<ResultFuture> result2(runner.Start(url2, msg2.get())); |
| 236 result2->WaitUntilDone(); |
| 237 |
| 238 scoped_ptr<ResultFuture> result3(runner.Start(url3, msg3.get())); |
| 239 result3->WaitUntilDone(); |
| 240 |
| 241 // Check that each request gave the expected result. |
| 242 |
| 243 EXPECT_EQ(msg1.get(), result1->reply_msg); |
| 244 EXPECT_EQ(net::OK, result1->error_code); |
| 245 EXPECT_EQ("PROXY www.google1.com", result1->proxy_list); |
| 246 |
| 247 EXPECT_EQ(msg2.get(), result2->reply_msg); |
| 248 EXPECT_EQ(net::OK, result2->error_code); |
| 249 EXPECT_EQ("PROXY www.google2.com", result2->proxy_list); |
| 250 |
| 251 EXPECT_EQ(msg3.get(), result3->reply_msg); |
| 252 EXPECT_EQ(net::OK, result3->error_code); |
| 253 EXPECT_EQ("PROXY www.google3.com", result3->proxy_list); |
| 254 } |
| 255 |
| 256 // Issue a request while one is already in progress -- should be queued. |
| 257 TEST(ResolveProxyMsgHelperTest, QueueRequests) { |
| 258 RunnerBridge runner; |
| 259 |
| 260 GURL url1("http://www.google1.com/"); |
| 261 GURL url2("http://www.google2.com/"); |
| 262 GURL url3("http://www.google3.com/"); |
| 263 |
| 264 scoped_ptr<IPC::Message> msg1(new IPC::Message()); |
| 265 scoped_ptr<IPC::Message> msg2(new IPC::Message()); |
| 266 scoped_ptr<IPC::Message> msg3(new IPC::Message()); |
| 267 |
| 268 // Make the proxy resolver hang on the next request. |
| 269 runner.proxy_resolver()->Block(); |
| 270 |
| 271 // Start three requests. Since the proxy resolver is hung, the second two |
| 272 // will be pending. |
| 273 |
| 274 scoped_ptr<ResultFuture> result1(runner.Start(url1, msg1.get())); |
| 275 scoped_ptr<ResultFuture> result2(runner.Start(url2, msg2.get())); |
| 276 scoped_ptr<ResultFuture> result3(runner.Start(url3, msg3.get())); |
| 277 |
| 278 // Wait for the final request to have been scheduled. Otherwise we may rush |
| 279 // to calling Unblock() without actually having blocked anything. |
| 280 result3->WaitUntilStarted(); |
| 281 |
| 282 // Unblock the proxy service so requests 1-3 can complete. |
| 283 runner.proxy_resolver()->Unblock(); |
| 284 |
| 285 // Wait for all the requests to finish (they run in FIFO order). |
| 286 result3->WaitUntilDone(); |
| 287 |
| 288 // Check that each call invoked the callback with the right parameters. |
| 289 |
| 290 EXPECT_EQ(msg1.get(), result1->reply_msg); |
| 291 EXPECT_EQ(net::OK, result1->error_code); |
| 292 EXPECT_EQ("PROXY www.google1.com", result1->proxy_list); |
| 293 |
| 294 EXPECT_EQ(msg2.get(), result2->reply_msg); |
| 295 EXPECT_EQ(net::OK, result2->error_code); |
| 296 EXPECT_EQ("PROXY www.google2.com", result2->proxy_list); |
| 297 |
| 298 EXPECT_EQ(msg3.get(), result3->reply_msg); |
| 299 EXPECT_EQ(net::OK, result3->error_code); |
| 300 EXPECT_EQ("PROXY www.google3.com", result3->proxy_list); |
| 301 } |
| 302 |
| 303 // Delete the helper while a request is in progress, and others are pending. |
| 304 TEST(ResolveProxyMsgHelperTest, CancelPendingRequests) { |
| 305 RunnerBridge runner; |
| 306 |
| 307 GURL url1("http://www.google1.com/"); |
| 308 GURL url2("http://www.google2.com/"); |
| 309 GURL url3("http://www.google3.com/"); |
| 310 |
| 311 // NOTE: these are not scoped ptr, since they will be deleted by the |
| 312 // request's cancellation. |
| 313 IPC::Message* msg1 = new IPC::Message(); |
| 314 IPC::Message* msg2 = new IPC::Message(); |
| 315 IPC::Message* msg3 = new IPC::Message(); |
| 316 |
| 317 // Make the next request block. |
| 318 runner.proxy_resolver()->Block(); |
| 319 |
| 320 // Start three requests; since the first one blocked, the other two should |
| 321 // be pending. |
| 322 |
| 323 scoped_ptr<ResultFuture> result1(runner.Start(url1, msg1)); |
| 324 scoped_ptr<ResultFuture> result2(runner.Start(url2, msg2)); |
| 325 scoped_ptr<ResultFuture> result3(runner.Start(url3, msg3)); |
| 326 |
| 327 result3->WaitUntilStarted(); |
| 328 |
| 329 // Delete the underlying ResolveProxyMsgHelper -- this should cancel all |
| 330 // the requests which are outstanding. |
| 331 runner.DestroyAsyncRunner(); |
| 332 |
| 333 // Unblocking the proxy resolver means the three requests can complete -- |
| 334 // however they should not try to notify the delegate since we have already |
| 335 // deleted the helper. |
| 336 runner.proxy_resolver()->Unblock(); |
| 337 |
| 338 // Check that none of the requests were sent to the delegate. |
| 339 EXPECT_FALSE( |
| 340 result1->TimedWaitUntilDone(base::TimeDelta::FromMilliseconds(2))); |
| 341 EXPECT_FALSE( |
| 342 result2->TimedWaitUntilDone(base::TimeDelta::FromMilliseconds(2))); |
| 343 EXPECT_FALSE( |
| 344 result3->TimedWaitUntilDone(base::TimeDelta::FromMilliseconds(2))); |
| 345 |
| 346 // It should also be the case that msg1, msg2, msg3 were deleted by the |
| 347 // cancellation. (Else will show up as a leak in Purify). |
| 348 } |
| 349 |
OLD | NEW |