Chromium Code Reviews| 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 <memory> | 9 #include <memory> |
| 10 #include <utility> | 10 #include <utility> |
| 11 #include <vector> | |
| 11 | 12 |
| 12 #include "base/command_line.h" | 13 #include "base/command_line.h" |
| 13 #include "base/macros.h" | 14 #include "base/macros.h" |
| 15 #include "base/path_service.h" | |
| 16 #include "base/stl_util.h" | |
| 14 #include "base/strings/string_util.h" | 17 #include "base/strings/string_util.h" |
| 18 #include "base/test/scoped_command_line.h" | |
| 15 #include "chrome/browser/browser_process.h" | 19 #include "chrome/browser/browser_process.h" |
| 16 #include "chrome/browser/policy/cloud/policy_header_service_factory.h" | 20 #include "chrome/browser/policy/cloud/policy_header_service_factory.h" |
| 17 #include "chrome/browser/profiles/profile.h" | 21 #include "chrome/browser/profiles/profile.h" |
| 18 #include "chrome/browser/renderer_host/chrome_navigation_data.h" | 22 #include "chrome/browser/renderer_host/chrome_navigation_data.h" |
| 19 #include "chrome/browser/ui/browser.h" | 23 #include "chrome/browser/ui/browser.h" |
| 20 #include "chrome/browser/ui/tabs/tab_strip_model.h" | 24 #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| 25 #include "chrome/common/chrome_paths.h" | |
| 21 #include "chrome/test/base/in_process_browser_test.h" | 26 #include "chrome/test/base/in_process_browser_test.h" |
| 22 #include "chrome/test/base/ui_test_utils.h" | 27 #include "chrome/test/base/ui_test_utils.h" |
| 23 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_data .h" | 28 #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_data .h" |
| 24 #include "components/policy/core/common/cloud/cloud_policy_constants.h" | 29 #include "components/policy/core/common/cloud/cloud_policy_constants.h" |
| 25 #include "components/policy/core/common/cloud/policy_header_io_helper.h" | 30 #include "components/policy/core/common/cloud/policy_header_io_helper.h" |
| 26 #include "components/policy/core/common/cloud/policy_header_service.h" | 31 #include "components/policy/core/common/cloud/policy_header_service.h" |
| 27 #include "components/policy/core/common/policy_switches.h" | 32 #include "components/policy/core/common/policy_switches.h" |
| 33 #include "components/prefs/pref_service.h" | |
| 34 #include "components/signin/core/browser/signin_header_helper.h" | |
| 35 #include "components/signin/core/common/signin_pref_names.h" | |
| 36 #include "components/signin/core/common/signin_switches.h" | |
| 28 #include "content/public/browser/navigation_data.h" | 37 #include "content/public/browser/navigation_data.h" |
| 29 #include "content/public/browser/navigation_handle.h" | 38 #include "content/public/browser/navigation_handle.h" |
| 30 #include "content/public/browser/resource_dispatcher_host.h" | 39 #include "content/public/browser/resource_dispatcher_host.h" |
| 40 #include "content/public/browser/resource_dispatcher_host_delegate.h" | |
| 31 #include "content/public/browser/web_contents_observer.h" | 41 #include "content/public/browser/web_contents_observer.h" |
| 32 #include "content/public/test/browser_test_utils.h" | 42 #include "content/public/test/browser_test_utils.h" |
| 33 #include "net/http/http_request_headers.h" | 43 #include "net/http/http_request_headers.h" |
| 34 #include "net/test/embedded_test_server/embedded_test_server.h" | 44 #include "net/test/embedded_test_server/embedded_test_server.h" |
| 35 #include "net/test/embedded_test_server/http_request.h" | 45 #include "net/test/embedded_test_server/http_request.h" |
| 36 #include "net/test/embedded_test_server/http_response.h" | 46 #include "net/test/embedded_test_server/http_response.h" |
| 47 #include "net/test/url_request/url_request_mock_http_job.h" | |
| 37 #include "net/url_request/url_request.h" | 48 #include "net/url_request/url_request.h" |
| 49 #include "net/url_request/url_request_filter.h" | |
| 50 #include "testing/gmock/include/gmock/gmock-matchers.h" | |
| 38 | 51 |
| 39 using content::ResourceType; | 52 using content::ResourceType; |
| 53 using testing::HasSubstr; | |
| 54 using testing::Not; | |
| 40 | 55 |
| 41 namespace { | 56 namespace { |
| 42 static const char kTestPolicyHeader[] = "test_header"; | 57 static const char kTestPolicyHeader[] = "test_header"; |
| 43 static const char kServerRedirectUrl[] = "/server-redirect"; | 58 static const char kServerRedirectUrl[] = "/server-redirect"; |
| 44 | 59 |
| 45 std::unique_ptr<net::test_server::HttpResponse> HandleTestRequest( | 60 std::unique_ptr<net::test_server::HttpResponse> HandleTestRequest( |
| 46 const net::test_server::HttpRequest& request) { | 61 const net::test_server::HttpRequest& request) { |
| 47 if (base::StartsWith(request.relative_url, kServerRedirectUrl, | 62 if (base::StartsWith(request.relative_url, kServerRedirectUrl, |
| 48 base::CompareCase::SENSITIVE)) { | 63 base::CompareCase::SENSITIVE)) { |
| 49 // Extract the target URL and redirect there. | 64 // 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( | 259 ui_test_utils::NavigateToURL( |
| 245 browser(), embedded_test_server()->GetURL("/google/google.html")); | 260 browser(), embedded_test_server()->GetURL("/google/google.html")); |
| 246 } | 261 } |
| 247 SetShouldAddDataReductionProxyData(true); | 262 SetShouldAddDataReductionProxyData(true); |
| 248 { | 263 { |
| 249 DidFinishNavigationObserver nav_observer( | 264 DidFinishNavigationObserver nav_observer( |
| 250 browser()->tab_strip_model()->GetActiveWebContents(), true); | 265 browser()->tab_strip_model()->GetActiveWebContents(), true); |
| 251 ui_test_utils::NavigateToURL(browser(), embedded_test_server()->base_url()); | 266 ui_test_utils::NavigateToURL(browser(), embedded_test_server()->base_url()); |
| 252 } | 267 } |
| 253 } | 268 } |
| 269 | |
| 270 namespace { | |
| 271 | |
| 272 // A URLRequestMockHTTPJob to that reports HTTP request headers of outgoing | |
| 273 // requests. | |
| 274 class MirrorMockURLRequestJob : public net::URLRequestMockHTTPJob { | |
| 275 public: | |
| 276 // Callback function on the UI thread to report a (URL, request headers) | |
| 277 // pair. Indicating that the |request headers| will be sent for the |URL|. | |
| 278 using ReportResponseHeadersOnUI = | |
| 279 base::Callback<void(const std::string&, const std::string&)>; | |
| 280 | |
| 281 MirrorMockURLRequestJob(net::URLRequest* request, | |
| 282 net::NetworkDelegate* network_delegate, | |
| 283 const base::FilePath& file_path, | |
| 284 const scoped_refptr<base::TaskRunner>& task_runner, | |
| 285 ReportResponseHeadersOnUI report_on_ui) | |
| 286 : net::URLRequestMockHTTPJob(request, | |
| 287 network_delegate, | |
| 288 file_path, | |
| 289 task_runner), | |
| 290 report_on_ui_(report_on_ui) {} | |
| 291 | |
| 292 void Start() override { | |
| 293 // Report the observed request headers on the UI thread. | |
| 294 content::BrowserThread::PostTask( | |
| 295 content::BrowserThread::UI, FROM_HERE, | |
| 296 base::Bind(report_on_ui_, request_->url().spec(), | |
| 297 request_->extra_request_headers().ToString())); | |
| 298 | |
| 299 URLRequestMockHTTPJob::Start(); | |
| 300 } | |
| 301 | |
| 302 protected: | |
| 303 const ReportResponseHeadersOnUI report_on_ui_; | |
| 304 | |
| 305 private: | |
| 306 DISALLOW_COPY_AND_ASSIGN(MirrorMockURLRequestJob); | |
| 307 }; | |
|
mmenke
2016/08/30 19:12:26
Do the google checks care about port? It doesn't
mmenke
2016/08/30 19:23:39
Actually, probably best to ignore this suggestion
Ramin Halavati
2016/09/01 10:41:44
Acknowledged.
Ramin Halavati
2016/09/01 10:41:44
Acknowledged.
| |
| 308 | |
| 309 // A URLRequestInterceptor to inject MirrorMockURLRequestJobs. | |
| 310 class MirrorMockJobInterceptor : public net::URLRequestInterceptor { | |
| 311 public: | |
| 312 using ReportResponseHeadersOnUI = | |
| 313 MirrorMockURLRequestJob::ReportResponseHeadersOnUI; | |
| 314 | |
| 315 MirrorMockJobInterceptor(const base::FilePath& root_http, | |
| 316 ReportResponseHeadersOnUI report_on_ui) | |
| 317 : root_http_(root_http), report_on_ui_(report_on_ui) {} | |
| 318 ~MirrorMockJobInterceptor() override = default; | |
| 319 | |
| 320 // URLRequestInterceptor implementation | |
| 321 net::URLRequestJob* MaybeInterceptRequest( | |
| 322 net::URLRequest* request, | |
| 323 net::NetworkDelegate* network_delegate) const override { | |
| 324 return new MirrorMockURLRequestJob( | |
| 325 request, network_delegate, root_http_, | |
| 326 content::BrowserThread::GetBlockingPool() | |
| 327 ->GetTaskRunnerWithShutdownBehavior( | |
| 328 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN), | |
| 329 report_on_ui_); | |
| 330 } | |
| 331 | |
| 332 static void Register(const GURL& url, | |
| 333 const base::FilePath& root_http, | |
| 334 ReportResponseHeadersOnUI report_on_ui) { | |
| 335 EXPECT_TRUE( | |
| 336 content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); | |
| 337 base::FilePath file_path(root_http); | |
| 338 file_path = | |
| 339 file_path.AppendASCII(url.scheme() + "." + url.host() + ".html"); | |
| 340 net::URLRequestFilter::GetInstance()->AddHostnameInterceptor( | |
| 341 url.scheme(), url.host(), base::WrapUnique(new MirrorMockJobInterceptor( | |
| 342 file_path, report_on_ui))); | |
| 343 } | |
| 344 | |
| 345 static void Unregister(const GURL& url) { | |
| 346 EXPECT_TRUE( | |
| 347 content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); | |
| 348 net::URLRequestFilter::GetInstance()->RemoveHostnameHandler(url.scheme(), | |
| 349 url.host()); | |
| 350 } | |
| 351 | |
| 352 private: | |
| 353 const base::FilePath root_http_; | |
| 354 ReportResponseHeadersOnUI report_on_ui_; | |
| 355 | |
| 356 DISALLOW_COPY_AND_ASSIGN(MirrorMockJobInterceptor); | |
| 357 }; | |
| 358 | |
| 359 void ReportRequestHeaders(std::map<std::string, std::string>* request_headers, | |
| 360 const std::string& url, | |
| 361 const std::string& headers) { | |
| 362 (*request_headers)[url] = headers; | |
| 363 } | |
| 364 | |
| 365 void EmptyClosure() {} | |
| 366 | |
| 367 } // namespace | |
| 368 | |
| 369 class HeaderTestDispatcherHostDelegate | |
| 370 : public ChromeResourceDispatcherHostDelegate { | |
| 371 public: | |
| 372 explicit HeaderTestDispatcherHostDelegate(const GURL& watch_url) | |
| 373 : watch_url_(watch_url) {} | |
| 374 ~HeaderTestDispatcherHostDelegate() override {} | |
| 375 | |
| 376 void RequestBeginning( | |
| 377 net::URLRequest* request, | |
| 378 content::ResourceContext* resource_context, | |
| 379 content::AppCacheService* appcache_service, | |
| 380 ResourceType resource_type, | |
| 381 ScopedVector<content::ResourceThrottle>* throttles) override { | |
| 382 ChromeResourceDispatcherHostDelegate::RequestBeginning( | |
| 383 request, resource_context, appcache_service, resource_type, throttles); | |
| 384 if (request->url() == watch_url_) { | |
| 385 request->SetExtraRequestHeaderByName(signin::kChromeConnectedHeader, | |
| 386 "User Data", false); | |
|
mmenke
2016/08/30 19:12:26
Why are we appending this? Shouldn't the RDH do t
Ramin Halavati
2016/09/01 10:41:44
We added a new test in the last CL in which a webs
| |
| 387 } | |
| 388 } | |
| 389 | |
| 390 private: | |
| 391 const GURL watch_url_; | |
| 392 DISALLOW_COPY_AND_ASSIGN(HeaderTestDispatcherHostDelegate); | |
| 393 }; | |
| 394 | |
| 395 // Verify the following items: | |
| 396 // 1- X-Chrome-Connected is appended on Google domains if account | |
| 397 // consistency is enabled and access is secure. | |
| 398 // 2- The header is stripped in case a request is redirected from a Gooogle | |
| 399 // domain to non-google domain. | |
| 400 // 3- The header is NOT stripped in case it is added directly by the page | |
| 401 // and not because it was on a secure Google domain. | |
| 402 // This is a regression test for crbug.com/588492. | |
| 403 IN_PROC_BROWSER_TEST_F(ChromeResourceDispatcherHostDelegateBrowserTest, | |
| 404 MirrorRequestHeader) { | |
| 405 // Enable account consistency so that mirror actually sets the | |
| 406 // X-Chrome-Connected header in requests to Google. | |
| 407 base::test::ScopedCommandLine command_line; | |
| 408 command_line.GetProcessCommandLine()->AppendSwitch( | |
| 409 switches::kEnableAccountConsistency); | |
| 410 | |
| 411 browser()->profile()->GetPrefs()->SetString(prefs::kGoogleServicesUsername, | |
| 412 "user@gmail.com"); | |
| 413 browser()->profile()->GetPrefs()->SetString( | |
| 414 prefs::kGoogleServicesUserAccountId, "account_id"); | |
| 415 | |
| 416 // Set URLS: | |
| 417 // The one that needs X-Chrome-Header injection: | |
| 418 GURL http_header_adder_com("http://www.header_adder.com"); | |
| 419 // The ones that should have the X-Chrome-Header: | |
| 420 std::vector<GURL> urls_with_header = { | |
| 421 http_header_adder_com, GURL("https://www.google.com"), | |
| 422 GURL("http://www.redirected_from_header_adder.com")}; | |
| 423 // All others: | |
| 424 std::vector<GURL> all_urls = {GURL("http://www.google.com"), | |
| 425 GURL("https://www.redirected.com"), | |
| 426 GURL("http://www.redirected.com")}; | |
| 427 // Merge: | |
| 428 all_urls.insert(all_urls.end(), urls_with_header.begin(), | |
| 429 urls_with_header.end()); | |
|
mmenke
2016/08/30 19:12:26
This seems like it would be much cleaner as single
Ramin Halavati
2016/09/01 10:41:44
Done.
| |
| 430 | |
| 431 // Change the delegate to the one that adds the header for | |
| 432 // http_header_adder_com. | |
| 433 HeaderTestDispatcherHostDelegate dispatcher_host_delegate( | |
| 434 http_header_adder_com); | |
| 435 content::ResourceDispatcherHost::Get()->SetDelegate( | |
| 436 &dispatcher_host_delegate); | |
| 437 | |
| 438 // The HTTP Request headers (i.e. the ones that are managed on the URLRequest | |
| 439 // layer, not on the URLRequestJob layer) sent from the browser are collected | |
| 440 // in this map. The keys are URLs the values the request headers. | |
| 441 std::map<std::string, std::string> request_headers; | |
| 442 MirrorMockURLRequestJob::ReportResponseHeadersOnUI report_request_headers = | |
| 443 base::Bind(&ReportRequestHeaders, &request_headers); | |
| 444 | |
| 445 base::FilePath root_http; | |
| 446 PathService::Get(chrome::DIR_TEST_DATA, &root_http); | |
| 447 root_http = root_http.AppendASCII("mirror_request_header"); | |
| 448 | |
| 449 for (const GURL& url : all_urls) { | |
| 450 content::BrowserThread::PostTask( | |
| 451 content::BrowserThread::IO, FROM_HERE, | |
| 452 base::Bind(&MirrorMockJobInterceptor::Register, url, root_http, | |
| 453 report_request_headers)); | |
| 454 } | |
| 455 | |
| 456 // Perform a navigation to all none-redirected urls to observe | |
| 457 // the request headers. | |
| 458 for (const GURL& url : all_urls) { | |
| 459 if (url.host().find("redirect") == -1) | |
| 460 ui_test_utils::NavigateToURL(browser(), url); | |
| 461 } | |
|
mmenke
2016/08/30 19:12:26
I'm unable to figure out how we visit these URLs.
Ramin Halavati
2016/09/01 10:41:44
We have 3 starting URLs here and each one redirect
| |
| 462 | |
| 463 // Cleanup before verifying the observed headers. | |
| 464 for (const GURL& url : all_urls) { | |
| 465 content::BrowserThread::PostTask( | |
| 466 content::BrowserThread::IO, FROM_HERE, | |
| 467 base::Bind(&MirrorMockJobInterceptor::Unregister, url)); | |
| 468 } | |
| 469 | |
| 470 // Ensure that the response headers have been reported to the UI thread | |
| 471 // and unregistration has been processed on the IO thread. | |
| 472 base::RunLoop run_loop; | |
| 473 content::BrowserThread::PostTaskAndReply(content::BrowserThread::IO, | |
| 474 FROM_HERE, | |
| 475 // Flush IO thread... | |
| 476 base::Bind(&EmptyClosure), | |
| 477 // ... and UI thread. | |
| 478 run_loop.QuitClosure()); | |
| 479 run_loop.Run(); | |
| 480 | |
| 481 content::ResourceDispatcherHost::Get()->SetDelegate(nullptr); | |
| 482 | |
| 483 // Check if header exists and X-Chrome-Connected is correctly provided. | |
| 484 for (const GURL url : all_urls) { | |
| 485 ASSERT_EQ(1u, request_headers.count(url.spec())); | |
| 486 if (base::ContainsValue(urls_with_header, url)) | |
| 487 EXPECT_THAT(request_headers[url.spec()], | |
| 488 HasSubstr(signin::kChromeConnectedHeader)); | |
| 489 else | |
| 490 EXPECT_THAT(request_headers[url.spec()], | |
| 491 Not(HasSubstr(signin::kChromeConnectedHeader))); | |
| 492 } | |
| 493 } | |
| OLD | NEW |