OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/renderer_host/chrome_resource_dispatcher_host_delegate.
h" | 5 #include "chrome/browser/renderer_host/chrome_resource_dispatcher_host_delegate.
h" |
6 | 6 |
7 #include <stddef.h> | 7 #include <stddef.h> |
8 | 8 |
| 9 #include <map> |
9 #include <memory> | 10 #include <memory> |
10 #include <utility> | 11 #include <utility> |
| 12 #include <vector> |
11 | 13 |
| 14 #include "base/bind_helpers.h" |
12 #include "base/command_line.h" | 15 #include "base/command_line.h" |
13 #include "base/macros.h" | 16 #include "base/macros.h" |
| 17 #include "base/path_service.h" |
| 18 #include "base/stl_util.h" |
14 #include "base/strings/string_util.h" | 19 #include "base/strings/string_util.h" |
| 20 #include "base/test/scoped_command_line.h" |
15 #include "chrome/browser/browser_process.h" | 21 #include "chrome/browser/browser_process.h" |
16 #include "chrome/browser/policy/cloud/policy_header_service_factory.h" | 22 #include "chrome/browser/policy/cloud/policy_header_service_factory.h" |
17 #include "chrome/browser/profiles/profile.h" | 23 #include "chrome/browser/profiles/profile.h" |
18 #include "chrome/browser/renderer_host/chrome_navigation_data.h" | 24 #include "chrome/browser/renderer_host/chrome_navigation_data.h" |
19 #include "chrome/browser/ui/browser.h" | 25 #include "chrome/browser/ui/browser.h" |
20 #include "chrome/browser/ui/tabs/tab_strip_model.h" | 26 #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| 27 #include "chrome/common/chrome_paths.h" |
21 #include "chrome/test/base/in_process_browser_test.h" | 28 #include "chrome/test/base/in_process_browser_test.h" |
22 #include "chrome/test/base/ui_test_utils.h" | 29 #include "chrome/test/base/ui_test_utils.h" |
23 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_data
.h" | 30 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_data
.h" |
24 #include "components/policy/core/common/cloud/cloud_policy_constants.h" | 31 #include "components/policy/core/common/cloud/cloud_policy_constants.h" |
25 #include "components/policy/core/common/cloud/policy_header_io_helper.h" | 32 #include "components/policy/core/common/cloud/policy_header_io_helper.h" |
26 #include "components/policy/core/common/cloud/policy_header_service.h" | 33 #include "components/policy/core/common/cloud/policy_header_service.h" |
27 #include "components/policy/core/common/policy_switches.h" | 34 #include "components/policy/core/common/policy_switches.h" |
| 35 #include "components/prefs/pref_service.h" |
| 36 #include "components/signin/core/browser/signin_header_helper.h" |
| 37 #include "components/signin/core/common/signin_pref_names.h" |
| 38 #include "components/signin/core/common/signin_switches.h" |
28 #include "content/public/browser/navigation_data.h" | 39 #include "content/public/browser/navigation_data.h" |
29 #include "content/public/browser/navigation_handle.h" | 40 #include "content/public/browser/navigation_handle.h" |
30 #include "content/public/browser/resource_dispatcher_host.h" | 41 #include "content/public/browser/resource_dispatcher_host.h" |
| 42 #include "content/public/browser/resource_dispatcher_host_delegate.h" |
31 #include "content/public/browser/web_contents_observer.h" | 43 #include "content/public/browser/web_contents_observer.h" |
32 #include "content/public/test/browser_test_utils.h" | 44 #include "content/public/test/browser_test_utils.h" |
33 #include "net/http/http_request_headers.h" | 45 #include "net/http/http_request_headers.h" |
34 #include "net/test/embedded_test_server/embedded_test_server.h" | 46 #include "net/test/embedded_test_server/embedded_test_server.h" |
35 #include "net/test/embedded_test_server/http_request.h" | 47 #include "net/test/embedded_test_server/http_request.h" |
36 #include "net/test/embedded_test_server/http_response.h" | 48 #include "net/test/embedded_test_server/http_response.h" |
| 49 #include "net/test/url_request/url_request_mock_http_job.h" |
37 #include "net/url_request/url_request.h" | 50 #include "net/url_request/url_request.h" |
| 51 #include "net/url_request/url_request_filter.h" |
| 52 #include "testing/gmock/include/gmock/gmock-matchers.h" |
38 | 53 |
39 using content::ResourceType; | 54 using content::ResourceType; |
| 55 using testing::HasSubstr; |
| 56 using testing::Not; |
40 | 57 |
41 namespace { | 58 namespace { |
42 static const char kTestPolicyHeader[] = "test_header"; | 59 static const char kTestPolicyHeader[] = "test_header"; |
43 static const char kServerRedirectUrl[] = "/server-redirect"; | 60 static const char kServerRedirectUrl[] = "/server-redirect"; |
44 | 61 |
45 std::unique_ptr<net::test_server::HttpResponse> HandleTestRequest( | 62 std::unique_ptr<net::test_server::HttpResponse> HandleTestRequest( |
46 const net::test_server::HttpRequest& request) { | 63 const net::test_server::HttpRequest& request) { |
47 if (base::StartsWith(request.relative_url, kServerRedirectUrl, | 64 if (base::StartsWith(request.relative_url, kServerRedirectUrl, |
48 base::CompareCase::SENSITIVE)) { | 65 base::CompareCase::SENSITIVE)) { |
49 // Extract the target URL and redirect there. | 66 // Extract the target URL and redirect there. |
(...skipping 194 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
244 ui_test_utils::NavigateToURL( | 261 ui_test_utils::NavigateToURL( |
245 browser(), embedded_test_server()->GetURL("/google/google.html")); | 262 browser(), embedded_test_server()->GetURL("/google/google.html")); |
246 } | 263 } |
247 SetShouldAddDataReductionProxyData(true); | 264 SetShouldAddDataReductionProxyData(true); |
248 { | 265 { |
249 DidFinishNavigationObserver nav_observer( | 266 DidFinishNavigationObserver nav_observer( |
250 browser()->tab_strip_model()->GetActiveWebContents(), true); | 267 browser()->tab_strip_model()->GetActiveWebContents(), true); |
251 ui_test_utils::NavigateToURL(browser(), embedded_test_server()->base_url()); | 268 ui_test_utils::NavigateToURL(browser(), embedded_test_server()->base_url()); |
252 } | 269 } |
253 } | 270 } |
| 271 |
| 272 namespace { |
| 273 |
| 274 // A URLRequestMockHTTPJob to that reports HTTP request headers of outgoing |
| 275 // requests. |
| 276 class MirrorMockURLRequestJob : public net::URLRequestMockHTTPJob { |
| 277 public: |
| 278 // Callback function on the UI thread to report a (URL, request headers) |
| 279 // pair. Indicating that the |request headers| will be sent for the |URL|. |
| 280 using ReportResponseHeadersOnUI = |
| 281 base::Callback<void(const std::string&, const std::string&)>; |
| 282 |
| 283 MirrorMockURLRequestJob(net::URLRequest* request, |
| 284 net::NetworkDelegate* network_delegate, |
| 285 const base::FilePath& file_path, |
| 286 const scoped_refptr<base::TaskRunner>& task_runner, |
| 287 ReportResponseHeadersOnUI report_on_ui) |
| 288 : net::URLRequestMockHTTPJob(request, |
| 289 network_delegate, |
| 290 file_path, |
| 291 task_runner), |
| 292 report_on_ui_(report_on_ui) {} |
| 293 |
| 294 void Start() override { |
| 295 // Report the observed request headers on the UI thread. |
| 296 content::BrowserThread::PostTask( |
| 297 content::BrowserThread::UI, FROM_HERE, |
| 298 base::Bind(report_on_ui_, request_->url().spec(), |
| 299 request_->extra_request_headers().ToString())); |
| 300 |
| 301 URLRequestMockHTTPJob::Start(); |
| 302 } |
| 303 |
| 304 protected: |
| 305 const ReportResponseHeadersOnUI report_on_ui_; |
| 306 |
| 307 private: |
| 308 DISALLOW_COPY_AND_ASSIGN(MirrorMockURLRequestJob); |
| 309 }; |
| 310 |
| 311 // A URLRequestInterceptor to inject MirrorMockURLRequestJobs. |
| 312 class MirrorMockJobInterceptor : public net::URLRequestInterceptor { |
| 313 public: |
| 314 using ReportResponseHeadersOnUI = |
| 315 MirrorMockURLRequestJob::ReportResponseHeadersOnUI; |
| 316 |
| 317 MirrorMockJobInterceptor(const base::FilePath& root_http, |
| 318 ReportResponseHeadersOnUI report_on_ui) |
| 319 : root_http_(root_http), report_on_ui_(report_on_ui) {} |
| 320 ~MirrorMockJobInterceptor() override = default; |
| 321 |
| 322 // URLRequestInterceptor implementation |
| 323 net::URLRequestJob* MaybeInterceptRequest( |
| 324 net::URLRequest* request, |
| 325 net::NetworkDelegate* network_delegate) const override { |
| 326 return new MirrorMockURLRequestJob( |
| 327 request, network_delegate, root_http_, |
| 328 content::BrowserThread::GetBlockingPool() |
| 329 ->GetTaskRunnerWithShutdownBehavior( |
| 330 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN), |
| 331 report_on_ui_); |
| 332 } |
| 333 |
| 334 static void Register(const GURL& url, |
| 335 const base::FilePath& root_http, |
| 336 ReportResponseHeadersOnUI report_on_ui) { |
| 337 EXPECT_TRUE( |
| 338 content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); |
| 339 base::FilePath file_path(root_http); |
| 340 file_path = |
| 341 file_path.AppendASCII(url.scheme() + "." + url.host() + ".html"); |
| 342 net::URLRequestFilter::GetInstance()->AddHostnameInterceptor( |
| 343 url.scheme(), url.host(), base::WrapUnique(new MirrorMockJobInterceptor( |
| 344 file_path, report_on_ui))); |
| 345 } |
| 346 |
| 347 static void Unregister(const GURL& url) { |
| 348 EXPECT_TRUE( |
| 349 content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); |
| 350 net::URLRequestFilter::GetInstance()->RemoveHostnameHandler(url.scheme(), |
| 351 url.host()); |
| 352 } |
| 353 |
| 354 private: |
| 355 const base::FilePath root_http_; |
| 356 ReportResponseHeadersOnUI report_on_ui_; |
| 357 |
| 358 DISALLOW_COPY_AND_ASSIGN(MirrorMockJobInterceptor); |
| 359 }; |
| 360 |
| 361 // Used in MirrorMockURLRequestJob to store HTTP request header for all received |
| 362 // URLs in the given map. |
| 363 void ReportRequestHeaders(std::map<std::string, std::string>* request_headers, |
| 364 const std::string& url, |
| 365 const std::string& headers) { |
| 366 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 367 // Ensure that a previous value is not overwritten. |
| 368 EXPECT_FALSE(base::ContainsKey(*request_headers, url)); |
| 369 (*request_headers)[url] = headers; |
| 370 } |
| 371 |
| 372 } // namespace |
| 373 |
| 374 // A delegate to insert a user generated X-Chrome-Connected header |
| 375 // to a specifict URL. |
| 376 class HeaderTestDispatcherHostDelegate |
| 377 : public ChromeResourceDispatcherHostDelegate { |
| 378 public: |
| 379 explicit HeaderTestDispatcherHostDelegate(const GURL& watch_url) |
| 380 : watch_url_(watch_url) {} |
| 381 ~HeaderTestDispatcherHostDelegate() override {} |
| 382 |
| 383 void RequestBeginning( |
| 384 net::URLRequest* request, |
| 385 content::ResourceContext* resource_context, |
| 386 content::AppCacheService* appcache_service, |
| 387 ResourceType resource_type, |
| 388 ScopedVector<content::ResourceThrottle>* throttles) override { |
| 389 ChromeResourceDispatcherHostDelegate::RequestBeginning( |
| 390 request, resource_context, appcache_service, resource_type, throttles); |
| 391 if (request->url() == watch_url_) { |
| 392 request->SetExtraRequestHeaderByName(signin::kChromeConnectedHeader, |
| 393 "User Data", false); |
| 394 } |
| 395 } |
| 396 |
| 397 private: |
| 398 const GURL watch_url_; |
| 399 |
| 400 DISALLOW_COPY_AND_ASSIGN(HeaderTestDispatcherHostDelegate); |
| 401 }; |
| 402 |
| 403 // Sets a new one on IO thread |
| 404 void SetDelegateOnIO(content::ResourceDispatcherHostDelegate* new_delegate) { |
| 405 content::ResourceDispatcherHost::Get()->SetDelegate(new_delegate); |
| 406 } |
| 407 |
| 408 // Verify the following items: |
| 409 // 1- X-Chrome-Connected is appended on Google domains if account |
| 410 // consistency is enabled and access is secure. |
| 411 // 2- The header is stripped in case a request is redirected from a Gooogle |
| 412 // domain to non-google domain. |
| 413 // 3- The header is NOT stripped in case it is added directly by the page |
| 414 // and not because it was on a secure Google domain. |
| 415 // This is a regression test for crbug.com/588492. |
| 416 IN_PROC_BROWSER_TEST_F(ChromeResourceDispatcherHostDelegateBrowserTest, |
| 417 MirrorRequestHeader) { |
| 418 // Enable account consistency so that mirror actually sets the |
| 419 // X-Chrome-Connected header in requests to Google. |
| 420 base::CommandLine::ForCurrentProcess()->AppendSwitch( |
| 421 switches::kEnableAccountConsistency); |
| 422 |
| 423 browser()->profile()->GetPrefs()->SetString(prefs::kGoogleServicesUsername, |
| 424 "user@gmail.com"); |
| 425 browser()->profile()->GetPrefs()->SetString( |
| 426 prefs::kGoogleServicesUserAccountId, "account_id"); |
| 427 |
| 428 base::FilePath root_http; |
| 429 PathService::Get(chrome::DIR_TEST_DATA, &root_http); |
| 430 root_http = root_http.AppendASCII("mirror_request_header"); |
| 431 |
| 432 struct TestCase { |
| 433 GURL original_url, redirected_to_url; |
| 434 bool inject_header, original_url_expects_header, |
| 435 redirected_to_url_expects_header; |
| 436 } all_tests[] = { |
| 437 // Neither should have the header. |
| 438 {GURL("http://www.google.com"), GURL("http://www.redirected.com"), false, |
| 439 false, false}, |
| 440 // First one should have the header, but not transfered to second one. |
| 441 {GURL("https://www.google.com"), GURL("https://www.redirected.com"), |
| 442 false, true, false}, |
| 443 // First one adds the header and transfers it to the second. |
| 444 {GURL("http://www.header_adder.com"), |
| 445 GURL("http://www.redirected_from_header_adder.com"), true, true, true}}; |
| 446 |
| 447 for (const auto& test_case : all_tests) { |
| 448 SCOPED_TRACE(test_case.original_url); |
| 449 |
| 450 // The HTTP Request headers (i.e. the ones that are managed on the |
| 451 // URLRequest layer, not on the URLRequestJob layer) sent from the browser |
| 452 // are collected in this map. The keys are URLs the values the request |
| 453 // headers. |
| 454 std::map<std::string, std::string> request_headers; |
| 455 MirrorMockURLRequestJob::ReportResponseHeadersOnUI report_request_headers = |
| 456 base::Bind(&ReportRequestHeaders, &request_headers); |
| 457 |
| 458 // If test case requires adding header for the first url, |
| 459 // change the delegate. |
| 460 std::unique_ptr<HeaderTestDispatcherHostDelegate> dispatcher_host_delegate; |
| 461 if (test_case.inject_header) { |
| 462 dispatcher_host_delegate = |
| 463 base::MakeUnique<HeaderTestDispatcherHostDelegate>( |
| 464 test_case.original_url); |
| 465 content::BrowserThread::PostTask( |
| 466 content::BrowserThread::IO, FROM_HERE, |
| 467 base::Bind(&SetDelegateOnIO, dispatcher_host_delegate.get())); |
| 468 } |
| 469 |
| 470 // Set up mockup interceptors. |
| 471 content::BrowserThread::PostTask( |
| 472 content::BrowserThread::IO, FROM_HERE, |
| 473 base::Bind(&MirrorMockJobInterceptor::Register, test_case.original_url, |
| 474 root_http, report_request_headers)); |
| 475 content::BrowserThread::PostTask( |
| 476 content::BrowserThread::IO, FROM_HERE, |
| 477 base::Bind(&MirrorMockJobInterceptor::Register, |
| 478 test_case.redirected_to_url, root_http, |
| 479 report_request_headers)); |
| 480 |
| 481 // Navigate to first url. |
| 482 ui_test_utils::NavigateToURL(browser(), test_case.original_url); |
| 483 |
| 484 // Cleanup before verifying the observed headers. |
| 485 content::BrowserThread::PostTask( |
| 486 content::BrowserThread::IO, FROM_HERE, |
| 487 base::Bind(&MirrorMockJobInterceptor::Unregister, |
| 488 test_case.original_url)); |
| 489 content::BrowserThread::PostTask( |
| 490 content::BrowserThread::IO, FROM_HERE, |
| 491 base::Bind(&MirrorMockJobInterceptor::Unregister, |
| 492 test_case.redirected_to_url)); |
| 493 |
| 494 // If delegate is changed, remove it. |
| 495 if (test_case.inject_header) { |
| 496 base::RunLoop run_loop; |
| 497 content::BrowserThread::PostTaskAndReply( |
| 498 content::BrowserThread::IO, FROM_HERE, |
| 499 base::Bind(&SetDelegateOnIO, nullptr), run_loop.QuitClosure()); |
| 500 run_loop.Run(); |
| 501 } |
| 502 |
| 503 // Ensure that the response headers have been reported to the UI thread |
| 504 // and unregistration has been processed on the IO thread. |
| 505 base::RunLoop run_loop; |
| 506 content::BrowserThread::PostTaskAndReply(content::BrowserThread::IO, |
| 507 FROM_HERE, |
| 508 // Flush IO thread... |
| 509 base::Bind(&base::DoNothing), |
| 510 // ... and UI thread. |
| 511 run_loop.QuitClosure()); |
| 512 run_loop.Run(); |
| 513 |
| 514 // Check if header exists and X-Chrome-Connected is correctly provided. |
| 515 ASSERT_EQ(1u, request_headers.count(test_case.original_url.spec())); |
| 516 if (test_case.original_url_expects_header) { |
| 517 EXPECT_THAT(request_headers[test_case.original_url.spec()], |
| 518 HasSubstr(signin::kChromeConnectedHeader)); |
| 519 } else { |
| 520 EXPECT_THAT(request_headers[test_case.original_url.spec()], |
| 521 Not(HasSubstr(signin::kChromeConnectedHeader))); |
| 522 } |
| 523 |
| 524 ASSERT_EQ(1u, request_headers.count(test_case.redirected_to_url.spec())); |
| 525 if (test_case.redirected_to_url_expects_header) { |
| 526 EXPECT_THAT(request_headers[test_case.redirected_to_url.spec()], |
| 527 HasSubstr(signin::kChromeConnectedHeader)); |
| 528 } else { |
| 529 EXPECT_THAT(request_headers[test_case.redirected_to_url.spec()], |
| 530 Not(HasSubstr(signin::kChromeConnectedHeader))); |
| 531 } |
| 532 } |
| 533 } |
OLD | NEW |