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/load_flags.h"
28 #include "net/base/net_errors.h"
29 #include "net/http/http_response_headers.h"
30 #include "net/url_request/redirect_info.h"
31 #include "net/url_request/url_request.h"
32 #include "net/url_request/url_request_context.h"
33 #include "net/url_request/url_request_job_factory_impl.h"
34 #include "net/url_request/url_request_test_job.h"
35 #include "net/url_request/url_request_test_util.h"
36 #include "testing/gtest/include/gtest/gtest.h"
42 class StreamProtocolHandler
43 : public net::URLRequestJobFactory::ProtocolHandler
{
45 StreamProtocolHandler(StreamRegistry
* registry
) : registry_(registry
) {}
47 // net::URLRequestJobFactory::ProtocolHandler implementation.
48 net::URLRequestJob
* MaybeCreateJob(
49 net::URLRequest
* request
,
50 net::NetworkDelegate
* network_delegate
) const override
{
51 scoped_refptr
<Stream
> stream
= registry_
->GetStream(request
->url());
53 return new StreamURLRequestJob(request
, network_delegate
, stream
);
58 StreamRegistry
* registry_
;
60 DISALLOW_COPY_AND_ASSIGN(StreamProtocolHandler
);
63 class TestNavigationURLLoaderDelegate
: public NavigationURLLoaderDelegate
{
65 TestNavigationURLLoaderDelegate()
66 : net_error_(0), on_request_handled_counter_(0) {}
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_
; }
75 int on_request_handled_counter() const { return on_request_handled_counter_
; }
77 void WaitForRequestRedirected() {
78 request_redirected_
.reset(new base::RunLoop
);
79 request_redirected_
->Run();
80 request_redirected_
.reset();
83 void WaitForResponseStarted() {
84 response_started_
.reset(new base::RunLoop
);
85 response_started_
->Run();
86 response_started_
.reset();
89 void WaitForRequestFailed() {
90 request_failed_
.reset(new base::RunLoop
);
91 request_failed_
->Run();
92 request_failed_
.reset();
99 // NavigationURLLoaderDelegate implementation.
100 void OnRequestRedirected(
101 const net::RedirectInfo
& redirect_info
,
102 const scoped_refptr
<ResourceResponse
>& response
) override
{
103 redirect_info_
= redirect_info
;
104 redirect_response_
= response
;
105 ASSERT_TRUE(request_redirected_
);
106 request_redirected_
->Quit();
109 void OnResponseStarted(const scoped_refptr
<ResourceResponse
>& response
,
110 scoped_ptr
<StreamHandle
> body
) override
{
111 response_
= response
;
113 ASSERT_TRUE(response_started_
);
114 response_started_
->Quit();
117 void OnRequestFailed(bool in_cache
, int net_error
) override
{
118 net_error_
= net_error
;
119 ASSERT_TRUE(request_failed_
);
120 request_failed_
->Quit();
123 void OnRequestStarted(base::TimeTicks timestamp
) override
{
124 ASSERT_FALSE(timestamp
.is_null());
125 ++on_request_handled_counter_
;
129 net::RedirectInfo redirect_info_
;
130 scoped_refptr
<ResourceResponse
> redirect_response_
;
131 scoped_refptr
<ResourceResponse
> response_
;
132 scoped_ptr
<StreamHandle
> body_
;
134 int on_request_handled_counter_
;
136 scoped_ptr
<base::RunLoop
> request_redirected_
;
137 scoped_ptr
<base::RunLoop
> response_started_
;
138 scoped_ptr
<base::RunLoop
> request_failed_
;
141 class RequestBlockingResourceDispatcherHostDelegate
142 : public ResourceDispatcherHostDelegate
{
144 // ResourceDispatcherHostDelegate implementation:
145 bool ShouldBeginRequest(const std::string
& method
,
147 ResourceType resource_type
,
148 ResourceContext
* resource_context
) override
{
155 class NavigationURLLoaderTest
: public testing::Test
{
157 NavigationURLLoaderTest()
158 : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP
),
159 browser_context_(new TestBrowserContext
) {
160 BrowserContext::EnsureResourceContextInitialized(browser_context_
.get());
161 base::RunLoop().RunUntilIdle();
162 net::URLRequestContext
* request_context
=
163 browser_context_
->GetResourceContext()->GetRequestContext();
164 // Attach URLRequestTestJob and make streams work.
165 job_factory_
.SetProtocolHandler(
166 "test", net::URLRequestTestJob::CreateProtocolHandler());
167 job_factory_
.SetProtocolHandler(
169 make_scoped_ptr(new StreamProtocolHandler(
170 StreamContext::GetFor(browser_context_
.get())->registry())));
171 request_context
->set_job_factory(&job_factory_
);
173 // NavigationURLLoader is only used for browser-side navigations.
174 base::CommandLine::ForCurrentProcess()->AppendSwitch(
175 switches::kEnableBrowserSideNavigation
);
178 scoped_ptr
<NavigationURLLoader
> MakeTestLoader(
180 NavigationURLLoaderDelegate
* delegate
) {
181 BeginNavigationParams
begin_params(
182 "GET", std::string(), net::LOAD_NORMAL
, false);
183 CommonNavigationParams common_params
;
184 common_params
.url
= url
;
185 scoped_ptr
<NavigationRequestInfo
> request_info(
186 new NavigationRequestInfo(common_params
, begin_params
, url
, true, false,
187 -1, scoped_refptr
<ResourceRequestBody
>()));
189 return NavigationURLLoader::Create(
190 browser_context_
.get(), 0, request_info
.Pass(), delegate
);
193 // Helper function for fetching the body of a URL to a string.
194 std::string
FetchURL(const GURL
& url
) {
195 net::TestDelegate delegate
;
196 net::URLRequestContext
* request_context
=
197 browser_context_
->GetResourceContext()->GetRequestContext();
198 scoped_ptr
<net::URLRequest
> request(request_context
->CreateRequest(
199 url
, net::DEFAULT_PRIORITY
, &delegate
));
201 base::RunLoop().Run();
203 EXPECT_TRUE(request
->status().is_success());
204 EXPECT_EQ(200, request
->response_headers()->response_code());
205 return delegate
.data_received();
209 TestBrowserThreadBundle thread_bundle_
;
210 net::URLRequestJobFactoryImpl job_factory_
;
211 scoped_ptr
<TestBrowserContext
> browser_context_
;
212 ResourceDispatcherHostImpl host_
;
215 // Tests that a basic request works.
216 TEST_F(NavigationURLLoaderTest
, Basic
) {
217 TestNavigationURLLoaderDelegate delegate
;
218 scoped_ptr
<NavigationURLLoader
> loader
=
219 MakeTestLoader(net::URLRequestTestJob::test_url_1(), &delegate
);
221 // Wait for the response to come back.
222 delegate
.WaitForResponseStarted();
224 // Check the response is correct.
225 EXPECT_EQ("text/html", delegate
.response()->head
.mime_type
);
226 EXPECT_EQ(200, delegate
.response()->head
.headers
->response_code());
228 // Check the body is correct.
229 EXPECT_EQ(net::URLRequestTestJob::test_data_1(),
230 FetchURL(delegate
.body()->GetURL()));
232 EXPECT_EQ(1, delegate
.on_request_handled_counter());
235 // Tests that request failures are propagated correctly.
236 TEST_F(NavigationURLLoaderTest
, RequestFailed
) {
237 TestNavigationURLLoaderDelegate delegate
;
238 scoped_ptr
<NavigationURLLoader
> loader
=
239 MakeTestLoader(GURL("bogus:bogus"), &delegate
);
241 // Wait for the request to fail as expected.
242 delegate
.WaitForRequestFailed();
243 EXPECT_EQ(net::ERR_UNKNOWN_URL_SCHEME
, delegate
.net_error());
244 EXPECT_EQ(1, delegate
.on_request_handled_counter());
247 // Test that redirects are sent to the delegate.
248 TEST_F(NavigationURLLoaderTest
, RequestRedirected
) {
249 // Fake a top-level request. Choose a URL which redirects so the request can
250 // be paused before the response comes in.
251 TestNavigationURLLoaderDelegate delegate
;
252 scoped_ptr
<NavigationURLLoader
> loader
=
253 MakeTestLoader(net::URLRequestTestJob::test_url_redirect_to_url_2(),
256 // Wait for the request to redirect.
257 delegate
.WaitForRequestRedirected();
258 EXPECT_EQ(net::URLRequestTestJob::test_url_2(),
259 delegate
.redirect_info().new_url
);
260 EXPECT_EQ("GET", delegate
.redirect_info().new_method
);
261 EXPECT_EQ(net::URLRequestTestJob::test_url_2(),
262 delegate
.redirect_info().new_first_party_for_cookies
);
263 EXPECT_EQ(302, delegate
.redirect_response()->head
.headers
->response_code());
264 EXPECT_EQ(1, delegate
.on_request_handled_counter());
266 // Wait for the response to complete.
267 loader
->FollowRedirect();
268 delegate
.WaitForResponseStarted();
270 // Check the response is correct.
271 EXPECT_EQ("text/html", delegate
.response()->head
.mime_type
);
272 EXPECT_EQ(200, delegate
.response()->head
.headers
->response_code());
274 // Release the body and check it is correct.
275 EXPECT_TRUE(net::URLRequestTestJob::ProcessOnePendingMessage());
276 EXPECT_EQ(net::URLRequestTestJob::test_data_2(),
277 FetchURL(delegate
.body()->GetURL()));
279 EXPECT_EQ(1, delegate
.on_request_handled_counter());
282 // Tests that the destroying the loader cancels the request.
283 TEST_F(NavigationURLLoaderTest
, CancelOnDestruct
) {
284 // Fake a top-level request. Choose a URL which redirects so the request can
285 // be paused before the response comes in.
286 TestNavigationURLLoaderDelegate delegate
;
287 scoped_ptr
<NavigationURLLoader
> loader
=
288 MakeTestLoader(net::URLRequestTestJob::test_url_redirect_to_url_2(),
291 // Wait for the request to redirect.
292 delegate
.WaitForRequestRedirected();
294 // Destroy the loader and verify that URLRequestTestJob no longer has anything
297 base::RunLoop().RunUntilIdle();
298 EXPECT_FALSE(net::URLRequestTestJob::ProcessOnePendingMessage());
301 // Test that the delegate is not called if OnResponseStarted and destroying the
303 TEST_F(NavigationURLLoaderTest
, CancelResponseRace
) {
304 TestNavigationURLLoaderDelegate delegate
;
305 scoped_ptr
<NavigationURLLoader
> loader
=
306 MakeTestLoader(net::URLRequestTestJob::test_url_redirect_to_url_2(),
309 // Wait for the request to redirect.
310 delegate
.WaitForRequestRedirected();
312 // In the same event loop iteration, follow the redirect (allowing the
313 // response to go through) and destroy the loader.
314 loader
->FollowRedirect();
317 // Verify the URLRequestTestJob no longer has anything paused and that no
318 // response body was received.
319 base::RunLoop().RunUntilIdle();
320 EXPECT_FALSE(net::URLRequestTestJob::ProcessOnePendingMessage());
321 EXPECT_FALSE(delegate
.body());
324 // Tests that the loader may be canceled by context.
325 TEST_F(NavigationURLLoaderTest
, CancelByContext
) {
326 TestNavigationURLLoaderDelegate delegate
;
327 scoped_ptr
<NavigationURLLoader
> loader
=
328 MakeTestLoader(net::URLRequestTestJob::test_url_redirect_to_url_2(),
331 // Wait for the request to redirect.
332 delegate
.WaitForRequestRedirected();
334 // Cancel all requests.
335 host_
.CancelRequestsForContext(browser_context_
->GetResourceContext());
337 // Wait for the request to now be aborted.
338 delegate
.WaitForRequestFailed();
339 EXPECT_EQ(net::ERR_ABORTED
, delegate
.net_error());
340 EXPECT_EQ(1, delegate
.on_request_handled_counter());
343 // Tests that, if the request is blocked by the ResourceDispatcherHostDelegate,
344 // the caller is informed appropriately.
345 TEST_F(NavigationURLLoaderTest
, RequestBlocked
) {
346 RequestBlockingResourceDispatcherHostDelegate rdh_delegate
;
347 host_
.SetDelegate(&rdh_delegate
);
349 TestNavigationURLLoaderDelegate delegate
;
350 scoped_ptr
<NavigationURLLoader
> loader
=
351 MakeTestLoader(net::URLRequestTestJob::test_url_1(), &delegate
);
353 // Wait for the request to fail as expected.
354 delegate
.WaitForRequestFailed();
355 EXPECT_EQ(net::ERR_ABORTED
, delegate
.net_error());
356 EXPECT_EQ(1, delegate
.on_request_handled_counter());
358 host_
.SetDelegate(nullptr);
361 // Tests that ownership leaves the loader once the response is received.
362 TEST_F(NavigationURLLoaderTest
, LoaderDetached
) {
363 // Fake a top-level request to a URL whose body does not load immediately.
364 TestNavigationURLLoaderDelegate delegate
;
365 scoped_ptr
<NavigationURLLoader
> loader
=
366 MakeTestLoader(net::URLRequestTestJob::test_url_2(), &delegate
);
368 // Wait for the response to come back.
369 delegate
.WaitForResponseStarted();
371 // Check the response is correct.
372 EXPECT_EQ("text/html", delegate
.response()->head
.mime_type
);
373 EXPECT_EQ(200, delegate
.response()->head
.headers
->response_code());
375 // Destroy the loader.
377 base::RunLoop().RunUntilIdle();
379 // Check the body can still be fetched through the StreamHandle.
380 EXPECT_TRUE(net::URLRequestTestJob::ProcessOnePendingMessage());
381 EXPECT_EQ(net::URLRequestTestJob::test_data_2(),
382 FetchURL(delegate
.body()->GetURL()));
385 // Tests that the request is owned by the body StreamHandle.
386 TEST_F(NavigationURLLoaderTest
, OwnedByHandle
) {
387 // Fake a top-level request to a URL whose body does not load immediately.
388 TestNavigationURLLoaderDelegate delegate
;
389 scoped_ptr
<NavigationURLLoader
> loader
=
390 MakeTestLoader(net::URLRequestTestJob::test_url_2(), &delegate
);
392 // Wait for the response to come back.
393 delegate
.WaitForResponseStarted();
396 delegate
.ReleaseBody();
397 base::RunLoop().RunUntilIdle();
399 // Verify that URLRequestTestJob no longer has anything paused.
400 EXPECT_FALSE(net::URLRequestTestJob::ProcessOnePendingMessage());
403 } // namespace content