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..818b46b898a68a07446061b674332fc1d054a335 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 |
@@ -12,6 +12,7 @@ |
#include "base/command_line.h" |
#include "base/macros.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" |
@@ -25,18 +26,31 @@ |
#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/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/web_contents_observer.h" |
#include "content/public/test/browser_test_utils.h" |
+#include "net/base/io_buffer.h" |
#include "net/http/http_request_headers.h" |
+#include "net/http/http_response_headers.h" |
+#include "net/http/http_util.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/url_request/url_request.h" |
+#include "net/url_request/url_request_filter.h" |
+#include "net/url_request/url_request_interceptor.h" |
+#include "net/url_request/url_request_job.h" |
+#include "testing/gmock/include/gmock/gmock.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
using content::ResourceType; |
+using testing::HasSubstr; |
+using testing::Not; |
namespace { |
static const char kTestPolicyHeader[] = "test_header"; |
@@ -251,3 +265,223 @@ IN_PROC_BROWSER_TEST_F(ChromeResourceDispatcherHostDelegateBrowserTest, |
ui_test_utils::NavigateToURL(browser(), embedded_test_server()->base_url()); |
} |
} |
+ |
+namespace { |
+ |
+// A mock URLRequestJob to that allows to inject specific response headers |
+// and a specific response body to a network request. |
+class MirrorMockURLRequestJob : public net::URLRequestJob { |
sky
2016/08/18 19:29:50
Can you use some of the test support classes in ne
Ramin Halavati
2016/08/26 17:04:31
Done.
|
+ 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 std::string& response_headers, |
+ const std::string& response_body, |
+ ReportResponseHeadersOnUI report_on_ui) |
+ : net::URLRequestJob(request, network_delegate), |
+ response_headers_(response_headers), |
+ response_body_(response_body), |
+ response_body_offset_(0), |
+ report_on_ui_(report_on_ui), |
+ weak_factory_(this) {} |
+ |
+ 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())); |
+ |
+ // Start reading asynchronously so that all error reporting and data |
+ // callbacks happen as they would for network requests. |
+ base::ThreadTaskRunnerHandle::Get()->PostTask( |
+ FROM_HERE, base::Bind(&MirrorMockURLRequestJob::StartAsync, |
+ weak_factory_.GetWeakPtr())); |
+ } |
+ |
+ int ReadRawData(net::IOBuffer* buf, int buf_size) override { |
+ size_t bytes_read = |
+ std::min(static_cast<size_t>(buf_size), |
+ response_body_.length() - response_body_offset_); |
+ memcpy(buf->data(), response_body_.c_str() + response_body_offset_, |
+ bytes_read); |
+ response_body_offset_ += bytes_read; |
+ return bytes_read; |
+ } |
+ |
+ int GetResponseCode() const override { |
+ net::HttpResponseInfo info; |
+ GetResponseInfoConst(&info); |
+ return info.headers->response_code(); |
+ } |
+ |
+ void GetResponseInfo(net::HttpResponseInfo* info) override { |
+ // Forward to private const version. |
+ GetResponseInfoConst(info); |
+ } |
+ |
+ protected: |
+ void StartAsync() { |
+ if (!request_) |
+ return; |
+ set_expected_content_size(response_body_.length()); |
+ NotifyHeadersComplete(); |
+ } |
+ |
+ // Private const version. |
+ void GetResponseInfoConst(net::HttpResponseInfo* info) const { |
+ // Send back mock headers. |
+ std::string raw_headers; |
+ raw_headers.append(response_headers_); |
+ if (!response_body_.empty()) { |
+ raw_headers.append(base::StringPrintf( |
+ "Content-Length: %d\n", static_cast<int>(response_body_.length()))); |
+ } |
+ info->headers = |
+ new net::HttpResponseHeaders(net::HttpUtil::AssembleRawHeaders( |
+ raw_headers.c_str(), static_cast<int>(raw_headers.length()))); |
+ } |
+ |
+ const std::string response_headers_; // All headers but 'Content-Length'. |
+ const std::string response_body_; |
+ size_t response_body_offset_; |
+ const ReportResponseHeadersOnUI report_on_ui_; |
+ base::WeakPtrFactory<MirrorMockURLRequestJob> weak_factory_; |
+}; |
+ |
+// A URLRequestInterceptor to inject MirrorMockURLRequestJobs. |
+class MirrorMockJobInterceptor : public net::URLRequestInterceptor { |
+ public: |
+ using ReportResponseHeadersOnUI = |
+ MirrorMockURLRequestJob::ReportResponseHeadersOnUI; |
+ |
+ MirrorMockJobInterceptor(const std::string& response_headers, |
+ const std::string& response_body, |
+ ReportResponseHeadersOnUI report_on_ui) |
+ : response_headers_(response_headers), |
+ response_body_(response_body), |
+ 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, |
+ response_headers_, response_body_, |
+ report_on_ui_); |
+ } |
+ |
+ static void Register(const GURL& url, |
+ const std::string& response_headers, |
+ const std::string& response_body, |
+ ReportResponseHeadersOnUI report_on_ui) { |
+ EXPECT_TRUE( |
+ content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); |
+ net::URLRequestFilter::GetInstance()->AddHostnameInterceptor( |
+ url.scheme(), url.host(), |
+ base::WrapUnique(new MirrorMockJobInterceptor( |
+ response_headers, response_body, 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: |
+ std::string response_headers_; |
+ std::string response_body_; |
+ 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 |
+ |
+IN_PROC_BROWSER_TEST_F(ChromeResourceDispatcherHostDelegateBrowserTest, |
+ MirrorRequestHeader) { |
+ // Verify that X-Chrome-Connected is appended on Google domains if account |
+ // consistency is enabled, but also that it is stripped in case a request |
+ // is redirected to a non-google domain. |
+ // This is a regression test for crbug.com/588492. |
+ |
+ // 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"); |
+ |
+ GURL google_com("http://www.google.com/"); |
+ GURL redirected_com("http://www.redirected.com/"); |
+ |
+ // 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); |
+ |
+ // Register MockJobInterceptors that redirect from google.com to |
+ // redirected.com and serve static content on redirected.com. |
+ content::BrowserThread::PostTask( |
+ content::BrowserThread::IO, FROM_HERE, |
+ base::Bind(&MirrorMockJobInterceptor::Register, google_com, |
+ "HTTP/1.1 301 Moved Permanently\n" |
+ "Location: http://www.redirected.com/\n", |
+ std::string(), report_request_headers)); |
+ content::BrowserThread::PostTask( |
+ content::BrowserThread::IO, FROM_HERE, |
+ base::Bind(&MirrorMockJobInterceptor::Register, redirected_com, |
+ "HTTP/1.1 200 OK\n" |
+ "Content-type: text/plain\n", |
+ "success", report_request_headers)); |
+ |
+ // Perform a navigation to google.com to observe the request headers. |
+ ui_test_utils::NavigateToURL(browser(), google_com); |
+ |
+ // Cleanup before verifying the observed headers. |
+ content::BrowserThread::PostTask( |
+ content::BrowserThread::IO, FROM_HERE, |
+ base::Bind(&MirrorMockJobInterceptor::Unregister, redirected_com)); |
+ content::BrowserThread::PostTask( |
+ content::BrowserThread::IO, FROM_HERE, |
+ base::Bind(&MirrorMockJobInterceptor::Unregister, google_com)); |
+ |
+ // 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(); |
+ |
+ ASSERT_EQ(1u, request_headers.count(google_com.spec())); |
+ EXPECT_THAT(request_headers[google_com.spec()], |
+ HasSubstr("X-Chrome-Connected")); |
+ ASSERT_EQ(1u, request_headers.count(redirected_com.spec())); |
+ EXPECT_THAT(request_headers[redirected_com.spec()], |
+ Not(HasSubstr("X-Chrome-Connected"))); |
+} |