blob: c636cda46a6b4b55d0ca89c5a29463774d39c76c [file] [log] [blame]
davidben6b77cd72014-10-29 21:13:451// 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.
4
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"
clamy0ab288e2015-02-05 17:39:1427#include "net/base/load_flags.h"
davidben6b77cd72014-10-29 21:13:4528#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"
37
38namespace content {
39
40namespace {
41
42class StreamProtocolHandler
43 : public net::URLRequestJobFactory::ProtocolHandler {
44 public:
45 StreamProtocolHandler(StreamRegistry* registry) : registry_(registry) {}
46
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());
52 if (stream.get())
53 return new StreamURLRequestJob(request, network_delegate, stream);
54 return nullptr;
55 }
56
57 private:
58 StreamRegistry* registry_;
59
60 DISALLOW_COPY_AND_ASSIGN(StreamProtocolHandler);
61};
62
63class TestNavigationURLLoaderDelegate : public NavigationURLLoaderDelegate {
64 public:
65 TestNavigationURLLoaderDelegate()
carlosk947ebfb62015-02-04 11:53:5966 : net_error_(0), on_request_handled_counter_(0) {}
davidben6b77cd72014-10-29 21:13:4567
68 const net::RedirectInfo& redirect_info() const { return redirect_info_; }
69 ResourceResponse* redirect_response() const {
70 return redirect_response_.get();
71 }
72 ResourceResponse* response() const { return response_.get(); }
73 StreamHandle* body() const { return body_.get(); }
74 int net_error() const { return net_error_; }
carlosk947ebfb62015-02-04 11:53:5975 int on_request_handled_counter() const { return on_request_handled_counter_; }
davidben6b77cd72014-10-29 21:13:4576
77 void WaitForRequestRedirected() {
78 request_redirected_.reset(new base::RunLoop);
79 request_redirected_->Run();
80 request_redirected_.reset();
81 }
82
83 void WaitForResponseStarted() {
84 response_started_.reset(new base::RunLoop);
85 response_started_->Run();
86 response_started_.reset();
87 }
88
89 void WaitForRequestFailed() {
90 request_failed_.reset(new base::RunLoop);
91 request_failed_->Run();
92 request_failed_.reset();
93 }
94
95 void ReleaseBody() {
96 body_.reset();
97 }
98
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();
107 }
108
109 void OnResponseStarted(const scoped_refptr<ResourceResponse>& response,
110 scoped_ptr<StreamHandle> body) override {
111 response_ = response;
112 body_ = body.Pass();
113 ASSERT_TRUE(response_started_);
114 response_started_->Quit();
115 }
116
clamy62b271d2015-04-16 11:54:57117 void OnRequestFailed(bool in_cache, int net_error) override {
davidben6b77cd72014-10-29 21:13:45118 net_error_ = net_error;
119 ASSERT_TRUE(request_failed_);
120 request_failed_->Quit();
121 }
122
carlosk947ebfb62015-02-04 11:53:59123 void OnRequestStarted(base::TimeTicks timestamp) override {
124 ASSERT_FALSE(timestamp.is_null());
125 ++on_request_handled_counter_;
126 }
127
davidben6b77cd72014-10-29 21:13:45128 private:
129 net::RedirectInfo redirect_info_;
130 scoped_refptr<ResourceResponse> redirect_response_;
131 scoped_refptr<ResourceResponse> response_;
132 scoped_ptr<StreamHandle> body_;
133 int net_error_;
carlosk947ebfb62015-02-04 11:53:59134 int on_request_handled_counter_;
davidben6b77cd72014-10-29 21:13:45135
136 scoped_ptr<base::RunLoop> request_redirected_;
137 scoped_ptr<base::RunLoop> response_started_;
138 scoped_ptr<base::RunLoop> request_failed_;
139};
140
141class RequestBlockingResourceDispatcherHostDelegate
142 : public ResourceDispatcherHostDelegate {
143 public:
144 // ResourceDispatcherHostDelegate implementation:
145 bool ShouldBeginRequest(const std::string& method,
146 const GURL& url,
147 ResourceType resource_type,
148 ResourceContext* resource_context) override {
149 return false;
150 }
151};
152
153} // namespace
154
155class NavigationURLLoaderTest : public testing::Test {
156 public:
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(
168 "blob", new StreamProtocolHandler(
169 StreamContext::GetFor(browser_context_.get())->registry()));
170 request_context->set_job_factory(&job_factory_);
171
172 // NavigationURLLoader is only used for browser-side navigations.
avi83883c82014-12-23 00:08:49173 base::CommandLine::ForCurrentProcess()->AppendSwitch(
davidben6b77cd72014-10-29 21:13:45174 switches::kEnableBrowserSideNavigation);
175 }
176
177 scoped_ptr<NavigationURLLoader> MakeTestLoader(
178 const GURL& url,
179 NavigationURLLoaderDelegate* delegate) {
clamy0ab288e2015-02-05 17:39:14180 BeginNavigationParams begin_params(
181 "GET", std::string(), net::LOAD_NORMAL, false);
davidben6b77cd72014-10-29 21:13:45182 CommonNavigationParams common_params;
davidben6b77cd72014-10-29 21:13:45183 common_params.url = url;
184 scoped_ptr<NavigationRequestInfo> request_info(
clamy0ab288e2015-02-05 17:39:14185 new NavigationRequestInfo(common_params, begin_params, url, true, false,
186 scoped_refptr<ResourceRequestBody>()));
davidben6b77cd72014-10-29 21:13:45187
188 return NavigationURLLoader::Create(
clamy0ab288e2015-02-05 17:39:14189 browser_context_.get(), 0, request_info.Pass(), delegate);
davidben6b77cd72014-10-29 21:13:45190 }
191
192 // Helper function for fetching the body of a URL to a string.
193 std::string FetchURL(const GURL& url) {
194 net::TestDelegate delegate;
195 net::URLRequestContext* request_context =
196 browser_context_->GetResourceContext()->GetRequestContext();
197 scoped_ptr<net::URLRequest> request(request_context->CreateRequest(
davidben151423e2015-03-23 18:48:36198 url, net::DEFAULT_PRIORITY, &delegate));
davidben6b77cd72014-10-29 21:13:45199 request->Start();
200 base::RunLoop().Run();
201
202 EXPECT_TRUE(request->status().is_success());
203 EXPECT_EQ(200, request->response_headers()->response_code());
204 return delegate.data_received();
205 }
206
207 protected:
208 TestBrowserThreadBundle thread_bundle_;
209 net::URLRequestJobFactoryImpl job_factory_;
210 scoped_ptr<TestBrowserContext> browser_context_;
211 ResourceDispatcherHostImpl host_;
212};
213
214// Tests that a basic request works.
215TEST_F(NavigationURLLoaderTest, Basic) {
216 TestNavigationURLLoaderDelegate delegate;
217 scoped_ptr<NavigationURLLoader> loader =
218 MakeTestLoader(net::URLRequestTestJob::test_url_1(), &delegate);
219
220 // Wait for the response to come back.
221 delegate.WaitForResponseStarted();
222
223 // Check the response is correct.
224 EXPECT_EQ("text/html", delegate.response()->head.mime_type);
225 EXPECT_EQ(200, delegate.response()->head.headers->response_code());
226
227 // Check the body is correct.
228 EXPECT_EQ(net::URLRequestTestJob::test_data_1(),
229 FetchURL(delegate.body()->GetURL()));
carlosk947ebfb62015-02-04 11:53:59230
231 EXPECT_EQ(1, delegate.on_request_handled_counter());
davidben6b77cd72014-10-29 21:13:45232}
233
234// Tests that request failures are propagated correctly.
235TEST_F(NavigationURLLoaderTest, RequestFailed) {
236 TestNavigationURLLoaderDelegate delegate;
237 scoped_ptr<NavigationURLLoader> loader =
238 MakeTestLoader(GURL("bogus:bogus"), &delegate);
239
240 // Wait for the request to fail as expected.
241 delegate.WaitForRequestFailed();
242 EXPECT_EQ(net::ERR_UNKNOWN_URL_SCHEME, delegate.net_error());
carlosk947ebfb62015-02-04 11:53:59243 EXPECT_EQ(1, delegate.on_request_handled_counter());
davidben6b77cd72014-10-29 21:13:45244}
245
246// Test that redirects are sent to the delegate.
247TEST_F(NavigationURLLoaderTest, RequestRedirected) {
248 // Fake a top-level request. Choose a URL which redirects so the request can
249 // be paused before the response comes in.
250 TestNavigationURLLoaderDelegate delegate;
251 scoped_ptr<NavigationURLLoader> loader =
252 MakeTestLoader(net::URLRequestTestJob::test_url_redirect_to_url_2(),
253 &delegate);
254
255 // Wait for the request to redirect.
256 delegate.WaitForRequestRedirected();
257 EXPECT_EQ(net::URLRequestTestJob::test_url_2(),
258 delegate.redirect_info().new_url);
259 EXPECT_EQ("GET", delegate.redirect_info().new_method);
260 EXPECT_EQ(net::URLRequestTestJob::test_url_2(),
261 delegate.redirect_info().new_first_party_for_cookies);
262 EXPECT_EQ(302, delegate.redirect_response()->head.headers->response_code());
carlosk947ebfb62015-02-04 11:53:59263 EXPECT_EQ(1, delegate.on_request_handled_counter());
davidben6b77cd72014-10-29 21:13:45264
265 // Wait for the response to complete.
266 loader->FollowRedirect();
267 delegate.WaitForResponseStarted();
268
269 // Check the response is correct.
270 EXPECT_EQ("text/html", delegate.response()->head.mime_type);
271 EXPECT_EQ(200, delegate.response()->head.headers->response_code());
272
273 // Release the body and check it is correct.
274 EXPECT_TRUE(net::URLRequestTestJob::ProcessOnePendingMessage());
275 EXPECT_EQ(net::URLRequestTestJob::test_data_2(),
276 FetchURL(delegate.body()->GetURL()));
carlosk947ebfb62015-02-04 11:53:59277
278 EXPECT_EQ(1, delegate.on_request_handled_counter());
davidben6b77cd72014-10-29 21:13:45279}
280
281// Tests that the destroying the loader cancels the request.
282TEST_F(NavigationURLLoaderTest, CancelOnDestruct) {
283 // Fake a top-level request. Choose a URL which redirects so the request can
284 // be paused before the response comes in.
285 TestNavigationURLLoaderDelegate delegate;
286 scoped_ptr<NavigationURLLoader> loader =
287 MakeTestLoader(net::URLRequestTestJob::test_url_redirect_to_url_2(),
288 &delegate);
289
290 // Wait for the request to redirect.
291 delegate.WaitForRequestRedirected();
292
293 // Destroy the loader and verify that URLRequestTestJob no longer has anything
294 // paused.
295 loader.reset();
296 base::RunLoop().RunUntilIdle();
297 EXPECT_FALSE(net::URLRequestTestJob::ProcessOnePendingMessage());
298}
299
300// Test that the delegate is not called if OnResponseStarted and destroying the
301// loader race.
302TEST_F(NavigationURLLoaderTest, CancelResponseRace) {
303 TestNavigationURLLoaderDelegate delegate;
304 scoped_ptr<NavigationURLLoader> loader =
305 MakeTestLoader(net::URLRequestTestJob::test_url_redirect_to_url_2(),
306 &delegate);
307
308 // Wait for the request to redirect.
309 delegate.WaitForRequestRedirected();
310
311 // In the same event loop iteration, follow the redirect (allowing the
312 // response to go through) and destroy the loader.
313 loader->FollowRedirect();
314 loader.reset();
315
316 // Verify the URLRequestTestJob no longer has anything paused and that no
317 // response body was received.
318 base::RunLoop().RunUntilIdle();
319 EXPECT_FALSE(net::URLRequestTestJob::ProcessOnePendingMessage());
320 EXPECT_FALSE(delegate.body());
321}
322
323// Tests that the loader may be canceled by context.
324TEST_F(NavigationURLLoaderTest, CancelByContext) {
325 TestNavigationURLLoaderDelegate delegate;
326 scoped_ptr<NavigationURLLoader> loader =
327 MakeTestLoader(net::URLRequestTestJob::test_url_redirect_to_url_2(),
328 &delegate);
329
330 // Wait for the request to redirect.
331 delegate.WaitForRequestRedirected();
332
333 // Cancel all requests.
334 host_.CancelRequestsForContext(browser_context_->GetResourceContext());
335
336 // Wait for the request to now be aborted.
337 delegate.WaitForRequestFailed();
338 EXPECT_EQ(net::ERR_ABORTED, delegate.net_error());
carlosk947ebfb62015-02-04 11:53:59339 EXPECT_EQ(1, delegate.on_request_handled_counter());
davidben6b77cd72014-10-29 21:13:45340}
341
342// Tests that, if the request is blocked by the ResourceDispatcherHostDelegate,
343// the caller is informed appropriately.
344TEST_F(NavigationURLLoaderTest, RequestBlocked) {
345 RequestBlockingResourceDispatcherHostDelegate rdh_delegate;
346 host_.SetDelegate(&rdh_delegate);
347
348 TestNavigationURLLoaderDelegate delegate;
349 scoped_ptr<NavigationURLLoader> loader =
350 MakeTestLoader(net::URLRequestTestJob::test_url_1(), &delegate);
351
352 // Wait for the request to fail as expected.
353 delegate.WaitForRequestFailed();
354 EXPECT_EQ(net::ERR_ABORTED, delegate.net_error());
carlosk947ebfb62015-02-04 11:53:59355 EXPECT_EQ(1, delegate.on_request_handled_counter());
davidben6b77cd72014-10-29 21:13:45356
357 host_.SetDelegate(nullptr);
358}
359
360// Tests that ownership leaves the loader once the response is received.
361TEST_F(NavigationURLLoaderTest, LoaderDetached) {
362 // Fake a top-level request to a URL whose body does not load immediately.
363 TestNavigationURLLoaderDelegate delegate;
364 scoped_ptr<NavigationURLLoader> loader =
365 MakeTestLoader(net::URLRequestTestJob::test_url_2(), &delegate);
366
367 // Wait for the response to come back.
368 delegate.WaitForResponseStarted();
369
370 // Check the response is correct.
371 EXPECT_EQ("text/html", delegate.response()->head.mime_type);
372 EXPECT_EQ(200, delegate.response()->head.headers->response_code());
373
374 // Destroy the loader.
375 loader.reset();
376 base::RunLoop().RunUntilIdle();
377
378 // Check the body can still be fetched through the StreamHandle.
379 EXPECT_TRUE(net::URLRequestTestJob::ProcessOnePendingMessage());
380 EXPECT_EQ(net::URLRequestTestJob::test_data_2(),
381 FetchURL(delegate.body()->GetURL()));
382}
383
384// Tests that the request is owned by the body StreamHandle.
385TEST_F(NavigationURLLoaderTest, OwnedByHandle) {
386 // Fake a top-level request to a URL whose body does not load immediately.
387 TestNavigationURLLoaderDelegate delegate;
388 scoped_ptr<NavigationURLLoader> loader =
389 MakeTestLoader(net::URLRequestTestJob::test_url_2(), &delegate);
390
391 // Wait for the response to come back.
392 delegate.WaitForResponseStarted();
393
394 // Release the body.
395 delegate.ReleaseBody();
396 base::RunLoop().RunUntilIdle();
397
398 // Verify that URLRequestTestJob no longer has anything paused.
399 EXPECT_FALSE(net::URLRequestTestJob::ProcessOnePendingMessage());
400}
401
402} // namespace content