url_fetcher_unittest.cc revision 4a5e2dc747d50c653511c68ccb2cfbfb740bd5a7
1// Copyright (c) 2010 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/message_loop_proxy.h"
6#include "base/thread.h"
7#include "base/waitable_event.h"
8#include "build/build_config.h"
9#include "chrome/common/chrome_plugin_lib.h"
10#include "chrome/common/net/url_fetcher.h"
11#include "chrome/common/net/url_fetcher_protect.h"
12#include "chrome/common/net/url_request_context_getter.h"
13#include "net/http/http_response_headers.h"
14#include "net/url_request/url_request_unittest.h"
15#include "net/test/test_server.h"
16#include "testing/gtest/include/gtest/gtest.h"
17
18#if defined(USE_NSS)
19#include "net/ocsp/nss_ocsp.h"
20#endif
21
22using base::Time;
23using base::TimeDelta;
24
25// TODO(eroman): Add a regression test for http://crbug.com/40505.
26
27namespace {
28
29const FilePath::CharType kDocRoot[] = FILE_PATH_LITERAL("chrome/test/data");
30
31class TestURLRequestContextGetter : public URLRequestContextGetter {
32 public:
33  explicit TestURLRequestContextGetter(
34      base::MessageLoopProxy* io_message_loop_proxy)
35          : io_message_loop_proxy_(io_message_loop_proxy) {
36  }
37  virtual URLRequestContext* GetURLRequestContext() {
38    if (!context_)
39      context_ = new TestURLRequestContext();
40    return context_;
41  }
42  virtual scoped_refptr<base::MessageLoopProxy> GetIOMessageLoopProxy() const {
43    return io_message_loop_proxy_;
44  }
45
46 protected:
47  scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_;
48
49 private:
50  ~TestURLRequestContextGetter() {}
51
52  scoped_refptr<URLRequestContext> context_;
53};
54
55class URLFetcherTest : public testing::Test, public URLFetcher::Delegate {
56 public:
57  URLFetcherTest() : fetcher_(NULL) { }
58
59  // Creates a URLFetcher, using the program's main thread to do IO.
60  virtual void CreateFetcher(const GURL& url);
61
62  // URLFetcher::Delegate
63  virtual void OnURLFetchComplete(const URLFetcher* source,
64                                  const GURL& url,
65                                  const URLRequestStatus& status,
66                                  int response_code,
67                                  const ResponseCookies& cookies,
68                                  const std::string& data);
69
70  scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy() {
71    return io_message_loop_proxy_;
72  }
73
74 protected:
75  virtual void SetUp() {
76    testing::Test::SetUp();
77
78    io_message_loop_proxy_ = base::MessageLoopProxy::CreateForCurrentThread();
79
80    // Ensure that any plugin operations done by other tests are cleaned up.
81    ChromePluginLib::UnloadAllPlugins();
82#if defined(USE_NSS)
83    net::EnsureOCSPInit();
84#endif
85  }
86
87  virtual void TearDown() {
88#if defined(USE_NSS)
89    net::ShutdownOCSP();
90#endif
91  }
92
93  // URLFetcher is designed to run on the main UI thread, but in our tests
94  // we assume that the current thread is the IO thread where the URLFetcher
95  // dispatches its requests to.  When we wish to simulate being used from
96  // a UI thread, we dispatch a worker thread to do so.
97  MessageLoopForIO io_loop_;
98  scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_;
99
100  URLFetcher* fetcher_;
101};
102
103// Version of URLFetcherTest that does a POST instead
104class URLFetcherPostTest : public URLFetcherTest {
105 public:
106  virtual void CreateFetcher(const GURL& url);
107
108  // URLFetcher::Delegate
109  virtual void OnURLFetchComplete(const URLFetcher* source,
110                                  const GURL& url,
111                                  const URLRequestStatus& status,
112                                  int response_code,
113                                  const ResponseCookies& cookies,
114                                  const std::string& data);
115};
116
117// Version of URLFetcherTest that tests headers.
118class URLFetcherHeadersTest : public URLFetcherTest {
119 public:
120  // URLFetcher::Delegate
121  virtual void OnURLFetchComplete(const URLFetcher* source,
122                                  const GURL& url,
123                                  const URLRequestStatus& status,
124                                  int response_code,
125                                  const ResponseCookies& cookies,
126                                  const std::string& data);
127};
128
129// Version of URLFetcherTest that tests overload protection.
130class URLFetcherProtectTest : public URLFetcherTest {
131 public:
132  virtual void CreateFetcher(const GURL& url);
133  // URLFetcher::Delegate
134  virtual void OnURLFetchComplete(const URLFetcher* source,
135                                  const GURL& url,
136                                  const URLRequestStatus& status,
137                                  int response_code,
138                                  const ResponseCookies& cookies,
139                                  const std::string& data);
140 private:
141  Time start_time_;
142};
143
144// Version of URLFetcherTest that tests overload protection, when responses
145// passed through.
146class URLFetcherProtectTestPassedThrough : public URLFetcherTest {
147 public:
148  virtual void CreateFetcher(const GURL& url);
149  // URLFetcher::Delegate
150  virtual void OnURLFetchComplete(const URLFetcher* source,
151                                  const GURL& url,
152                                  const URLRequestStatus& status,
153                                  int response_code,
154                                  const ResponseCookies& cookies,
155                                  const std::string& data);
156 private:
157  Time start_time_;
158};
159
160// Version of URLFetcherTest that tests bad HTTPS requests.
161class URLFetcherBadHTTPSTest : public URLFetcherTest {
162 public:
163  URLFetcherBadHTTPSTest();
164
165  // URLFetcher::Delegate
166  virtual void OnURLFetchComplete(const URLFetcher* source,
167                                  const GURL& url,
168                                  const URLRequestStatus& status,
169                                  int response_code,
170                                  const ResponseCookies& cookies,
171                                  const std::string& data);
172
173 private:
174  FilePath cert_dir_;
175};
176
177// Version of URLFetcherTest that tests request cancellation on shutdown.
178class URLFetcherCancelTest : public URLFetcherTest {
179 public:
180  virtual void CreateFetcher(const GURL& url);
181  // URLFetcher::Delegate
182  virtual void OnURLFetchComplete(const URLFetcher* source,
183                                  const GURL& url,
184                                  const URLRequestStatus& status,
185                                  int response_code,
186                                  const ResponseCookies& cookies,
187                                  const std::string& data);
188
189  void CancelRequest();
190};
191
192// Version of TestURLRequestContext that posts a Quit task to the IO
193// thread once it is deleted.
194class CancelTestURLRequestContext : public TestURLRequestContext {
195  virtual ~CancelTestURLRequestContext() {
196    // The d'tor should execute in the IO thread. Post the quit task to the
197    // current thread.
198    MessageLoop::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask());
199  }
200};
201
202class CancelTestURLRequestContextGetter : public URLRequestContextGetter {
203 public:
204  explicit CancelTestURLRequestContextGetter(
205      base::MessageLoopProxy* io_message_loop_proxy)
206      : io_message_loop_proxy_(io_message_loop_proxy),
207        context_created_(false, false) {
208  }
209  virtual URLRequestContext* GetURLRequestContext() {
210    if (!context_) {
211      context_ = new CancelTestURLRequestContext();
212      context_created_.Signal();
213    }
214    return context_;
215  }
216  virtual scoped_refptr<base::MessageLoopProxy> GetIOMessageLoopProxy() const {
217    return io_message_loop_proxy_;
218  }
219  void WaitForContextCreation() {
220    context_created_.Wait();
221  }
222
223 private:
224  ~CancelTestURLRequestContextGetter() {}
225
226  scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_;
227  base::WaitableEvent context_created_;
228  scoped_refptr<URLRequestContext> context_;
229};
230
231// Wrapper that lets us call CreateFetcher() on a thread of our choice.  We
232// could make URLFetcherTest refcounted and use PostTask(FROM_HERE.. ) to call
233// CreateFetcher() directly, but the ownership of the URLFetcherTest is a bit
234// confusing in that case because GTest doesn't know about the refcounting.
235// It's less confusing to just do it this way.
236class FetcherWrapperTask : public Task {
237 public:
238  FetcherWrapperTask(URLFetcherTest* test, const GURL& url)
239      : test_(test), url_(url) { }
240  virtual void Run() {
241    test_->CreateFetcher(url_);
242  }
243
244 private:
245  URLFetcherTest* test_;
246  GURL url_;
247};
248
249void URLFetcherTest::CreateFetcher(const GURL& url) {
250  fetcher_ = new URLFetcher(url, URLFetcher::GET, this);
251  fetcher_->set_request_context(new TestURLRequestContextGetter(
252      io_message_loop_proxy()));
253  fetcher_->Start();
254}
255
256void URLFetcherTest::OnURLFetchComplete(const URLFetcher* source,
257                                        const GURL& url,
258                                        const URLRequestStatus& status,
259                                        int response_code,
260                                        const ResponseCookies& cookies,
261                                        const std::string& data) {
262  EXPECT_TRUE(status.is_success());
263  EXPECT_EQ(200, response_code);  // HTTP OK
264  EXPECT_FALSE(data.empty());
265
266  delete fetcher_;  // Have to delete this here and not in the destructor,
267                    // because the destructor won't necessarily run on the
268                    // same thread that CreateFetcher() did.
269
270  io_message_loop_proxy()->PostTask(FROM_HERE, new MessageLoop::QuitTask());
271  // If the current message loop is not the IO loop, it will be shut down when
272  // the main loop returns and this thread subsequently goes out of scope.
273}
274
275void URLFetcherPostTest::CreateFetcher(const GURL& url) {
276  fetcher_ = new URLFetcher(url, URLFetcher::POST, this);
277  fetcher_->set_request_context(new TestURLRequestContextGetter(
278      io_message_loop_proxy()));
279  fetcher_->set_upload_data("application/x-www-form-urlencoded",
280                            "bobsyeruncle");
281  fetcher_->Start();
282}
283
284void URLFetcherPostTest::OnURLFetchComplete(const URLFetcher* source,
285                                            const GURL& url,
286                                            const URLRequestStatus& status,
287                                            int response_code,
288                                            const ResponseCookies& cookies,
289                                            const std::string& data) {
290  EXPECT_EQ(std::string("bobsyeruncle"), data);
291  URLFetcherTest::OnURLFetchComplete(source, url, status, response_code,
292                                     cookies, data);
293}
294
295void URLFetcherHeadersTest::OnURLFetchComplete(
296    const URLFetcher* source,
297    const GURL& url,
298    const URLRequestStatus& status,
299    int response_code,
300    const ResponseCookies& cookies,
301    const std::string& data) {
302  std::string header;
303  EXPECT_TRUE(source->response_headers()->GetNormalizedHeader("cache-control",
304                                                              &header));
305  EXPECT_EQ("private", header);
306  URLFetcherTest::OnURLFetchComplete(source, url, status, response_code,
307                                     cookies, data);
308}
309
310void URLFetcherProtectTest::CreateFetcher(const GURL& url) {
311  fetcher_ = new URLFetcher(url, URLFetcher::GET, this);
312  fetcher_->set_request_context(new TestURLRequestContextGetter(
313      io_message_loop_proxy()));
314  start_time_ = Time::Now();
315  fetcher_->Start();
316}
317
318void URLFetcherProtectTest::OnURLFetchComplete(const URLFetcher* source,
319                                               const GURL& url,
320                                               const URLRequestStatus& status,
321                                               int response_code,
322                                               const ResponseCookies& cookies,
323                                               const std::string& data) {
324  const TimeDelta one_second = TimeDelta::FromMilliseconds(1000);
325  if (response_code >= 500) {
326    // Now running ServerUnavailable test.
327    // It takes more than 1 second to finish all 11 requests.
328    EXPECT_TRUE(Time::Now() - start_time_ >= one_second);
329    EXPECT_TRUE(status.is_success());
330    EXPECT_FALSE(data.empty());
331    delete fetcher_;
332    io_message_loop_proxy()->PostTask(FROM_HERE, new MessageLoop::QuitTask());
333  } else {
334    // Now running Overload test.
335    static int count = 0;
336    count++;
337    if (count < 20) {
338      fetcher_->Start();
339    } else {
340      // We have already sent 20 requests continuously. And we expect that
341      // it takes more than 1 second due to the overload pretection settings.
342      EXPECT_TRUE(Time::Now() - start_time_ >= one_second);
343      URLFetcherTest::OnURLFetchComplete(source, url, status, response_code,
344                                         cookies, data);
345    }
346  }
347}
348
349void URLFetcherProtectTestPassedThrough::CreateFetcher(const GURL& url) {
350  fetcher_ = new URLFetcher(url, URLFetcher::GET, this);
351  fetcher_->set_request_context(new TestURLRequestContextGetter(
352      io_message_loop_proxy()));
353  fetcher_->set_automatically_retry_on_5xx(false);
354  start_time_ = Time::Now();
355  fetcher_->Start();
356}
357
358void URLFetcherProtectTestPassedThrough::OnURLFetchComplete(
359    const URLFetcher* source,
360    const GURL& url,
361    const URLRequestStatus& status,
362    int response_code,
363    const ResponseCookies& cookies,
364    const std::string& data) {
365  const TimeDelta one_minute = TimeDelta::FromMilliseconds(60000);
366  if (response_code >= 500) {
367    // Now running ServerUnavailable test.
368    // It should get here on the first attempt, so almost immediately and
369    // *not* to attempt to execute all 11 requests (2.5 minutes).
370    EXPECT_TRUE(Time::Now() - start_time_ < one_minute);
371    EXPECT_TRUE(status.is_success());
372    // Check that suggested back off time is bigger than 0.
373    EXPECT_GT(fetcher_->backoff_delay().InMicroseconds(), 0);
374    EXPECT_FALSE(data.empty());
375  } else {
376    // We should not get here!
377    ADD_FAILURE();
378  }
379
380  delete fetcher_;
381  io_message_loop_proxy()->PostTask(FROM_HERE, new MessageLoop::QuitTask());
382}
383
384
385URLFetcherBadHTTPSTest::URLFetcherBadHTTPSTest() {
386  PathService::Get(base::DIR_SOURCE_ROOT, &cert_dir_);
387  cert_dir_ = cert_dir_.AppendASCII("chrome");
388  cert_dir_ = cert_dir_.AppendASCII("test");
389  cert_dir_ = cert_dir_.AppendASCII("data");
390  cert_dir_ = cert_dir_.AppendASCII("ssl");
391  cert_dir_ = cert_dir_.AppendASCII("certificates");
392}
393
394// The "server certificate expired" error should result in automatic
395// cancellation of the request by
396// URLRequest::Delegate::OnSSLCertificateError.
397void URLFetcherBadHTTPSTest::OnURLFetchComplete(
398    const URLFetcher* source,
399    const GURL& url,
400    const URLRequestStatus& status,
401    int response_code,
402    const ResponseCookies& cookies,
403    const std::string& data) {
404  // This part is different from URLFetcherTest::OnURLFetchComplete
405  // because this test expects the request to be cancelled.
406  EXPECT_EQ(URLRequestStatus::CANCELED, status.status());
407  EXPECT_EQ(net::ERR_ABORTED, status.os_error());
408  EXPECT_EQ(-1, response_code);
409  EXPECT_TRUE(cookies.empty());
410  EXPECT_TRUE(data.empty());
411
412  // The rest is the same as URLFetcherTest::OnURLFetchComplete.
413  delete fetcher_;
414  io_message_loop_proxy()->PostTask(FROM_HERE, new MessageLoop::QuitTask());
415}
416
417void URLFetcherCancelTest::CreateFetcher(const GURL& url) {
418  fetcher_ = new URLFetcher(url, URLFetcher::GET, this);
419  CancelTestURLRequestContextGetter* context_getter =
420      new CancelTestURLRequestContextGetter(io_message_loop_proxy());
421  fetcher_->set_request_context(context_getter);
422  fetcher_->Start();
423  // We need to wait for the creation of the URLRequestContext, since we
424  // rely on it being destroyed as a signal to end the test.
425  context_getter->WaitForContextCreation();
426  CancelRequest();
427}
428
429void URLFetcherCancelTest::OnURLFetchComplete(const URLFetcher* source,
430                                              const GURL& url,
431                                              const URLRequestStatus& status,
432                                              int response_code,
433                                              const ResponseCookies& cookies,
434                                              const std::string& data) {
435  // We should have cancelled the request before completion.
436  ADD_FAILURE();
437  delete fetcher_;
438  io_message_loop_proxy()->PostTask(FROM_HERE, new MessageLoop::QuitTask());
439}
440
441void URLFetcherCancelTest::CancelRequest() {
442  delete fetcher_;
443  // The URLFetcher's test context will post a Quit task once it is
444  // deleted. So if this test simply hangs, it means cancellation
445  // did not work.
446}
447
448TEST_F(URLFetcherTest, SameThreadsTest) {
449  net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot));
450  ASSERT_TRUE(test_server.Start());
451
452  // Create the fetcher on the main thread.  Since IO will happen on the main
453  // thread, this will test URLFetcher's ability to do everything on one
454  // thread.
455  CreateFetcher(test_server.GetURL("defaultresponse"));
456
457  MessageLoop::current()->Run();
458}
459
460TEST_F(URLFetcherTest, DifferentThreadsTest) {
461  net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot));
462  ASSERT_TRUE(test_server.Start());
463
464  // Create a separate thread that will create the URLFetcher.  The current
465  // (main) thread will do the IO, and when the fetch is complete it will
466  // terminate the main thread's message loop; then the other thread's
467  // message loop will be shut down automatically as the thread goes out of
468  // scope.
469  base::Thread t("URLFetcher test thread");
470  ASSERT_TRUE(t.Start());
471  t.message_loop()->PostTask(FROM_HERE, new FetcherWrapperTask(this,
472      test_server.GetURL("defaultresponse")));
473
474  MessageLoop::current()->Run();
475}
476
477TEST_F(URLFetcherPostTest, Basic) {
478  net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot));
479  ASSERT_TRUE(test_server.Start());
480
481  CreateFetcher(test_server.GetURL("echo"));
482  MessageLoop::current()->Run();
483}
484
485TEST_F(URLFetcherHeadersTest, Headers) {
486  net::TestServer test_server(net::TestServer::TYPE_HTTP,
487      FilePath(FILE_PATH_LITERAL("net/data/url_request_unittest")));
488  ASSERT_TRUE(test_server.Start());
489
490  CreateFetcher(test_server.GetURL("files/with-headers.html"));
491  MessageLoop::current()->Run();
492  // The actual tests are in the URLFetcherHeadersTest fixture.
493}
494
495TEST_F(URLFetcherProtectTest, Overload) {
496  net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot));
497  ASSERT_TRUE(test_server.Start());
498
499  GURL url(test_server.GetURL("defaultresponse"));
500
501  // Registers an entry for test url. It only allows 3 requests to be sent
502  // in 200 milliseconds.
503  URLFetcherProtectManager* manager = URLFetcherProtectManager::GetInstance();
504  URLFetcherProtectEntry* entry =
505      new URLFetcherProtectEntry(200, 3, 11, 1, 2.0, 0, 256);
506  manager->Register(url.host(), entry);
507
508  CreateFetcher(url);
509
510  MessageLoop::current()->Run();
511}
512
513TEST_F(URLFetcherProtectTest, ServerUnavailable) {
514  net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot));
515  ASSERT_TRUE(test_server.Start());
516
517  GURL url(test_server.GetURL("files/server-unavailable.html"));
518
519  // Registers an entry for test url. The backoff time is calculated by:
520  //     new_backoff = 2.0 * old_backoff + 0
521  // and maximum backoff time is 256 milliseconds.
522  // Maximum retries allowed is set to 11.
523  URLFetcherProtectManager* manager = URLFetcherProtectManager::GetInstance();
524  URLFetcherProtectEntry* entry =
525      new URLFetcherProtectEntry(200, 3, 11, 1, 2.0, 0, 256);
526  manager->Register(url.host(), entry);
527
528  CreateFetcher(url);
529
530  MessageLoop::current()->Run();
531}
532
533TEST_F(URLFetcherProtectTestPassedThrough, ServerUnavailablePropagateResponse) {
534  net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot));
535  ASSERT_TRUE(test_server.Start());
536
537  GURL url(test_server.GetURL("files/server-unavailable.html"));
538
539  // Registers an entry for test url. The backoff time is calculated by:
540  //     new_backoff = 2.0 * old_backoff + 0
541  // and maximum backoff time is 256 milliseconds.
542  // Maximum retries allowed is set to 11.
543  URLFetcherProtectManager* manager = URLFetcherProtectManager::GetInstance();
544  // Total time if *not* for not doing automatic backoff would be 150s.
545  // In reality it should be "as soon as server responds".
546  URLFetcherProtectEntry* entry =
547      new URLFetcherProtectEntry(200, 3, 11, 100, 2.0, 0, 150000);
548  manager->Register(url.host(), entry);
549
550  CreateFetcher(url);
551
552  MessageLoop::current()->Run();
553}
554
555
556TEST_F(URLFetcherBadHTTPSTest, BadHTTPSTest) {
557  net::TestServer::HTTPSOptions https_options(
558      net::TestServer::HTTPSOptions::CERT_EXPIRED);
559  net::TestServer test_server(https_options, FilePath(kDocRoot));
560  ASSERT_TRUE(test_server.Start());
561
562  CreateFetcher(test_server.GetURL("defaultresponse"));
563  MessageLoop::current()->Run();
564}
565
566TEST_F(URLFetcherCancelTest, ReleasesContext) {
567  net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot));
568  ASSERT_TRUE(test_server.Start());
569
570  GURL url(test_server.GetURL("files/server-unavailable.html"));
571
572  // Registers an entry for test url. The backoff time is calculated by:
573  //     new_backoff = 2.0 * old_backoff + 0
574  // The initial backoff is 2 seconds and maximum backoff is 4 seconds.
575  // Maximum retries allowed is set to 2.
576  URLFetcherProtectManager* manager = URLFetcherProtectManager::GetInstance();
577  URLFetcherProtectEntry* entry =
578      new URLFetcherProtectEntry(200, 3, 2, 2000, 2.0, 0, 4000);
579  manager->Register(url.host(), entry);
580
581  // Create a separate thread that will create the URLFetcher.  The current
582  // (main) thread will do the IO, and when the fetch is complete it will
583  // terminate the main thread's message loop; then the other thread's
584  // message loop will be shut down automatically as the thread goes out of
585  // scope.
586  base::Thread t("URLFetcher test thread");
587  ASSERT_TRUE(t.Start());
588  t.message_loop()->PostTask(FROM_HERE, new FetcherWrapperTask(this, url));
589
590  MessageLoop::current()->Run();
591}
592
593TEST_F(URLFetcherCancelTest, CancelWhileDelayedStartTaskPending) {
594  net::TestServer test_server(net::TestServer::TYPE_HTTP, FilePath(kDocRoot));
595  ASSERT_TRUE(test_server.Start());
596
597  GURL url(test_server.GetURL("files/server-unavailable.html"));
598
599  // Register an entry for test url.
600  //
601  // Ideally we would mock URLFetcherProtectEntry to return XXX seconds
602  // in response to entry->UpdateBackoff(SEND).
603  //
604  // Unfortunately this function is time sensitive, so we fudge some numbers
605  // to make it at least somewhat likely to have a non-zero deferred
606  // delay when running.
607  //
608  // Using a sliding window of 2 seconds, and max of 1 request, under a fast
609  // run we expect to have a 4 second delay when posting the Start task.
610  URLFetcherProtectManager* manager = URLFetcherProtectManager::GetInstance();
611  URLFetcherProtectEntry* entry =
612      new URLFetcherProtectEntry(2000, 1, 2, 2000, 2.0, 0, 4000);
613  EXPECT_EQ(0, entry->UpdateBackoff(URLFetcherProtectEntry::SEND));
614  entry->UpdateBackoff(URLFetcherProtectEntry::SEND);  // Returns about 2000.
615  manager->Register(url.host(), entry);
616
617  // The next request we try to send will be delayed by ~4 seconds.
618  // The slower the test runs, the less the delay will be (since it takes the
619  // time difference from now).
620
621  base::Thread t("URLFetcher test thread");
622  ASSERT_TRUE(t.Start());
623  t.message_loop()->PostTask(FROM_HERE, new FetcherWrapperTask(this, url));
624
625  MessageLoop::current()->Run();
626}
627
628}  // namespace.
629