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"
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"
24 using blink::WebFrame
;
25 using blink::WebURLRequest
;
26 using blink::WebURLResponse
;
30 // The first RenderFrame is routing ID 1, and the first RenderView is 2.
31 const int kRenderViewRoutingId
= 2;
37 static const int kMaxWaitTimeMs
= 5000;
39 class FetcherDelegate
{
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;
50 virtual ~FetcherDelegate() {}
52 ResourceFetcher::Callback
NewCallback() {
53 return base::Bind(&FetcherDelegate::OnURLFetchComplete
,
54 base::Unretained(this));
57 virtual void OnURLFetchComplete(const WebURLResponse
& response
,
58 const std::string
& data
) {
67 bool completed() const { return completed_
; }
68 bool timed_out() const { return timed_out_
; }
70 std::string
data() const { return data_
; }
71 const WebURLResponse
& response() const { return response_
; }
73 // Wait for the request to complete or timeout.
74 void WaitForResponse() {
75 scoped_refptr
<MessageLoopRunner
> runner
= new MessageLoopRunner
;
76 quit_task_
= runner
->QuitClosure();
81 timer_
.Start(FROM_HERE
,
82 base::TimeDelta::FromMilliseconds(kMaxWaitTimeMs
),
84 &FetcherDelegate::TimerFired
);
88 ASSERT_FALSE(completed_
);
93 FAIL() << "fetch timed out";
96 static FetcherDelegate
* instance_
;
99 base::OneShotTimer
<FetcherDelegate
> timer_
;
102 WebURLResponse response_
;
104 base::Closure quit_task_
;
107 FetcherDelegate
* FetcherDelegate::instance_
= NULL
;
109 class EvilFetcherDelegate
: public FetcherDelegate
{
111 virtual ~EvilFetcherDelegate() {}
113 void SetFetcher(ResourceFetcher
* fetcher
) {
114 fetcher_
.reset(fetcher
);
117 virtual void OnURLFetchComplete(const WebURLResponse
& response
,
118 const std::string
& data
) OVERRIDE
{
119 FetcherDelegate::OnURLFetchComplete(response
, data
);
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|.
129 scoped_ptr
<ResourceFetcher
> fetcher_
;
132 class ResourceFetcherTests
: public ContentBrowserTest
{
134 virtual void SetUpCommandLine(CommandLine
* command_line
) OVERRIDE
{
135 command_line
->AppendSwitch(switches::kSingleProcess
);
137 // Don't want to try to create a GPU process.
138 command_line
->AppendSwitch(switches::kDisableGpu
);
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
);
148 void ResourceFetcherDownloadOnRenderer(const GURL
& url
) {
149 WebFrame
* frame
= GetRenderView()->GetWebView()->mainFrame();
151 scoped_ptr
<FetcherDelegate
> delegate(new FetcherDelegate
);
152 scoped_ptr
<ResourceFetcher
> fetcher(ResourceFetcher::Create(url
));
153 fetcher
->Start(frame
,
154 WebURLRequest::RequestContextInternal
,
155 WebURLRequest::FrameTypeNone
,
156 delegate
->NewCallback());
158 delegate
->WaitForResponse();
160 ASSERT_TRUE(delegate
->completed());
161 EXPECT_EQ(delegate
->response().httpStatusCode(), 200);
162 std::string text
= delegate
->data();
163 EXPECT_TRUE(text
.find("Basic html test.") != std::string::npos
);
166 void ResourceFetcher404OnRenderer(const GURL
& url
) {
167 WebFrame
* frame
= GetRenderView()->GetWebView()->mainFrame();
169 scoped_ptr
<FetcherDelegate
> delegate(new FetcherDelegate
);
170 scoped_ptr
<ResourceFetcher
> fetcher(ResourceFetcher::Create(url
));
171 fetcher
->Start(frame
,
172 WebURLRequest::RequestContextInternal
,
173 WebURLRequest::FrameTypeNone
,
174 delegate
->NewCallback());
176 delegate
->WaitForResponse();
178 ASSERT_TRUE(delegate
->completed());
179 EXPECT_EQ(delegate
->response().httpStatusCode(), 404);
180 EXPECT_TRUE(delegate
->data().find("Not Found.") != std::string::npos
);
183 void ResourceFetcherDidFailOnRenderer() {
184 WebFrame
* frame
= GetRenderView()->GetWebView()->mainFrame();
186 // Try to fetch a page on a site that doesn't exist.
187 GURL
url("http://localhost:1339/doesnotexist");
188 scoped_ptr
<FetcherDelegate
> delegate(new FetcherDelegate
);
189 scoped_ptr
<ResourceFetcher
> fetcher(ResourceFetcher::Create(url
));
190 fetcher
->Start(frame
,
191 WebURLRequest::RequestContextInternal
,
192 WebURLRequest::FrameTypeNone
,
193 delegate
->NewCallback());
195 delegate
->WaitForResponse();
197 // When we fail, we still call the Delegate callback but we pass in empty
199 EXPECT_TRUE(delegate
->completed());
200 EXPECT_TRUE(delegate
->response().isNull());
201 EXPECT_EQ(delegate
->data(), std::string());
202 EXPECT_FALSE(delegate
->timed_out());
205 void ResourceFetcherTimeoutOnRenderer(const GURL
& url
) {
206 WebFrame
* frame
= GetRenderView()->GetWebView()->mainFrame();
208 scoped_ptr
<FetcherDelegate
> delegate(new FetcherDelegate
);
209 scoped_ptr
<ResourceFetcher
> fetcher(ResourceFetcher::Create(url
));
210 fetcher
->Start(frame
,
211 WebURLRequest::RequestContextInternal
,
212 WebURLRequest::FrameTypeNone
,
213 delegate
->NewCallback());
214 fetcher
->SetTimeout(base::TimeDelta());
216 delegate
->WaitForResponse();
218 // When we timeout, we still call the Delegate callback but we pass in empty
220 EXPECT_TRUE(delegate
->completed());
221 EXPECT_TRUE(delegate
->response().isNull());
222 EXPECT_EQ(delegate
->data(), std::string());
223 EXPECT_FALSE(delegate
->timed_out());
226 void ResourceFetcherDeletedInCallbackOnRenderer(const GURL
& url
) {
227 WebFrame
* frame
= GetRenderView()->GetWebView()->mainFrame();
229 scoped_ptr
<EvilFetcherDelegate
> delegate(new EvilFetcherDelegate
);
230 scoped_ptr
<ResourceFetcher
> fetcher(ResourceFetcher::Create(url
));
231 fetcher
->Start(frame
,
232 WebURLRequest::RequestContextInternal
,
233 WebURLRequest::FrameTypeNone
,
234 delegate
->NewCallback());
235 fetcher
->SetTimeout(base::TimeDelta());
236 delegate
->SetFetcher(fetcher
.release());
238 delegate
->WaitForResponse();
239 EXPECT_FALSE(delegate
->timed_out());
242 void ResourceFetcherPost(const GURL
& url
) {
243 const char* kBody
= "Really nifty POST body!";
245 WebFrame
* frame
= GetRenderView()->GetWebView()->mainFrame();
247 scoped_ptr
<FetcherDelegate
> delegate(new FetcherDelegate
);
248 scoped_ptr
<ResourceFetcher
> fetcher(ResourceFetcher::Create(url
));
249 fetcher
->SetMethod("POST");
250 fetcher
->SetBody(kBody
);
251 fetcher
->Start(frame
,
252 WebURLRequest::RequestContextInternal
,
253 WebURLRequest::FrameTypeNone
,
254 delegate
->NewCallback());
256 delegate
->WaitForResponse();
257 ASSERT_TRUE(delegate
->completed());
258 EXPECT_EQ(delegate
->response().httpStatusCode(), 200);
259 EXPECT_EQ(kBody
, delegate
->data());
262 void ResourceFetcherSetHeader(const GURL
& url
) {
263 const char* kHeader
= "Rather boring header.";
265 WebFrame
* frame
= GetRenderView()->GetWebView()->mainFrame();
267 scoped_ptr
<FetcherDelegate
> delegate(new FetcherDelegate
);
268 scoped_ptr
<ResourceFetcher
> fetcher(ResourceFetcher::Create(url
));
269 fetcher
->SetHeader("header", kHeader
);
270 fetcher
->Start(frame
,
271 WebURLRequest::RequestContextInternal
,
272 WebURLRequest::FrameTypeNone
,
273 delegate
->NewCallback());
275 delegate
->WaitForResponse();
276 ASSERT_TRUE(delegate
->completed());
277 EXPECT_EQ(delegate
->response().httpStatusCode(), 200);
278 EXPECT_EQ(kHeader
, delegate
->data());
282 #if defined(OS_ANDROID)
283 // Disable (http://crbug.com/248796).
284 #define MAYBE_ResourceFetcher404 DISABLED_ResourceFetcher404
285 #define MAYBE_ResourceFetcherDeletedInCallback \
286 DISABLED_ResourceFetcherDeletedInCallback
287 #define MAYBE_ResourceFetcherTimeout DISABLED_ResourceFetcherTimeout
288 #define MAYBE_ResourceFetcherDownload DISABLED_ResourceFetcherDownload
289 // Disable (http://crbug.com/341142).
290 #define MAYBE_ResourceFetcherPost DISABLED_ResourceFetcherPost
291 #define MAYBE_ResourceFetcherSetHeader DISABLED_ResourceFetcherSetHeader
293 #define MAYBE_ResourceFetcher404 ResourceFetcher404
294 #define MAYBE_ResourceFetcherDeletedInCallback ResourceFetcherDeletedInCallback
295 #define MAYBE_ResourceFetcherTimeout ResourceFetcherTimeout
296 #define MAYBE_ResourceFetcherDownload ResourceFetcherDownload
297 #define MAYBE_ResourceFetcherPost ResourceFetcherPost
298 #define MAYBE_ResourceFetcherSetHeader ResourceFetcherSetHeader
301 // Test a fetch from the test server.
302 // If this flakes, use http://crbug.com/51622.
303 IN_PROC_BROWSER_TEST_F(ResourceFetcherTests
, MAYBE_ResourceFetcherDownload
) {
304 // Need to spin up the renderer.
305 NavigateToURL(shell(), GURL(url::kAboutBlankURL
));
307 ASSERT_TRUE(test_server()->Start());
308 GURL
url(test_server()->GetURL("files/simple_page.html"));
310 PostTaskToInProcessRendererAndWait(
311 base::Bind(&ResourceFetcherTests::ResourceFetcherDownloadOnRenderer
,
312 base::Unretained(this), url
));
315 IN_PROC_BROWSER_TEST_F(ResourceFetcherTests
, MAYBE_ResourceFetcher404
) {
316 // Need to spin up the renderer.
317 NavigateToURL(shell(), GURL(url::kAboutBlankURL
));
319 // Test 404 response.
320 ASSERT_TRUE(test_server()->Start());
321 GURL url
= test_server()->GetURL("files/thisfiledoesntexist.html");
323 PostTaskToInProcessRendererAndWait(
324 base::Bind(&ResourceFetcherTests::ResourceFetcher404OnRenderer
,
325 base::Unretained(this), url
));
328 // If this flakes, use http://crbug.com/51622.
329 IN_PROC_BROWSER_TEST_F(ResourceFetcherTests
, ResourceFetcherDidFail
) {
330 // Need to spin up the renderer.
331 NavigateToURL(shell(), GURL(url::kAboutBlankURL
));
333 PostTaskToInProcessRendererAndWait(
334 base::Bind(&ResourceFetcherTests::ResourceFetcherDidFailOnRenderer
,
335 base::Unretained(this)));
338 IN_PROC_BROWSER_TEST_F(ResourceFetcherTests
, MAYBE_ResourceFetcherTimeout
) {
339 // Need to spin up the renderer.
340 NavigateToURL(shell(), GURL(url::kAboutBlankURL
));
342 // Grab a page that takes at least 1 sec to respond, but set the fetcher to
344 ASSERT_TRUE(test_server()->Start());
345 GURL
url(test_server()->GetURL("slow?1"));
347 PostTaskToInProcessRendererAndWait(
348 base::Bind(&ResourceFetcherTests::ResourceFetcherTimeoutOnRenderer
,
349 base::Unretained(this), url
));
352 IN_PROC_BROWSER_TEST_F(ResourceFetcherTests
,
353 MAYBE_ResourceFetcherDeletedInCallback
) {
354 // Need to spin up the renderer.
355 NavigateToURL(shell(), GURL(url::kAboutBlankURL
));
357 // Grab a page that takes at least 1 sec to respond, but set the fetcher to
359 ASSERT_TRUE(test_server()->Start());
360 GURL
url(test_server()->GetURL("slow?1"));
362 PostTaskToInProcessRendererAndWait(
364 &ResourceFetcherTests::ResourceFetcherDeletedInCallbackOnRenderer
,
365 base::Unretained(this), url
));
370 // Test that ResourceFetchers can handle POSTs.
371 IN_PROC_BROWSER_TEST_F(ResourceFetcherTests
, MAYBE_ResourceFetcherPost
) {
372 // Need to spin up the renderer.
373 NavigateToURL(shell(), GURL(url::kAboutBlankURL
));
375 // Grab a page that echos the POST body.
376 ASSERT_TRUE(test_server()->Start());
377 GURL
url(test_server()->GetURL("echo"));
379 PostTaskToInProcessRendererAndWait(
381 &ResourceFetcherTests::ResourceFetcherPost
,
382 base::Unretained(this), url
));
385 // Test that ResourceFetchers can set headers.
386 IN_PROC_BROWSER_TEST_F(ResourceFetcherTests
, MAYBE_ResourceFetcherSetHeader
) {
387 // Need to spin up the renderer.
388 NavigateToURL(shell(), GURL(url::kAboutBlankURL
));
390 // Grab a page that echos the POST body.
391 ASSERT_TRUE(test_server()->Start());
392 GURL
url(test_server()->GetURL("echoheader?header"));
394 PostTaskToInProcessRendererAndWait(
396 &ResourceFetcherTests::ResourceFetcherSetHeader
,
397 base::Unretained(this), url
));
400 } // namespace content