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.
5 #include "content/public/renderer/resource_fetcher.h"
9 #include "base/bind_helpers.h"
10 #include "base/command_line.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/time/time.h"
13 #include "base/timer/timer.h"
14 #include "content/public/browser/render_view_host.h"
15 #include "content/public/browser/web_contents.h"
16 #include "content/public/common/content_switches.h"
17 #include "content/public/common/url_constants.h"
18 #include "content/public/renderer/render_view.h"
19 #include "content/public/test/content_browser_test.h"
20 #include "content/public/test/content_browser_test_utils.h"
21 #include "content/public/test/test_utils.h"
22 #include "content/shell/browser/shell.h"
23 #include "third_party/WebKit/public/platform/WebURLResponse.h"
24 #include "third_party/WebKit/public/web/WebFrame.h"
25 #include "third_party/WebKit/public/web/WebView.h"
27 using blink::WebFrame
;
28 using blink::WebURLRequest
;
29 using blink::WebURLResponse
;
33 static const int kMaxWaitTimeMs
= 5000;
35 class FetcherDelegate
{
40 // Start a repeating timer waiting for the download to complete. The
41 // callback has to be a static function, so we hold on to our instance.
42 FetcherDelegate::instance_
= this;
46 virtual ~FetcherDelegate() {}
48 ResourceFetcher::Callback
NewCallback() {
49 return base::Bind(&FetcherDelegate::OnURLFetchComplete
,
50 base::Unretained(this));
53 virtual void OnURLFetchComplete(const WebURLResponse
& response
,
54 const std::string
& data
) {
63 bool completed() const { return completed_
; }
64 bool timed_out() const { return timed_out_
; }
66 std::string
data() const { return data_
; }
67 const WebURLResponse
& response() const { return response_
; }
69 // Wait for the request to complete or timeout.
70 void WaitForResponse() {
71 scoped_refptr
<MessageLoopRunner
> runner
= new MessageLoopRunner
;
72 quit_task_
= runner
->QuitClosure();
77 timer_
.Start(FROM_HERE
,
78 base::TimeDelta::FromMilliseconds(kMaxWaitTimeMs
),
80 &FetcherDelegate::TimerFired
);
84 ASSERT_FALSE(completed_
);
89 FAIL() << "fetch timed out";
92 static FetcherDelegate
* instance_
;
95 base::OneShotTimer
<FetcherDelegate
> timer_
;
98 WebURLResponse response_
;
100 base::Closure quit_task_
;
103 FetcherDelegate
* FetcherDelegate::instance_
= NULL
;
105 class EvilFetcherDelegate
: public FetcherDelegate
{
107 ~EvilFetcherDelegate() override
{}
109 void SetFetcher(ResourceFetcher
* fetcher
) {
110 fetcher_
.reset(fetcher
);
113 void OnURLFetchComplete(const WebURLResponse
& response
,
114 const std::string
& data
) override
{
115 FetcherDelegate::OnURLFetchComplete(response
, data
);
117 // Destroy the ResourceFetcher here. We are testing that upon returning
118 // to the ResourceFetcher that it does not crash. This must be done after
119 // calling FetcherDelegate::OnURLFetchComplete, since deleting the fetcher
120 // invalidates |response| and |data|.
125 scoped_ptr
<ResourceFetcher
> fetcher_
;
128 class ResourceFetcherTests
: public ContentBrowserTest
{
130 ResourceFetcherTests() : render_view_routing_id_(MSG_ROUTING_NONE
) {}
132 void SetUpCommandLine(base::CommandLine
* command_line
) override
{
133 command_line
->AppendSwitch(switches::kSingleProcess
);
135 // Don't want to try to create a GPU process.
136 command_line
->AppendSwitch(switches::kDisableGpu
);
140 void SetUpOnMainThread() override
{
141 render_view_routing_id_
=
142 shell()->web_contents()->GetRenderViewHost()->GetRoutingID();
145 RenderView
* GetRenderView() {
146 return RenderView::FromRoutingID(render_view_routing_id_
);
149 void ResourceFetcherDownloadOnRenderer(const GURL
& url
) {
150 WebFrame
* frame
= GetRenderView()->GetWebView()->mainFrame();
152 scoped_ptr
<FetcherDelegate
> delegate(new FetcherDelegate
);
153 scoped_ptr
<ResourceFetcher
> fetcher(ResourceFetcher::Create(url
));
154 fetcher
->Start(frame
,
155 WebURLRequest::RequestContextInternal
,
156 WebURLRequest::FrameTypeNone
,
157 ResourceFetcher::PLATFORM_LOADER
,
158 delegate
->NewCallback());
160 delegate
->WaitForResponse();
162 ASSERT_TRUE(delegate
->completed());
163 EXPECT_EQ(delegate
->response().httpStatusCode(), 200);
164 std::string text
= delegate
->data();
165 EXPECT_TRUE(text
.find("Basic html test.") != std::string::npos
);
168 void ResourceFetcher404OnRenderer(const GURL
& url
) {
169 WebFrame
* frame
= GetRenderView()->GetWebView()->mainFrame();
171 scoped_ptr
<FetcherDelegate
> delegate(new FetcherDelegate
);
172 scoped_ptr
<ResourceFetcher
> fetcher(ResourceFetcher::Create(url
));
173 fetcher
->Start(frame
,
174 WebURLRequest::RequestContextInternal
,
175 WebURLRequest::FrameTypeNone
,
176 ResourceFetcher::PLATFORM_LOADER
,
177 delegate
->NewCallback());
179 delegate
->WaitForResponse();
181 ASSERT_TRUE(delegate
->completed());
182 EXPECT_EQ(delegate
->response().httpStatusCode(), 404);
183 EXPECT_TRUE(delegate
->data().find("Not Found.") != std::string::npos
);
186 void ResourceFetcherDidFailOnRenderer() {
187 WebFrame
* frame
= GetRenderView()->GetWebView()->mainFrame();
189 // Try to fetch a page on a site that doesn't exist.
190 GURL
url("http://localhost:1339/doesnotexist");
191 scoped_ptr
<FetcherDelegate
> delegate(new FetcherDelegate
);
192 scoped_ptr
<ResourceFetcher
> fetcher(ResourceFetcher::Create(url
));
193 fetcher
->Start(frame
,
194 WebURLRequest::RequestContextInternal
,
195 WebURLRequest::FrameTypeNone
,
196 ResourceFetcher::PLATFORM_LOADER
,
197 delegate
->NewCallback());
199 delegate
->WaitForResponse();
201 // When we fail, we still call the Delegate callback but we pass in empty
203 EXPECT_TRUE(delegate
->completed());
204 EXPECT_TRUE(delegate
->response().isNull());
205 EXPECT_EQ(delegate
->data(), std::string());
206 EXPECT_FALSE(delegate
->timed_out());
209 void ResourceFetcherTimeoutOnRenderer(const GURL
& url
) {
210 WebFrame
* frame
= GetRenderView()->GetWebView()->mainFrame();
212 scoped_ptr
<FetcherDelegate
> delegate(new FetcherDelegate
);
213 scoped_ptr
<ResourceFetcher
> fetcher(ResourceFetcher::Create(url
));
214 fetcher
->Start(frame
,
215 WebURLRequest::RequestContextInternal
,
216 WebURLRequest::FrameTypeNone
,
217 ResourceFetcher::PLATFORM_LOADER
,
218 delegate
->NewCallback());
219 fetcher
->SetTimeout(base::TimeDelta());
221 delegate
->WaitForResponse();
223 // When we timeout, we still call the Delegate callback but we pass in empty
225 EXPECT_TRUE(delegate
->completed());
226 EXPECT_TRUE(delegate
->response().isNull());
227 EXPECT_EQ(delegate
->data(), std::string());
228 EXPECT_FALSE(delegate
->timed_out());
231 void ResourceFetcherDeletedInCallbackOnRenderer(const GURL
& url
) {
232 WebFrame
* frame
= GetRenderView()->GetWebView()->mainFrame();
234 scoped_ptr
<EvilFetcherDelegate
> delegate(new EvilFetcherDelegate
);
235 scoped_ptr
<ResourceFetcher
> fetcher(ResourceFetcher::Create(url
));
236 fetcher
->Start(frame
,
237 WebURLRequest::RequestContextInternal
,
238 WebURLRequest::FrameTypeNone
,
239 ResourceFetcher::PLATFORM_LOADER
,
240 delegate
->NewCallback());
241 fetcher
->SetTimeout(base::TimeDelta());
242 delegate
->SetFetcher(fetcher
.release());
244 delegate
->WaitForResponse();
245 EXPECT_FALSE(delegate
->timed_out());
248 void ResourceFetcherPost(const GURL
& url
) {
249 const char* kBody
= "Really nifty POST body!";
251 WebFrame
* frame
= GetRenderView()->GetWebView()->mainFrame();
253 scoped_ptr
<FetcherDelegate
> delegate(new FetcherDelegate
);
254 scoped_ptr
<ResourceFetcher
> fetcher(ResourceFetcher::Create(url
));
255 fetcher
->SetMethod("POST");
256 fetcher
->SetBody(kBody
);
257 fetcher
->Start(frame
,
258 WebURLRequest::RequestContextInternal
,
259 WebURLRequest::FrameTypeNone
,
260 ResourceFetcher::PLATFORM_LOADER
,
261 delegate
->NewCallback());
263 delegate
->WaitForResponse();
264 ASSERT_TRUE(delegate
->completed());
265 EXPECT_EQ(delegate
->response().httpStatusCode(), 200);
266 EXPECT_EQ(kBody
, delegate
->data());
269 void ResourceFetcherSetHeader(const GURL
& url
) {
270 const char* kHeader
= "Rather boring header.";
272 WebFrame
* frame
= GetRenderView()->GetWebView()->mainFrame();
274 scoped_ptr
<FetcherDelegate
> delegate(new FetcherDelegate
);
275 scoped_ptr
<ResourceFetcher
> fetcher(ResourceFetcher::Create(url
));
276 fetcher
->SetHeader("header", kHeader
);
277 fetcher
->Start(frame
,
278 WebURLRequest::RequestContextInternal
,
279 WebURLRequest::FrameTypeNone
,
280 ResourceFetcher::PLATFORM_LOADER
,
281 delegate
->NewCallback());
283 delegate
->WaitForResponse();
284 ASSERT_TRUE(delegate
->completed());
285 EXPECT_EQ(delegate
->response().httpStatusCode(), 200);
286 EXPECT_EQ(kHeader
, delegate
->data());
289 int32 render_view_routing_id_
;
292 // Test a fetch from the test server.
293 // If this flakes, use http://crbug.com/51622.
294 IN_PROC_BROWSER_TEST_F(ResourceFetcherTests
, ResourceFetcherDownload
) {
295 // Need to spin up the renderer.
296 NavigateToURL(shell(), GURL(url::kAboutBlankURL
));
298 ASSERT_TRUE(test_server()->Start());
299 GURL
url(test_server()->GetURL("files/simple_page.html"));
301 PostTaskToInProcessRendererAndWait(
302 base::Bind(&ResourceFetcherTests::ResourceFetcherDownloadOnRenderer
,
303 base::Unretained(this), url
));
306 IN_PROC_BROWSER_TEST_F(ResourceFetcherTests
, ResourceFetcher404
) {
307 // Need to spin up the renderer.
308 NavigateToURL(shell(), GURL(url::kAboutBlankURL
));
310 // Test 404 response.
311 ASSERT_TRUE(test_server()->Start());
312 GURL url
= test_server()->GetURL("files/thisfiledoesntexist.html");
314 PostTaskToInProcessRendererAndWait(
315 base::Bind(&ResourceFetcherTests::ResourceFetcher404OnRenderer
,
316 base::Unretained(this), url
));
319 // If this flakes, use http://crbug.com/51622.
320 IN_PROC_BROWSER_TEST_F(ResourceFetcherTests
, ResourceFetcherDidFail
) {
321 // Need to spin up the renderer.
322 NavigateToURL(shell(), GURL(url::kAboutBlankURL
));
324 PostTaskToInProcessRendererAndWait(
325 base::Bind(&ResourceFetcherTests::ResourceFetcherDidFailOnRenderer
,
326 base::Unretained(this)));
329 IN_PROC_BROWSER_TEST_F(ResourceFetcherTests
, ResourceFetcherTimeout
) {
330 // Need to spin up the renderer.
331 NavigateToURL(shell(), GURL(url::kAboutBlankURL
));
333 // Grab a page that takes at least 1 sec to respond, but set the fetcher to
335 ASSERT_TRUE(test_server()->Start());
336 GURL
url(test_server()->GetURL("slow?1"));
338 PostTaskToInProcessRendererAndWait(
339 base::Bind(&ResourceFetcherTests::ResourceFetcherTimeoutOnRenderer
,
340 base::Unretained(this), url
));
343 IN_PROC_BROWSER_TEST_F(ResourceFetcherTests
, ResourceFetcherDeletedInCallback
) {
344 // Need to spin up the renderer.
345 NavigateToURL(shell(), GURL(url::kAboutBlankURL
));
347 // Grab a page that takes at least 1 sec to respond, but set the fetcher to
349 ASSERT_TRUE(test_server()->Start());
350 GURL
url(test_server()->GetURL("slow?1"));
352 PostTaskToInProcessRendererAndWait(
354 &ResourceFetcherTests::ResourceFetcherDeletedInCallbackOnRenderer
,
355 base::Unretained(this), url
));
360 // Test that ResourceFetchers can handle POSTs.
361 IN_PROC_BROWSER_TEST_F(ResourceFetcherTests
, ResourceFetcherPost
) {
362 // Need to spin up the renderer.
363 NavigateToURL(shell(), GURL(url::kAboutBlankURL
));
365 // Grab a page that echos the POST body.
366 ASSERT_TRUE(test_server()->Start());
367 GURL
url(test_server()->GetURL("echo"));
369 PostTaskToInProcessRendererAndWait(
371 &ResourceFetcherTests::ResourceFetcherPost
,
372 base::Unretained(this), url
));
375 // Test that ResourceFetchers can set headers.
376 IN_PROC_BROWSER_TEST_F(ResourceFetcherTests
, ResourceFetcherSetHeader
) {
377 // Need to spin up the renderer.
378 NavigateToURL(shell(), GURL(url::kAboutBlankURL
));
380 // Grab a page that echos the POST body.
381 ASSERT_TRUE(test_server()->Start());
382 GURL
url(test_server()->GetURL("echoheader?header"));
384 PostTaskToInProcessRendererAndWait(
386 &ResourceFetcherTests::ResourceFetcherSetHeader
,
387 base::Unretained(this), url
));
390 } // namespace content