1 // Copyright 2014 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 "base/command_line.h"
6 #include "base/macros.h"
7 #include "base/memory/ref_counted.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/run_loop.h"
10 #include "content/browser/frame_host/navigation_request_info.h"
11 #include "content/browser/loader/navigation_url_loader_delegate.h"
12 #include "content/browser/loader/navigation_url_loader_impl.h"
13 #include "content/browser/loader/resource_dispatcher_host_impl.h"
14 #include "content/browser/streams/stream.h"
15 #include "content/browser/streams/stream_context.h"
16 #include "content/browser/streams/stream_registry.h"
17 #include "content/browser/streams/stream_url_request_job.h"
18 #include "content/common/navigation_params.h"
19 #include "content/public/browser/browser_context.h"
20 #include "content/public/browser/resource_context.h"
21 #include "content/public/browser/resource_dispatcher_host_delegate.h"
22 #include "content/public/browser/stream_handle.h"
23 #include "content/public/common/content_switches.h"
24 #include "content/public/common/resource_response.h"
25 #include "content/public/test/test_browser_context.h"
26 #include "content/public/test/test_browser_thread_bundle.h"
27 #include "net/base/net_errors.h"
28 #include "net/http/http_response_headers.h"
29 #include "net/url_request/redirect_info.h"
30 #include "net/url_request/url_request.h"
31 #include "net/url_request/url_request_context.h"
32 #include "net/url_request/url_request_job_factory_impl.h"
33 #include "net/url_request/url_request_test_job.h"
34 #include "net/url_request/url_request_test_util.h"
35 #include "testing/gtest/include/gtest/gtest.h"
41 class StreamProtocolHandler
42 : public net::URLRequestJobFactory::ProtocolHandler
{
44 StreamProtocolHandler(StreamRegistry
* registry
) : registry_(registry
) {}
46 // net::URLRequestJobFactory::ProtocolHandler implementation.
47 net::URLRequestJob
* MaybeCreateJob(
48 net::URLRequest
* request
,
49 net::NetworkDelegate
* network_delegate
) const override
{
50 scoped_refptr
<Stream
> stream
= registry_
->GetStream(request
->url());
52 return new StreamURLRequestJob(request
, network_delegate
, stream
);
57 StreamRegistry
* registry_
;
59 DISALLOW_COPY_AND_ASSIGN(StreamProtocolHandler
);
62 class TestNavigationURLLoaderDelegate
: public NavigationURLLoaderDelegate
{
64 TestNavigationURLLoaderDelegate()
68 const net::RedirectInfo
& redirect_info() const { return redirect_info_
; }
69 ResourceResponse
* redirect_response() const {
70 return redirect_response_
.get();
72 ResourceResponse
* response() const { return response_
.get(); }
73 StreamHandle
* body() const { return body_
.get(); }
74 int net_error() const { return net_error_
; }
76 void WaitForRequestRedirected() {
77 request_redirected_
.reset(new base::RunLoop
);
78 request_redirected_
->Run();
79 request_redirected_
.reset();
82 void WaitForResponseStarted() {
83 response_started_
.reset(new base::RunLoop
);
84 response_started_
->Run();
85 response_started_
.reset();
88 void WaitForRequestFailed() {
89 request_failed_
.reset(new base::RunLoop
);
90 request_failed_
->Run();
91 request_failed_
.reset();
98 // NavigationURLLoaderDelegate implementation.
99 void OnRequestRedirected(
100 const net::RedirectInfo
& redirect_info
,
101 const scoped_refptr
<ResourceResponse
>& response
) override
{
102 redirect_info_
= redirect_info
;
103 redirect_response_
= response
;
104 ASSERT_TRUE(request_redirected_
);
105 request_redirected_
->Quit();
108 void OnResponseStarted(const scoped_refptr
<ResourceResponse
>& response
,
109 scoped_ptr
<StreamHandle
> body
) override
{
110 response_
= response
;
112 ASSERT_TRUE(response_started_
);
113 response_started_
->Quit();
116 void OnRequestFailed(int net_error
) override
{
117 net_error_
= net_error
;
118 ASSERT_TRUE(request_failed_
);
119 request_failed_
->Quit();
123 net::RedirectInfo redirect_info_
;
124 scoped_refptr
<ResourceResponse
> redirect_response_
;
125 scoped_refptr
<ResourceResponse
> response_
;
126 scoped_ptr
<StreamHandle
> body_
;
129 scoped_ptr
<base::RunLoop
> request_redirected_
;
130 scoped_ptr
<base::RunLoop
> response_started_
;
131 scoped_ptr
<base::RunLoop
> request_failed_
;
134 class RequestBlockingResourceDispatcherHostDelegate
135 : public ResourceDispatcherHostDelegate
{
137 // ResourceDispatcherHostDelegate implementation:
138 bool ShouldBeginRequest(const std::string
& method
,
140 ResourceType resource_type
,
141 ResourceContext
* resource_context
) override
{
148 class NavigationURLLoaderTest
: public testing::Test
{
150 NavigationURLLoaderTest()
151 : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP
),
152 browser_context_(new TestBrowserContext
) {
153 BrowserContext::EnsureResourceContextInitialized(browser_context_
.get());
154 base::RunLoop().RunUntilIdle();
155 net::URLRequestContext
* request_context
=
156 browser_context_
->GetResourceContext()->GetRequestContext();
157 // Attach URLRequestTestJob and make streams work.
158 job_factory_
.SetProtocolHandler(
159 "test", net::URLRequestTestJob::CreateProtocolHandler());
160 job_factory_
.SetProtocolHandler(
161 "blob", new StreamProtocolHandler(
162 StreamContext::GetFor(browser_context_
.get())->registry()));
163 request_context
->set_job_factory(&job_factory_
);
165 // NavigationURLLoader is only used for browser-side navigations.
166 CommandLine::ForCurrentProcess()->AppendSwitch(
167 switches::kEnableBrowserSideNavigation
);
170 scoped_ptr
<NavigationURLLoader
> MakeTestLoader(
172 NavigationURLLoaderDelegate
* delegate
) {
173 FrameHostMsg_BeginNavigation_Params begin_params
;
174 CommonNavigationParams common_params
;
175 begin_params
.method
= "GET";
176 common_params
.url
= url
;
177 scoped_ptr
<NavigationRequestInfo
> request_info(
178 new NavigationRequestInfo(begin_params
));
179 request_info
->first_party_for_cookies
= url
;
180 request_info
->is_main_frame
= true;
182 return NavigationURLLoader::Create(
183 browser_context_
.get(), 0,
184 common_params
, request_info
.Pass(), nullptr, delegate
);
187 // Helper function for fetching the body of a URL to a string.
188 std::string
FetchURL(const GURL
& url
) {
189 net::TestDelegate delegate
;
190 net::URLRequestContext
* request_context
=
191 browser_context_
->GetResourceContext()->GetRequestContext();
192 scoped_ptr
<net::URLRequest
> request(request_context
->CreateRequest(
193 url
, net::DEFAULT_PRIORITY
, &delegate
, nullptr));
195 base::RunLoop().Run();
197 EXPECT_TRUE(request
->status().is_success());
198 EXPECT_EQ(200, request
->response_headers()->response_code());
199 return delegate
.data_received();
203 TestBrowserThreadBundle thread_bundle_
;
204 net::URLRequestJobFactoryImpl job_factory_
;
205 scoped_ptr
<TestBrowserContext
> browser_context_
;
206 ResourceDispatcherHostImpl host_
;
209 // Tests that a basic request works.
210 TEST_F(NavigationURLLoaderTest
, Basic
) {
211 TestNavigationURLLoaderDelegate delegate
;
212 scoped_ptr
<NavigationURLLoader
> loader
=
213 MakeTestLoader(net::URLRequestTestJob::test_url_1(), &delegate
);
215 // Wait for the response to come back.
216 delegate
.WaitForResponseStarted();
218 // Check the response is correct.
219 EXPECT_EQ("text/html", delegate
.response()->head
.mime_type
);
220 EXPECT_EQ(200, delegate
.response()->head
.headers
->response_code());
222 // Check the body is correct.
223 EXPECT_EQ(net::URLRequestTestJob::test_data_1(),
224 FetchURL(delegate
.body()->GetURL()));
227 // Tests that request failures are propagated correctly.
228 TEST_F(NavigationURLLoaderTest
, RequestFailed
) {
229 TestNavigationURLLoaderDelegate delegate
;
230 scoped_ptr
<NavigationURLLoader
> loader
=
231 MakeTestLoader(GURL("bogus:bogus"), &delegate
);
233 // Wait for the request to fail as expected.
234 delegate
.WaitForRequestFailed();
235 EXPECT_EQ(net::ERR_UNKNOWN_URL_SCHEME
, delegate
.net_error());
238 // Test that redirects are sent to the delegate.
239 TEST_F(NavigationURLLoaderTest
, RequestRedirected
) {
240 // Fake a top-level request. Choose a URL which redirects so the request can
241 // be paused before the response comes in.
242 TestNavigationURLLoaderDelegate delegate
;
243 scoped_ptr
<NavigationURLLoader
> loader
=
244 MakeTestLoader(net::URLRequestTestJob::test_url_redirect_to_url_2(),
247 // Wait for the request to redirect.
248 delegate
.WaitForRequestRedirected();
249 EXPECT_EQ(net::URLRequestTestJob::test_url_2(),
250 delegate
.redirect_info().new_url
);
251 EXPECT_EQ("GET", delegate
.redirect_info().new_method
);
252 EXPECT_EQ(net::URLRequestTestJob::test_url_2(),
253 delegate
.redirect_info().new_first_party_for_cookies
);
254 EXPECT_EQ(302, delegate
.redirect_response()->head
.headers
->response_code());
256 // Wait for the response to complete.
257 loader
->FollowRedirect();
258 delegate
.WaitForResponseStarted();
260 // Check the response is correct.
261 EXPECT_EQ("text/html", delegate
.response()->head
.mime_type
);
262 EXPECT_EQ(200, delegate
.response()->head
.headers
->response_code());
264 // Release the body and check it is correct.
265 EXPECT_TRUE(net::URLRequestTestJob::ProcessOnePendingMessage());
266 EXPECT_EQ(net::URLRequestTestJob::test_data_2(),
267 FetchURL(delegate
.body()->GetURL()));
270 // Tests that the destroying the loader cancels the request.
271 TEST_F(NavigationURLLoaderTest
, CancelOnDestruct
) {
272 // Fake a top-level request. Choose a URL which redirects so the request can
273 // be paused before the response comes in.
274 TestNavigationURLLoaderDelegate delegate
;
275 scoped_ptr
<NavigationURLLoader
> loader
=
276 MakeTestLoader(net::URLRequestTestJob::test_url_redirect_to_url_2(),
279 // Wait for the request to redirect.
280 delegate
.WaitForRequestRedirected();
282 // Destroy the loader and verify that URLRequestTestJob no longer has anything
285 base::RunLoop().RunUntilIdle();
286 EXPECT_FALSE(net::URLRequestTestJob::ProcessOnePendingMessage());
289 // Test that the delegate is not called if OnResponseStarted and destroying the
291 TEST_F(NavigationURLLoaderTest
, CancelResponseRace
) {
292 TestNavigationURLLoaderDelegate delegate
;
293 scoped_ptr
<NavigationURLLoader
> loader
=
294 MakeTestLoader(net::URLRequestTestJob::test_url_redirect_to_url_2(),
297 // Wait for the request to redirect.
298 delegate
.WaitForRequestRedirected();
300 // In the same event loop iteration, follow the redirect (allowing the
301 // response to go through) and destroy the loader.
302 loader
->FollowRedirect();
305 // Verify the URLRequestTestJob no longer has anything paused and that no
306 // response body was received.
307 base::RunLoop().RunUntilIdle();
308 EXPECT_FALSE(net::URLRequestTestJob::ProcessOnePendingMessage());
309 EXPECT_FALSE(delegate
.body());
312 // Tests that the loader may be canceled by context.
313 TEST_F(NavigationURLLoaderTest
, CancelByContext
) {
314 TestNavigationURLLoaderDelegate delegate
;
315 scoped_ptr
<NavigationURLLoader
> loader
=
316 MakeTestLoader(net::URLRequestTestJob::test_url_redirect_to_url_2(),
319 // Wait for the request to redirect.
320 delegate
.WaitForRequestRedirected();
322 // Cancel all requests.
323 host_
.CancelRequestsForContext(browser_context_
->GetResourceContext());
325 // Wait for the request to now be aborted.
326 delegate
.WaitForRequestFailed();
327 EXPECT_EQ(net::ERR_ABORTED
, delegate
.net_error());
330 // Tests that, if the request is blocked by the ResourceDispatcherHostDelegate,
331 // the caller is informed appropriately.
332 TEST_F(NavigationURLLoaderTest
, RequestBlocked
) {
333 RequestBlockingResourceDispatcherHostDelegate rdh_delegate
;
334 host_
.SetDelegate(&rdh_delegate
);
336 TestNavigationURLLoaderDelegate delegate
;
337 scoped_ptr
<NavigationURLLoader
> loader
=
338 MakeTestLoader(net::URLRequestTestJob::test_url_1(), &delegate
);
340 // Wait for the request to fail as expected.
341 delegate
.WaitForRequestFailed();
342 EXPECT_EQ(net::ERR_ABORTED
, delegate
.net_error());
344 host_
.SetDelegate(nullptr);
347 // Tests that ownership leaves the loader once the response is received.
348 TEST_F(NavigationURLLoaderTest
, LoaderDetached
) {
349 // Fake a top-level request to a URL whose body does not load immediately.
350 TestNavigationURLLoaderDelegate delegate
;
351 scoped_ptr
<NavigationURLLoader
> loader
=
352 MakeTestLoader(net::URLRequestTestJob::test_url_2(), &delegate
);
354 // Wait for the response to come back.
355 delegate
.WaitForResponseStarted();
357 // Check the response is correct.
358 EXPECT_EQ("text/html", delegate
.response()->head
.mime_type
);
359 EXPECT_EQ(200, delegate
.response()->head
.headers
->response_code());
361 // Destroy the loader.
363 base::RunLoop().RunUntilIdle();
365 // Check the body can still be fetched through the StreamHandle.
366 EXPECT_TRUE(net::URLRequestTestJob::ProcessOnePendingMessage());
367 EXPECT_EQ(net::URLRequestTestJob::test_data_2(),
368 FetchURL(delegate
.body()->GetURL()));
371 // Tests that the request is owned by the body StreamHandle.
372 TEST_F(NavigationURLLoaderTest
, OwnedByHandle
) {
373 // Fake a top-level request to a URL whose body does not load immediately.
374 TestNavigationURLLoaderDelegate delegate
;
375 scoped_ptr
<NavigationURLLoader
> loader
=
376 MakeTestLoader(net::URLRequestTestJob::test_url_2(), &delegate
);
378 // Wait for the response to come back.
379 delegate
.WaitForResponseStarted();
382 delegate
.ReleaseBody();
383 base::RunLoop().RunUntilIdle();
385 // Verify that URLRequestTestJob no longer has anything paused.
386 EXPECT_FALSE(net::URLRequestTestJob::ProcessOnePendingMessage());
389 } // namespace content