Home | History | Annotate | Download | only in renderer
      1 // Copyright (c) 2012 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 "content/public/renderer/resource_fetcher.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/bind_helpers.h"
      9 #include "base/command_line.h"
     10 #include "base/message_loop/message_loop.h"
     11 #include "base/time/time.h"
     12 #include "base/timer/timer.h"
     13 #include "content/public/common/content_switches.h"
     14 #include "content/public/common/url_constants.h"
     15 #include "content/public/renderer/render_view.h"
     16 #include "content/public/test/content_browser_test.h"
     17 #include "content/public/test/content_browser_test_utils.h"
     18 #include "content/public/test/test_utils.h"
     19 #include "content/shell/browser/shell.h"
     20 #include "third_party/WebKit/public/platform/WebURLResponse.h"
     21 #include "third_party/WebKit/public/web/WebFrame.h"
     22 #include "third_party/WebKit/public/web/WebView.h"
     23 
     24 using blink::WebFrame;
     25 using blink::WebURLRequest;
     26 using blink::WebURLResponse;
     27 
     28 namespace {
     29 
     30 // The first RenderFrame is routing ID 1, and the first RenderView is 2.
     31 const int kRenderViewRoutingId = 2;
     32 
     33 }
     34 
     35 namespace content {
     36 
     37 static const int kMaxWaitTimeMs = 5000;
     38 
     39 class FetcherDelegate {
     40  public:
     41   FetcherDelegate()
     42       : completed_(false),
     43         timed_out_(false) {
     44     // Start a repeating timer waiting for the download to complete.  The
     45     // callback has to be a static function, so we hold on to our instance.
     46     FetcherDelegate::instance_ = this;
     47     StartTimer();
     48   }
     49 
     50   virtual ~FetcherDelegate() {}
     51 
     52   ResourceFetcher::Callback NewCallback() {
     53     return base::Bind(&FetcherDelegate::OnURLFetchComplete,
     54                       base::Unretained(this));
     55   }
     56 
     57   virtual void OnURLFetchComplete(const WebURLResponse& response,
     58                                   const std::string& data) {
     59     response_ = response;
     60     data_ = data;
     61     completed_ = true;
     62     timer_.Stop();
     63     if (!timed_out_)
     64       quit_task_.Run();
     65   }
     66 
     67   bool completed() const { return completed_; }
     68   bool timed_out() const { return timed_out_; }
     69 
     70   std::string data() const { return data_; }
     71   const WebURLResponse& response() const { return response_; }
     72 
     73   // Wait for the request to complete or timeout.
     74   void WaitForResponse() {
     75     scoped_refptr<MessageLoopRunner> runner = new MessageLoopRunner;
     76     quit_task_ = runner->QuitClosure();
     77     runner->Run();
     78   }
     79 
     80   void StartTimer() {
     81     timer_.Start(FROM_HERE,
     82                  base::TimeDelta::FromMilliseconds(kMaxWaitTimeMs),
     83                  this,
     84                  &FetcherDelegate::TimerFired);
     85   }
     86 
     87   void TimerFired() {
     88     ASSERT_FALSE(completed_);
     89 
     90     timed_out_ = true;
     91     if (!completed_)
     92       quit_task_.Run();
     93     FAIL() << "fetch timed out";
     94   }
     95 
     96   static FetcherDelegate* instance_;
     97 
     98  private:
     99   base::OneShotTimer<FetcherDelegate> timer_;
    100   bool completed_;
    101   bool timed_out_;
    102   WebURLResponse response_;
    103   std::string data_;
    104   base::Closure quit_task_;
    105 };
    106 
    107 FetcherDelegate* FetcherDelegate::instance_ = NULL;
    108 
    109 class EvilFetcherDelegate : public FetcherDelegate {
    110  public:
    111   virtual ~EvilFetcherDelegate() {}
    112 
    113   void SetFetcher(ResourceFetcher* fetcher) {
    114     fetcher_.reset(fetcher);
    115   }
    116 
    117   virtual void OnURLFetchComplete(const WebURLResponse& response,
    118                                   const std::string& data) OVERRIDE {
    119     FetcherDelegate::OnURLFetchComplete(response, data);
    120 
    121     // Destroy the ResourceFetcher here.  We are testing that upon returning
    122     // to the ResourceFetcher that it does not crash.  This must be done after
    123     // calling FetcherDelegate::OnURLFetchComplete, since deleting the fetcher
    124     // invalidates |response| and |data|.
    125     fetcher_.reset();
    126   }
    127 
    128  private:
    129   scoped_ptr<ResourceFetcher> fetcher_;
    130 };
    131 
    132 class ResourceFetcherTests : public ContentBrowserTest {
    133  public:
    134   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
    135     command_line->AppendSwitch(switches::kSingleProcess);
    136 #if defined(OS_WIN)
    137     // Don't want to try to create a GPU process.
    138     command_line->AppendSwitch(switches::kDisableGpu);
    139 #endif
    140   }
    141 
    142   RenderView* GetRenderView() {
    143     // We could have the test on the UI thread get the WebContent's routing ID,
    144     // but we know this will be the first RV so skip that and just hardcode it.
    145     return RenderView::FromRoutingID(kRenderViewRoutingId);
    146   }
    147 
    148   void ResourceFetcherDownloadOnRenderer(const GURL& url) {
    149     WebFrame* frame = GetRenderView()->GetWebView()->mainFrame();
    150 
    151     scoped_ptr<FetcherDelegate> delegate(new FetcherDelegate);
    152     scoped_ptr<ResourceFetcher> fetcher(ResourceFetcher::Create(url));
    153     fetcher->Start(frame, WebURLRequest::TargetIsMainFrame,
    154                    delegate->NewCallback());
    155 
    156     delegate->WaitForResponse();
    157 
    158     ASSERT_TRUE(delegate->completed());
    159     EXPECT_EQ(delegate->response().httpStatusCode(), 200);
    160     std::string text = delegate->data();
    161     EXPECT_TRUE(text.find("Basic html test.") != std::string::npos);
    162   }
    163 
    164   void ResourceFetcher404OnRenderer(const GURL& url) {
    165     WebFrame* frame = GetRenderView()->GetWebView()->mainFrame();
    166 
    167     scoped_ptr<FetcherDelegate> delegate(new FetcherDelegate);
    168     scoped_ptr<ResourceFetcher> fetcher(ResourceFetcher::Create(url));
    169     fetcher->Start(frame, WebURLRequest::TargetIsMainFrame,
    170                    delegate->NewCallback());
    171 
    172     delegate->WaitForResponse();
    173 
    174     ASSERT_TRUE(delegate->completed());
    175     EXPECT_EQ(delegate->response().httpStatusCode(), 404);
    176     EXPECT_TRUE(delegate->data().find("Not Found.") != std::string::npos);
    177   }
    178 
    179   void ResourceFetcherDidFailOnRenderer() {
    180     WebFrame* frame = GetRenderView()->GetWebView()->mainFrame();
    181 
    182     // Try to fetch a page on a site that doesn't exist.
    183     GURL url("http://localhost:1339/doesnotexist");
    184     scoped_ptr<FetcherDelegate> delegate(new FetcherDelegate);
    185     scoped_ptr<ResourceFetcher> fetcher(ResourceFetcher::Create(url));
    186     fetcher->Start(frame, WebURLRequest::TargetIsMainFrame,
    187                    delegate->NewCallback());
    188 
    189     delegate->WaitForResponse();
    190 
    191     // When we fail, we still call the Delegate callback but we pass in empty
    192     // values.
    193     EXPECT_TRUE(delegate->completed());
    194     EXPECT_TRUE(delegate->response().isNull());
    195     EXPECT_EQ(delegate->data(), std::string());
    196     EXPECT_FALSE(delegate->timed_out());
    197   }
    198 
    199   void ResourceFetcherTimeoutOnRenderer(const GURL& url) {
    200     WebFrame* frame = GetRenderView()->GetWebView()->mainFrame();
    201 
    202     scoped_ptr<FetcherDelegate> delegate(new FetcherDelegate);
    203     scoped_ptr<ResourceFetcher> fetcher(ResourceFetcher::Create(url));
    204     fetcher->Start(frame, WebURLRequest::TargetIsMainFrame,
    205                    delegate->NewCallback());
    206     fetcher->SetTimeout(base::TimeDelta());
    207 
    208     delegate->WaitForResponse();
    209 
    210     // When we timeout, we still call the Delegate callback but we pass in empty
    211     // values.
    212     EXPECT_TRUE(delegate->completed());
    213     EXPECT_TRUE(delegate->response().isNull());
    214     EXPECT_EQ(delegate->data(), std::string());
    215     EXPECT_FALSE(delegate->timed_out());
    216   }
    217 
    218   void ResourceFetcherDeletedInCallbackOnRenderer(const GURL& url) {
    219     WebFrame* frame = GetRenderView()->GetWebView()->mainFrame();
    220 
    221     scoped_ptr<EvilFetcherDelegate> delegate(new EvilFetcherDelegate);
    222     scoped_ptr<ResourceFetcher> fetcher(ResourceFetcher::Create(url));
    223     fetcher->Start(frame, WebURLRequest::TargetIsMainFrame,
    224                    delegate->NewCallback());
    225     fetcher->SetTimeout(base::TimeDelta());
    226     delegate->SetFetcher(fetcher.release());
    227 
    228     delegate->WaitForResponse();
    229     EXPECT_FALSE(delegate->timed_out());
    230   }
    231 
    232   void ResourceFetcherPost(const GURL& url) {
    233     const char* kBody = "Really nifty POST body!";
    234 
    235     WebFrame* frame = GetRenderView()->GetWebView()->mainFrame();
    236 
    237     scoped_ptr<FetcherDelegate> delegate(new FetcherDelegate);
    238     scoped_ptr<ResourceFetcher> fetcher(ResourceFetcher::Create(url));
    239     fetcher->SetMethod("POST");
    240     fetcher->SetBody(kBody);
    241     fetcher->Start(frame, WebURLRequest::TargetIsMainFrame,
    242                    delegate->NewCallback());
    243 
    244     delegate->WaitForResponse();
    245     ASSERT_TRUE(delegate->completed());
    246     EXPECT_EQ(delegate->response().httpStatusCode(), 200);
    247     EXPECT_EQ(kBody, delegate->data());
    248   }
    249 
    250   void ResourceFetcherSetHeader(const GURL& url) {
    251     const char* kHeader = "Rather boring header.";
    252 
    253     WebFrame* frame = GetRenderView()->GetWebView()->mainFrame();
    254 
    255     scoped_ptr<FetcherDelegate> delegate(new FetcherDelegate);
    256     scoped_ptr<ResourceFetcher> fetcher(ResourceFetcher::Create(url));
    257     fetcher->SetHeader("header", kHeader);
    258     fetcher->Start(frame, WebURLRequest::TargetIsMainFrame,
    259                    delegate->NewCallback());
    260 
    261     delegate->WaitForResponse();
    262     ASSERT_TRUE(delegate->completed());
    263     EXPECT_EQ(delegate->response().httpStatusCode(), 200);
    264     EXPECT_EQ(kHeader, delegate->data());
    265   }
    266 };
    267 
    268 #if defined(OS_ANDROID)
    269 // Disable (http://crbug.com/248796).
    270 #define MAYBE_ResourceFetcher404 DISABLED_ResourceFetcher404
    271 #define MAYBE_ResourceFetcherDeletedInCallback \
    272   DISABLED_ResourceFetcherDeletedInCallback
    273 #define MAYBE_ResourceFetcherTimeout DISABLED_ResourceFetcherTimeout
    274 #define MAYBE_ResourceFetcherDownload DISABLED_ResourceFetcherDownload
    275 // Disable (http://crbug.com/341142).
    276 #define MAYBE_ResourceFetcherPost DISABLED_ResourceFetcherPost
    277 #define MAYBE_ResourceFetcherSetHeader DISABLED_ResourceFetcherSetHeader
    278 #else
    279 #define MAYBE_ResourceFetcher404 ResourceFetcher404
    280 #define MAYBE_ResourceFetcherDeletedInCallback ResourceFetcherDeletedInCallback
    281 #define MAYBE_ResourceFetcherTimeout ResourceFetcherTimeout
    282 #define MAYBE_ResourceFetcherDownload ResourceFetcherDownload
    283 #define MAYBE_ResourceFetcherPost ResourceFetcherPost
    284 #define MAYBE_ResourceFetcherSetHeader ResourceFetcherSetHeader
    285 #endif
    286 
    287 // Test a fetch from the test server.
    288 // If this flakes, use http://crbug.com/51622.
    289 IN_PROC_BROWSER_TEST_F(ResourceFetcherTests, MAYBE_ResourceFetcherDownload) {
    290   // Need to spin up the renderer.
    291   NavigateToURL(shell(), GURL(url::kAboutBlankURL));
    292 
    293   ASSERT_TRUE(test_server()->Start());
    294   GURL url(test_server()->GetURL("files/simple_page.html"));
    295 
    296   PostTaskToInProcessRendererAndWait(
    297         base::Bind(&ResourceFetcherTests::ResourceFetcherDownloadOnRenderer,
    298                    base::Unretained(this), url));
    299 }
    300 
    301 IN_PROC_BROWSER_TEST_F(ResourceFetcherTests, MAYBE_ResourceFetcher404) {
    302   // Need to spin up the renderer.
    303   NavigateToURL(shell(), GURL(url::kAboutBlankURL));
    304 
    305   // Test 404 response.
    306   ASSERT_TRUE(test_server()->Start());
    307   GURL url = test_server()->GetURL("files/thisfiledoesntexist.html");
    308 
    309   PostTaskToInProcessRendererAndWait(
    310         base::Bind(&ResourceFetcherTests::ResourceFetcher404OnRenderer,
    311                    base::Unretained(this), url));
    312 }
    313 
    314 // If this flakes, use http://crbug.com/51622.
    315 IN_PROC_BROWSER_TEST_F(ResourceFetcherTests, ResourceFetcherDidFail) {
    316   // Need to spin up the renderer.
    317   NavigateToURL(shell(), GURL(url::kAboutBlankURL));
    318 
    319   PostTaskToInProcessRendererAndWait(
    320         base::Bind(&ResourceFetcherTests::ResourceFetcherDidFailOnRenderer,
    321                    base::Unretained(this)));
    322 }
    323 
    324 IN_PROC_BROWSER_TEST_F(ResourceFetcherTests, MAYBE_ResourceFetcherTimeout) {
    325   // Need to spin up the renderer.
    326   NavigateToURL(shell(), GURL(url::kAboutBlankURL));
    327 
    328   // Grab a page that takes at least 1 sec to respond, but set the fetcher to
    329   // timeout in 0 sec.
    330   ASSERT_TRUE(test_server()->Start());
    331   GURL url(test_server()->GetURL("slow?1"));
    332 
    333   PostTaskToInProcessRendererAndWait(
    334         base::Bind(&ResourceFetcherTests::ResourceFetcherTimeoutOnRenderer,
    335                    base::Unretained(this), url));
    336 }
    337 
    338 IN_PROC_BROWSER_TEST_F(ResourceFetcherTests,
    339                        MAYBE_ResourceFetcherDeletedInCallback) {
    340   // Need to spin up the renderer.
    341   NavigateToURL(shell(), GURL(url::kAboutBlankURL));
    342 
    343   // Grab a page that takes at least 1 sec to respond, but set the fetcher to
    344   // timeout in 0 sec.
    345   ASSERT_TRUE(test_server()->Start());
    346   GURL url(test_server()->GetURL("slow?1"));
    347 
    348   PostTaskToInProcessRendererAndWait(
    349         base::Bind(
    350             &ResourceFetcherTests::ResourceFetcherDeletedInCallbackOnRenderer,
    351             base::Unretained(this), url));
    352 }
    353 
    354 
    355 
    356 // Test that ResourceFetchers can handle POSTs.
    357 IN_PROC_BROWSER_TEST_F(ResourceFetcherTests, MAYBE_ResourceFetcherPost) {
    358   // Need to spin up the renderer.
    359   NavigateToURL(shell(), GURL(url::kAboutBlankURL));
    360 
    361   // Grab a page that echos the POST body.
    362   ASSERT_TRUE(test_server()->Start());
    363   GURL url(test_server()->GetURL("echo"));
    364 
    365   PostTaskToInProcessRendererAndWait(
    366         base::Bind(
    367             &ResourceFetcherTests::ResourceFetcherPost,
    368             base::Unretained(this), url));
    369 }
    370 
    371 // Test that ResourceFetchers can set headers.
    372 IN_PROC_BROWSER_TEST_F(ResourceFetcherTests, MAYBE_ResourceFetcherSetHeader) {
    373   // Need to spin up the renderer.
    374   NavigateToURL(shell(), GURL(url::kAboutBlankURL));
    375 
    376   // Grab a page that echos the POST body.
    377   ASSERT_TRUE(test_server()->Start());
    378   GURL url(test_server()->GetURL("echoheader?header"));
    379 
    380   PostTaskToInProcessRendererAndWait(
    381         base::Bind(
    382             &ResourceFetcherTests::ResourceFetcherSetHeader,
    383             base::Unretained(this), url));
    384 }
    385 
    386 }  // namespace content
    387