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