Index: chrome/browser/renderer_host/chrome_resource_dispatcher_host_delegate_browsertest.cc |
diff --git a/chrome/browser/renderer_host/chrome_resource_dispatcher_host_delegate_browsertest.cc b/chrome/browser/renderer_host/chrome_resource_dispatcher_host_delegate_browsertest.cc |
index 57388aaed81d6c3ad32f7421455e84acb653a35b..c34027a0ad8aef2402fe7445e4f1838f65d80e5b 100644 |
--- a/chrome/browser/renderer_host/chrome_resource_dispatcher_host_delegate_browsertest.cc |
+++ b/chrome/browser/renderer_host/chrome_resource_dispatcher_host_delegate_browsertest.cc |
@@ -8,16 +8,21 @@ |
#include <memory> |
#include <utility> |
+#include <vector> |
#include "base/command_line.h" |
#include "base/macros.h" |
+#include "base/path_service.h" |
+#include "base/stl_util.h" |
#include "base/strings/string_util.h" |
+#include "base/test/scoped_command_line.h" |
#include "chrome/browser/browser_process.h" |
#include "chrome/browser/policy/cloud/policy_header_service_factory.h" |
#include "chrome/browser/profiles/profile.h" |
#include "chrome/browser/renderer_host/chrome_navigation_data.h" |
#include "chrome/browser/ui/browser.h" |
#include "chrome/browser/ui/tabs/tab_strip_model.h" |
+#include "chrome/common/chrome_paths.h" |
#include "chrome/test/base/in_process_browser_test.h" |
#include "chrome/test/base/ui_test_utils.h" |
#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_data.h" |
@@ -25,18 +30,28 @@ |
#include "components/policy/core/common/cloud/policy_header_io_helper.h" |
#include "components/policy/core/common/cloud/policy_header_service.h" |
#include "components/policy/core/common/policy_switches.h" |
+#include "components/prefs/pref_service.h" |
+#include "components/signin/core/browser/signin_header_helper.h" |
+#include "components/signin/core/common/signin_pref_names.h" |
+#include "components/signin/core/common/signin_switches.h" |
#include "content/public/browser/navigation_data.h" |
#include "content/public/browser/navigation_handle.h" |
#include "content/public/browser/resource_dispatcher_host.h" |
+#include "content/public/browser/resource_dispatcher_host_delegate.h" |
#include "content/public/browser/web_contents_observer.h" |
#include "content/public/test/browser_test_utils.h" |
#include "net/http/http_request_headers.h" |
#include "net/test/embedded_test_server/embedded_test_server.h" |
#include "net/test/embedded_test_server/http_request.h" |
#include "net/test/embedded_test_server/http_response.h" |
+#include "net/test/url_request/url_request_mock_http_job.h" |
#include "net/url_request/url_request.h" |
+#include "net/url_request/url_request_filter.h" |
+#include "testing/gmock/include/gmock/gmock-matchers.h" |
using content::ResourceType; |
+using testing::HasSubstr; |
+using testing::Not; |
namespace { |
static const char kTestPolicyHeader[] = "test_header"; |
@@ -251,3 +266,228 @@ IN_PROC_BROWSER_TEST_F(ChromeResourceDispatcherHostDelegateBrowserTest, |
ui_test_utils::NavigateToURL(browser(), embedded_test_server()->base_url()); |
} |
} |
+ |
+namespace { |
+ |
+// A URLRequestMockHTTPJob to that reports HTTP request headers of outgoing |
+// requests. |
+class MirrorMockURLRequestJob : public net::URLRequestMockHTTPJob { |
+ public: |
+ // Callback function on the UI thread to report a (URL, request headers) |
+ // pair. Indicating that the |request headers| will be sent for the |URL|. |
+ using ReportResponseHeadersOnUI = |
+ base::Callback<void(const std::string&, const std::string&)>; |
+ |
+ MirrorMockURLRequestJob(net::URLRequest* request, |
+ net::NetworkDelegate* network_delegate, |
+ const base::FilePath& file_path, |
+ const scoped_refptr<base::TaskRunner>& task_runner, |
+ ReportResponseHeadersOnUI report_on_ui) |
+ : net::URLRequestMockHTTPJob(request, |
+ network_delegate, |
+ file_path, |
+ task_runner), |
+ report_on_ui_(report_on_ui) {} |
+ |
+ void Start() override { |
+ // Report the observed request headers on the UI thread. |
+ content::BrowserThread::PostTask( |
+ content::BrowserThread::UI, FROM_HERE, |
+ base::Bind(report_on_ui_, request_->url().spec(), |
+ request_->extra_request_headers().ToString())); |
+ |
+ URLRequestMockHTTPJob::Start(); |
+ } |
+ |
+ protected: |
+ const ReportResponseHeadersOnUI report_on_ui_; |
+ |
+ private: |
+ DISALLOW_COPY_AND_ASSIGN(MirrorMockURLRequestJob); |
+}; |
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.
|
+ |
+// A URLRequestInterceptor to inject MirrorMockURLRequestJobs. |
+class MirrorMockJobInterceptor : public net::URLRequestInterceptor { |
+ public: |
+ using ReportResponseHeadersOnUI = |
+ MirrorMockURLRequestJob::ReportResponseHeadersOnUI; |
+ |
+ MirrorMockJobInterceptor(const base::FilePath& root_http, |
+ ReportResponseHeadersOnUI report_on_ui) |
+ : root_http_(root_http), report_on_ui_(report_on_ui) {} |
+ ~MirrorMockJobInterceptor() override = default; |
+ |
+ // URLRequestInterceptor implementation |
+ net::URLRequestJob* MaybeInterceptRequest( |
+ net::URLRequest* request, |
+ net::NetworkDelegate* network_delegate) const override { |
+ return new MirrorMockURLRequestJob( |
+ request, network_delegate, root_http_, |
+ content::BrowserThread::GetBlockingPool() |
+ ->GetTaskRunnerWithShutdownBehavior( |
+ base::SequencedWorkerPool::SKIP_ON_SHUTDOWN), |
+ report_on_ui_); |
+ } |
+ |
+ static void Register(const GURL& url, |
+ const base::FilePath& root_http, |
+ ReportResponseHeadersOnUI report_on_ui) { |
+ EXPECT_TRUE( |
+ content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); |
+ base::FilePath file_path(root_http); |
+ file_path = |
+ file_path.AppendASCII(url.scheme() + "." + url.host() + ".html"); |
+ net::URLRequestFilter::GetInstance()->AddHostnameInterceptor( |
+ url.scheme(), url.host(), base::WrapUnique(new MirrorMockJobInterceptor( |
+ file_path, report_on_ui))); |
+ } |
+ |
+ static void Unregister(const GURL& url) { |
+ EXPECT_TRUE( |
+ content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); |
+ net::URLRequestFilter::GetInstance()->RemoveHostnameHandler(url.scheme(), |
+ url.host()); |
+ } |
+ |
+ private: |
+ const base::FilePath root_http_; |
+ ReportResponseHeadersOnUI report_on_ui_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(MirrorMockJobInterceptor); |
+}; |
+ |
+void ReportRequestHeaders(std::map<std::string, std::string>* request_headers, |
+ const std::string& url, |
+ const std::string& headers) { |
+ (*request_headers)[url] = headers; |
+} |
+ |
+void EmptyClosure() {} |
+ |
+} // namespace |
+ |
+class HeaderTestDispatcherHostDelegate |
+ : public ChromeResourceDispatcherHostDelegate { |
+ public: |
+ explicit HeaderTestDispatcherHostDelegate(const GURL& watch_url) |
+ : watch_url_(watch_url) {} |
+ ~HeaderTestDispatcherHostDelegate() override {} |
+ |
+ void RequestBeginning( |
+ net::URLRequest* request, |
+ content::ResourceContext* resource_context, |
+ content::AppCacheService* appcache_service, |
+ ResourceType resource_type, |
+ ScopedVector<content::ResourceThrottle>* throttles) override { |
+ ChromeResourceDispatcherHostDelegate::RequestBeginning( |
+ request, resource_context, appcache_service, resource_type, throttles); |
+ if (request->url() == watch_url_) { |
+ request->SetExtraRequestHeaderByName(signin::kChromeConnectedHeader, |
+ "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
|
+ } |
+ } |
+ |
+ private: |
+ const GURL watch_url_; |
+ DISALLOW_COPY_AND_ASSIGN(HeaderTestDispatcherHostDelegate); |
+}; |
+ |
+// Verify the following items: |
+// 1- X-Chrome-Connected is appended on Google domains if account |
+// consistency is enabled and access is secure. |
+// 2- The header is stripped in case a request is redirected from a Gooogle |
+// domain to non-google domain. |
+// 3- The header is NOT stripped in case it is added directly by the page |
+// and not because it was on a secure Google domain. |
+// This is a regression test for crbug.com/588492. |
+IN_PROC_BROWSER_TEST_F(ChromeResourceDispatcherHostDelegateBrowserTest, |
+ MirrorRequestHeader) { |
+ // Enable account consistency so that mirror actually sets the |
+ // X-Chrome-Connected header in requests to Google. |
+ base::test::ScopedCommandLine command_line; |
+ command_line.GetProcessCommandLine()->AppendSwitch( |
+ switches::kEnableAccountConsistency); |
+ |
+ browser()->profile()->GetPrefs()->SetString(prefs::kGoogleServicesUsername, |
+ "user@gmail.com"); |
+ browser()->profile()->GetPrefs()->SetString( |
+ prefs::kGoogleServicesUserAccountId, "account_id"); |
+ |
+ // Set URLS: |
+ // The one that needs X-Chrome-Header injection: |
+ GURL http_header_adder_com("http://www.header_adder.com"); |
+ // The ones that should have the X-Chrome-Header: |
+ std::vector<GURL> urls_with_header = { |
+ http_header_adder_com, GURL("https://www.google.com"), |
+ GURL("http://www.redirected_from_header_adder.com")}; |
+ // All others: |
+ std::vector<GURL> all_urls = {GURL("http://www.google.com"), |
+ GURL("https://www.redirected.com"), |
+ GURL("http://www.redirected.com")}; |
+ // Merge: |
+ all_urls.insert(all_urls.end(), urls_with_header.begin(), |
+ 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.
|
+ |
+ // Change the delegate to the one that adds the header for |
+ // http_header_adder_com. |
+ HeaderTestDispatcherHostDelegate dispatcher_host_delegate( |
+ http_header_adder_com); |
+ content::ResourceDispatcherHost::Get()->SetDelegate( |
+ &dispatcher_host_delegate); |
+ |
+ // The HTTP Request headers (i.e. the ones that are managed on the URLRequest |
+ // layer, not on the URLRequestJob layer) sent from the browser are collected |
+ // in this map. The keys are URLs the values the request headers. |
+ std::map<std::string, std::string> request_headers; |
+ MirrorMockURLRequestJob::ReportResponseHeadersOnUI report_request_headers = |
+ base::Bind(&ReportRequestHeaders, &request_headers); |
+ |
+ base::FilePath root_http; |
+ PathService::Get(chrome::DIR_TEST_DATA, &root_http); |
+ root_http = root_http.AppendASCII("mirror_request_header"); |
+ |
+ for (const GURL& url : all_urls) { |
+ content::BrowserThread::PostTask( |
+ content::BrowserThread::IO, FROM_HERE, |
+ base::Bind(&MirrorMockJobInterceptor::Register, url, root_http, |
+ report_request_headers)); |
+ } |
+ |
+ // Perform a navigation to all none-redirected urls to observe |
+ // the request headers. |
+ for (const GURL& url : all_urls) { |
+ if (url.host().find("redirect") == -1) |
+ ui_test_utils::NavigateToURL(browser(), url); |
+ } |
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
|
+ |
+ // Cleanup before verifying the observed headers. |
+ for (const GURL& url : all_urls) { |
+ content::BrowserThread::PostTask( |
+ content::BrowserThread::IO, FROM_HERE, |
+ base::Bind(&MirrorMockJobInterceptor::Unregister, url)); |
+ } |
+ |
+ // Ensure that the response headers have been reported to the UI thread |
+ // and unregistration has been processed on the IO thread. |
+ base::RunLoop run_loop; |
+ content::BrowserThread::PostTaskAndReply(content::BrowserThread::IO, |
+ FROM_HERE, |
+ // Flush IO thread... |
+ base::Bind(&EmptyClosure), |
+ // ... and UI thread. |
+ run_loop.QuitClosure()); |
+ run_loop.Run(); |
+ |
+ content::ResourceDispatcherHost::Get()->SetDelegate(nullptr); |
+ |
+ // Check if header exists and X-Chrome-Connected is correctly provided. |
+ for (const GURL url : all_urls) { |
+ ASSERT_EQ(1u, request_headers.count(url.spec())); |
+ if (base::ContainsValue(urls_with_header, url)) |
+ EXPECT_THAT(request_headers[url.spec()], |
+ HasSubstr(signin::kChromeConnectedHeader)); |
+ else |
+ EXPECT_THAT(request_headers[url.spec()], |
+ Not(HasSubstr(signin::kChromeConnectedHeader))); |
+ } |
+} |