blob: da8d32e6a5324dba2e3eea1b6220b27deb63f20a [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/variations/net/variations_http_headers.h"
#include <map>
#include <memory>
#include "base/bind.h"
#include "base/macros.h"
#include "base/optional.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/strings/strcat.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_browser_main.h"
#include "chrome/browser/chrome_browser_main_extra_parts.h"
#include "chrome/browser/net/system_network_context_manager.h"
#include "chrome/browser/optimization_guide/optimization_guide_keyed_service.h"
#include "chrome/browser/optimization_guide/optimization_guide_keyed_service_factory.h"
#include "chrome/browser/predictors/predictors_features.h"
#include "chrome/browser/predictors/predictors_switches.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.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/network_session_configurator/common/network_switches.h"
#include "components/optimization_guide/optimization_guide_features.h"
#include "components/optimization_guide/proto/hints.pb.h"
#include "components/signin/public/identity_manager/identity_test_utils.h"
#include "components/variations/net/variations_http_headers.h"
#include "components/variations/proto/study.pb.h"
#include "components/variations/variations.mojom.h"
#include "components/variations/variations_features.h"
#include "components/variations/variations_ids_provider.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/network_connection_change_simulator.h"
#include "content/public/test/simple_url_loader_test_helper.h"
#include "net/base/escape.h"
#include "net/dns/mock_host_resolver.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/traffic_annotation/network_traffic_annotation_test_helper.h"
#include "services/network/public/cpp/features.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace {
class VariationHeaderSetter : public ChromeBrowserMainExtraParts {
public:
VariationHeaderSetter() = default;
~VariationHeaderSetter() override = default;
// ChromeBrowserMainExtraParts:
void PostEarlyInitialization() override {
// Set up some fake variations.
auto* variations_provider =
variations::VariationsIdsProvider::GetInstance();
variations_provider->ForceVariationIds({"12", "456", "t789"}, "");
}
private:
DISALLOW_COPY_AND_ASSIGN(VariationHeaderSetter);
};
class VariationsHttpHeadersBrowserTest : public InProcessBrowserTest {
public:
VariationsHttpHeadersBrowserTest()
: https_server_(net::test_server::EmbeddedTestServer::TYPE_HTTPS) {}
~VariationsHttpHeadersBrowserTest() override = default;
void CreatedBrowserMainParts(content::BrowserMainParts* parts) override {
static_cast<ChromeBrowserMainParts*>(parts)->AddParts(
std::make_unique<VariationHeaderSetter>());
}
void SetUp() override {
ASSERT_TRUE(server()->InitializeAndListen());
InProcessBrowserTest::SetUp();
}
void SetUpOnMainThread() override {
InProcessBrowserTest::SetUpOnMainThread();
content::NetworkConnectionChangeSimulator().SetConnectionType(
network::mojom::ConnectionType::CONNECTION_ETHERNET);
host_resolver()->AddRule("*", "127.0.0.1");
base::FilePath test_data_dir;
ASSERT_TRUE(base::PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir));
server()->ServeFilesFromDirectory(test_data_dir);
server()->RegisterRequestHandler(
base::BindRepeating(&VariationsHttpHeadersBrowserTest::RequestHandler,
base::Unretained(this)));
server()->StartAcceptingConnections();
}
void SetUpCommandLine(base::CommandLine* command_line) override {
InProcessBrowserTest::SetUpCommandLine(command_line);
command_line->AppendSwitch(switches::kIgnoreCertificateErrors);
}
const net::EmbeddedTestServer* server() const { return &https_server_; }
net::EmbeddedTestServer* server() { return &https_server_; }
GURL GetGoogleUrlWithPath(const std::string& path) const {
return server()->GetURL("www.google.com", path);
}
GURL GetGoogleUrl() const { return GetGoogleUrlWithPath("/landing.html"); }
GURL GetGoogleRedirectUrl1() const {
return GetGoogleUrlWithPath("/redirect");
}
GURL GetGoogleRedirectUrl2() const {
return GetGoogleUrlWithPath("/redirect2");
}
GURL GetGoogleSubresourceUrl() const {
return GetGoogleUrlWithPath("/logo.png");
}
GURL GetExampleUrlWithPath(const std::string& path) const {
return server()->GetURL("www.example.com", path);
}
GURL GetExampleUrl() const { return GetExampleUrlWithPath("/landing.html"); }
void WaitForRequest(const GURL& url) {
auto it = received_headers_.find(url);
if (it != received_headers_.end())
return;
base::RunLoop loop;
done_callbacks_.emplace(url, loop.QuitClosure());
loop.Run();
}
// Returns whether a given |header| has been received for a |url|. If
// |url| has not been observed, fails an EXPECT and returns false.
bool HasReceivedHeader(const GURL& url, const std::string& header) const {
auto it = received_headers_.find(url);
EXPECT_TRUE(it != received_headers_.end());
if (it == received_headers_.end())
return false;
return it->second.find(header) != it->second.end();
}
// Returns the |header| recievced by |url| or nullopt if it hasn't been
// received. Fails an EXPECT if |url| hasn't been observed.
base::Optional<std::string> GetReceivedHeader(
const GURL& url,
const std::string& header) const {
auto it = received_headers_.find(url);
EXPECT_TRUE(it != received_headers_.end());
if (it == received_headers_.end())
return base::nullopt;
auto it2 = it->second.find(header);
if (it2 == it->second.end())
return base::nullopt;
return it2->second;
}
void ClearReceivedHeaders() { received_headers_.clear(); }
bool FetchResource(Browser* browser, const GURL& url) {
if (!url.is_valid())
return false;
std::string script(
"var xhr = new XMLHttpRequest();"
"xhr.open('GET', '");
script += url.spec() +
"', true);"
"xhr.onload = function (e) {"
" if (xhr.readyState === 4) {"
" window.domAutomationController.send(xhr.status === 200);"
" }"
"};"
"xhr.onerror = function () {"
" window.domAutomationController.send(false);"
"};"
"xhr.send(null)";
return ExecuteScript(browser, script);
}
content::WebContents* GetWebContents() { return GetWebContents(browser()); }
content::WebContents* GetWebContents(Browser* browser) {
return browser->tab_strip_model()->GetActiveWebContents();
}
// Registers a service worker for google.com root scope.
void RegisterServiceWorker(const std::string& worker_path) {
GURL url =
GetGoogleUrlWithPath("/service_worker/create_service_worker.html");
EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
EXPECT_EQ("DONE", EvalJs(GetWebContents(),
base::StringPrintf("register('%s', '/');",
worker_path.c_str())));
}
// Registers the given service worker for google.com then tests navigation and
// subresource requests through the worker have X-Client-Data when
// appropriate.
void ServiceWorkerTest(const std::string& worker_path) {
RegisterServiceWorker(worker_path);
// Navigate to a Google URL.
GURL page_url =
GetGoogleUrlWithPath("/service_worker/fetch_from_page.html");
ui_test_utils::NavigateToURL(browser(), page_url);
EXPECT_TRUE(HasReceivedHeader(page_url, "X-Client-Data"));
// Check that there is a controller to check that the test is really testing
// service worker.
EXPECT_EQ(true,
EvalJs(GetWebContents(), "!!navigator.serviceWorker.controller"));
// Verify subresource requests from the page also have X-Client-Data.
EXPECT_EQ("hello", EvalJs(GetWebContents(),
base::StrCat({"fetch_from_page('",
GetGoogleUrl().spec(), "');"})));
EXPECT_TRUE(HasReceivedHeader(GetGoogleUrl(), "X-Client-Data"));
// But not if they are to non-Google domains.
EXPECT_EQ("hello", EvalJs(GetWebContents(),
base::StrCat({"fetch_from_page('",
GetExampleUrl().spec(), "');"})));
EXPECT_FALSE(HasReceivedHeader(GetExampleUrl(), "X-Client-Data"));
}
// Creates a worker and tests that the main script and import scripts have
// X-Client-Data when appropriate. |page| is the page that creates the
// specified |worker|, which should be an "import_*_worker.js" script that is
// expected to import "empty.js" (as a relative path) and also accept an
// "import=" parameter specifying another script to import. This allows
// testing that the empty.js import request for google.com has the header, and
// an import request to example.com does not have the header.
void WorkerScriptTest(const std::string& page, const std::string& worker) {
// Build a worker URL for a google.com worker that imports
// an example.com script.
GURL absolute_import = GetExampleUrlWithPath("/workers/empty.js");
const std::string worker_path = base::StrCat(
{worker, "?import=",
net::EscapeQueryParamValue(absolute_import.spec(), false)});
GURL worker_url = GetGoogleUrlWithPath(worker_path);
// Build the page URL that tells the page to create the worker.
const std::string page_path = base::StrCat(
{page,
"?worker_url=", net::EscapeQueryParamValue(worker_url.spec(), false)});
GURL page_url = GetGoogleUrlWithPath(page_path);
// Navigate and test.
EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), page_url));
EXPECT_EQ("DONE", EvalJs(GetWebContents(), "waitForMessage();"));
// The header should be on the main script request.
EXPECT_TRUE(HasReceivedHeader(worker_url, "X-Client-Data"));
// And on import script requests to Google.
EXPECT_TRUE(HasReceivedHeader(GetGoogleUrlWithPath("/workers/empty.js"),
"X-Client-Data"));
// But not on requests not to Google.
EXPECT_FALSE(HasReceivedHeader(absolute_import, "X-Client-Data"));
}
private:
bool ExecuteScript(Browser* browser, const std::string& script) {
bool xhr_result = false;
// The JS call will fail if disallowed because the process will be killed.
bool execute_result = ExecuteScriptAndExtractBool(GetWebContents(browser),
script, &xhr_result);
return xhr_result && execute_result;
}
// Custom request handler that record request headers and simulates a redirect
// from google.com to example.com.
std::unique_ptr<net::test_server::HttpResponse> RequestHandler(
const net::test_server::HttpRequest& request);
net::EmbeddedTestServer https_server_;
// Stores the observed HTTP Request headers.
std::map<GURL, net::test_server::HttpRequest::HeaderMap> received_headers_;
// For waiting for requests.
std::map<GURL, base::OnceClosure> done_callbacks_;
DISALLOW_COPY_AND_ASSIGN(VariationsHttpHeadersBrowserTest);
};
// Used for testing the kRestrictGoogleWebVisibility feature.
class VariationsHttpHeadersBrowserTestWithRestrictedVisibility
: public VariationsHttpHeadersBrowserTest,
public testing::WithParamInterface<bool> {
public:
VariationsHttpHeadersBrowserTestWithRestrictedVisibility() {
if (GetParam()) {
scoped_feature_list_.InitAndEnableFeature(
variations::internal::kRestrictGoogleWebVisibility);
} else {
scoped_feature_list_.InitAndDisableFeature(
variations::internal::kRestrictGoogleWebVisibility);
}
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
DISALLOW_COPY_AND_ASSIGN(
VariationsHttpHeadersBrowserTestWithRestrictedVisibility);
};
std::unique_ptr<net::test_server::HttpResponse>
VariationsHttpHeadersBrowserTest::RequestHandler(
const net::test_server::HttpRequest& request) {
// Retrieve the host name (without port) from the request headers.
std::string host;
if (request.headers.find("Host") != request.headers.end())
host = request.headers.find("Host")->second;
if (host.find(':') != std::string::npos)
host = host.substr(0, host.find(':'));
// Recover the original URL of the request by replacing the host name in
// request.GetURL() (which is 127.0.0.1) with the host name from the request
// headers.
url::Replacements<char> replacements;
replacements.SetHost(host.c_str(), url::Component(0, host.length()));
GURL original_url = request.GetURL().ReplaceComponents(replacements);
// Memorize the request headers for this URL for later verification.
received_headers_[original_url] = request.headers;
auto iter = done_callbacks_.find(original_url);
if (iter != done_callbacks_.end()) {
std::move(iter->second).Run();
}
// Set up a test server that redirects according to the
// following redirect chain:
// https://www.google.com:<port>/redirect
// --> https://www.google.com:<port>/redirect2
// --> https://www.example.com:<port>/
auto http_response = std::make_unique<net::test_server::BasicHttpResponse>();
http_response->AddCustomHeader("Access-Control-Allow-Origin", "*");
if (request.relative_url == GetGoogleRedirectUrl1().path()) {
http_response->set_code(net::HTTP_MOVED_PERMANENTLY);
http_response->AddCustomHeader("Location", GetGoogleRedirectUrl2().spec());
} else if (request.relative_url == GetGoogleRedirectUrl2().path()) {
http_response->set_code(net::HTTP_MOVED_PERMANENTLY);
http_response->AddCustomHeader("Location", GetExampleUrl().spec());
} else if (request.relative_url == GetExampleUrl().path()) {
http_response->set_code(net::HTTP_OK);
http_response->set_content("hello");
http_response->set_content_type("text/plain");
} else if (request.relative_url == GetGoogleSubresourceUrl().path()) {
http_response->set_code(net::HTTP_OK);
http_response->set_content("");
http_response->set_content_type("image/png");
} else {
return nullptr;
}
return http_response;
}
scoped_refptr<base::FieldTrial> CreateTrialAndAssociateId(
const std::string& trial_name,
const std::string& default_group_name,
variations::IDCollectionKey key,
variations::VariationID id) {
AssociateGoogleVariationID(key, trial_name, default_group_name, id);
scoped_refptr<base::FieldTrial> trial(
base::FieldTrialList::CreateFieldTrial(trial_name, default_group_name));
EXPECT_TRUE(trial);
if (trial) {
// Ensure the trial is registered under the correct key so we can look it
// up.
trial->group();
}
return trial;
}
// Sets up a FieldTrial for Google properites when signed in.
void CreateGoogleSignedInFieldTrial() {
const std::string default_name = "default";
scoped_refptr<base::FieldTrial> trial_1(CreateTrialAndAssociateId(
"t1", default_name, variations::GOOGLE_WEB_PROPERTIES_SIGNED_IN, 123));
auto* provider = variations::VariationsIdsProvider::GetInstance();
variations::mojom::VariationsHeadersPtr signed_in_headers =
provider->GetClientDataHeaders(/*is_signed_in=*/true);
variations::mojom::VariationsHeadersPtr signed_out_headers =
provider->GetClientDataHeaders(/*is_signed_in=*/false);
EXPECT_NE(signed_in_headers->headers_map.at(
variations::mojom::GoogleWebVisibility::ANY),
signed_out_headers->headers_map.at(
variations::mojom::GoogleWebVisibility::ANY));
EXPECT_NE(signed_in_headers->headers_map.at(
variations::mojom::GoogleWebVisibility::FIRST_PARTY),
signed_out_headers->headers_map.at(
variations::mojom::GoogleWebVisibility::FIRST_PARTY));
}
// Creates FieldTrials associatedd with the FIRST_PARTY IDCollectionKeys and
// their corresponding ANY_CONTEXT keys.
void CreateFieldTrialsWithDifferentVisibilities() {
scoped_refptr<base::FieldTrial> trial_1(CreateTrialAndAssociateId(
"t1", "g1", variations::GOOGLE_WEB_PROPERTIES_ANY_CONTEXT, 11));
scoped_refptr<base::FieldTrial> trial_2(CreateTrialAndAssociateId(
"t2", "g2", variations::GOOGLE_WEB_PROPERTIES_FIRST_PARTY, 22));
scoped_refptr<base::FieldTrial> trial_3(CreateTrialAndAssociateId(
"t3", "g3", variations::GOOGLE_WEB_PROPERTIES_TRIGGER_ANY_CONTEXT, 33));
scoped_refptr<base::FieldTrial> trial_4(CreateTrialAndAssociateId(
"t4", "g4", variations::GOOGLE_WEB_PROPERTIES_TRIGGER_FIRST_PARTY, 44));
auto* provider = variations::VariationsIdsProvider::GetInstance();
variations::mojom::VariationsHeadersPtr signed_in_headers =
provider->GetClientDataHeaders(/*is_signed_in=*/true);
variations::mojom::VariationsHeadersPtr signed_out_headers =
provider->GetClientDataHeaders(/*is_signed_in=*/false);
if (base::FeatureList::IsEnabled(
variations::internal::kRestrictGoogleWebVisibility)) {
EXPECT_NE(signed_in_headers->headers_map.at(
variations::mojom::GoogleWebVisibility::ANY),
signed_in_headers->headers_map.at(
variations::mojom::GoogleWebVisibility::FIRST_PARTY));
EXPECT_NE(signed_out_headers->headers_map.at(
variations::mojom::GoogleWebVisibility::ANY),
signed_out_headers->headers_map.at(
variations::mojom::GoogleWebVisibility::FIRST_PARTY));
} else {
// When kRestrictGoogleWebVisibility is disabled, the transmission of
// VariationIDs is not restricted. This is the status quo implementation.
//
// This means that IDs associated with the FIRST_PARTY IDCollectionKeys are
// treated as if they were associated with their corresponding ANY_CONTEXT
// IDCollectionKeys. For example, when the feature is disabled, IDs
// associated with GOOGLE_WEB_PROPERTIES_FIRST_PARTY are transmitted when
// IDs associated with GOOGLE_WEB_PROPERTIES_ANY_CONTEXT are.
EXPECT_EQ(signed_in_headers->headers_map.at(
variations::mojom::GoogleWebVisibility::ANY),
signed_in_headers->headers_map.at(
variations::mojom::GoogleWebVisibility::FIRST_PARTY));
EXPECT_EQ(signed_out_headers->headers_map.at(
variations::mojom::GoogleWebVisibility::ANY),
signed_out_headers->headers_map.at(
variations::mojom::GoogleWebVisibility::FIRST_PARTY));
}
}
} // namespace
// Verify in an integration test that the variations header (X-Client-Data) is
// attached to network requests to Google but stripped on redirects.
IN_PROC_BROWSER_TEST_F(VariationsHttpHeadersBrowserTest,
TestStrippingHeadersFromResourceRequest) {
ui_test_utils::NavigateToURL(browser(), GetGoogleRedirectUrl1());
EXPECT_TRUE(HasReceivedHeader(GetGoogleRedirectUrl1(), "X-Client-Data"));
EXPECT_TRUE(HasReceivedHeader(GetGoogleRedirectUrl2(), "X-Client-Data"));
EXPECT_TRUE(HasReceivedHeader(GetExampleUrl(), "Host"));
EXPECT_FALSE(HasReceivedHeader(GetExampleUrl(), "X-Client-Data"));
}
// Verify in an integration that that the variations header (X-Client-Data) is
// correctly attached and stripped from network requests.
IN_PROC_BROWSER_TEST_F(VariationsHttpHeadersBrowserTest,
TestStrippingHeadersFromSubresourceRequest) {
GURL url = server()->GetURL("/simple_page.html");
ui_test_utils::NavigateToURL(browser(), url);
EXPECT_TRUE(FetchResource(browser(), GetGoogleRedirectUrl1()));
EXPECT_TRUE(HasReceivedHeader(GetGoogleRedirectUrl1(), "X-Client-Data"));
EXPECT_TRUE(HasReceivedHeader(GetGoogleRedirectUrl2(), "X-Client-Data"));
EXPECT_TRUE(HasReceivedHeader(GetExampleUrl(), "Host"));
EXPECT_FALSE(HasReceivedHeader(GetExampleUrl(), "X-Client-Data"));
}
IN_PROC_BROWSER_TEST_F(VariationsHttpHeadersBrowserTest, Incognito) {
Browser* incognito = CreateIncognitoBrowser();
ui_test_utils::NavigateToURL(incognito, GetGoogleUrl());
EXPECT_FALSE(HasReceivedHeader(GetGoogleUrl(), "X-Client-Data"));
EXPECT_TRUE(FetchResource(incognito, GetGoogleSubresourceUrl()));
EXPECT_FALSE(HasReceivedHeader(GetGoogleSubresourceUrl(), "X-Client-Data"));
}
IN_PROC_BROWSER_TEST_F(VariationsHttpHeadersBrowserTest, UserSignedIn) {
// Ensure GetClientDataHeader() returns different values when signed in vs
// not signed in.
CreateGoogleSignedInFieldTrial();
// Sign the user in.
signin::MakePrimaryAccountAvailable(
IdentityManagerFactory::GetForProfile(browser()->profile()),
"[email protected]");
ui_test_utils::NavigateToURL(browser(), GetGoogleUrl());
base::Optional<std::string> header =
GetReceivedHeader(GetGoogleUrl(), "X-Client-Data");
ASSERT_TRUE(header);
variations::mojom::VariationsHeadersPtr headers =
variations::VariationsIdsProvider::GetInstance()->GetClientDataHeaders(
/*is_signed_in=*/true);
EXPECT_EQ(*header, headers->headers_map.at(
variations::mojom::GoogleWebVisibility::ANY));
}
IN_PROC_BROWSER_TEST_F(VariationsHttpHeadersBrowserTest, UserNotSignedIn) {
// Ensure GetClientDataHeader() returns different values when signed in vs
// not signed in.
CreateGoogleSignedInFieldTrial();
// By default the user is not signed in.
ui_test_utils::NavigateToURL(browser(), GetGoogleUrl());
base::Optional<std::string> header =
GetReceivedHeader(GetGoogleUrl(), "X-Client-Data");
ASSERT_TRUE(header);
variations::mojom::VariationsHeadersPtr headers =
variations::VariationsIdsProvider::GetInstance()->GetClientDataHeaders(
/*is_signed_in=*/false);
EXPECT_EQ(*header, headers->headers_map.at(
variations::mojom::GoogleWebVisibility::ANY));
}
INSTANTIATE_TEST_SUITE_P(
VariationsHttpHeadersBrowserTest,
VariationsHttpHeadersBrowserTestWithRestrictedVisibility,
testing::Bool());
IN_PROC_BROWSER_TEST_P(VariationsHttpHeadersBrowserTestWithRestrictedVisibility,
TestRestrictGoogleWebVisibilityInThirdPartyContexts) {
// Ensure GetClientDataHeader() returns different values when
// kRestrictGoogleWebVisibility is enabled and the same values otherwise.
CreateFieldTrialsWithDifferentVisibilities();
ui_test_utils::NavigateToURL(browser(), GetGoogleUrl());
base::Optional<std::string> header =
GetReceivedHeader(GetGoogleUrl(), "X-Client-Data");
ASSERT_TRUE(header);
variations::mojom::GoogleWebVisibility web_visibility =
base::FeatureList::IsEnabled(
variations::internal::kRestrictGoogleWebVisibility)
? variations::mojom::GoogleWebVisibility::FIRST_PARTY
: variations::mojom::GoogleWebVisibility::ANY;
variations::mojom::VariationsHeadersPtr headers =
variations::VariationsIdsProvider::GetInstance()->GetClientDataHeaders(
/*is_signed_in=*/false);
EXPECT_EQ(*header, headers->headers_map.at(web_visibility));
}
IN_PROC_BROWSER_TEST_F(
VariationsHttpHeadersBrowserTest,
TestStrippingHeadersFromRequestUsingSimpleURLLoaderWithProfileNetworkContext) {
GURL url = GetGoogleRedirectUrl1();
auto resource_request = std::make_unique<network::ResourceRequest>();
resource_request->url = url;
std::unique_ptr<network::SimpleURLLoader> loader =
variations::CreateSimpleURLLoaderWithVariationsHeaderUnknownSignedIn(
std::move(resource_request), variations::InIncognito::kNo,
TRAFFIC_ANNOTATION_FOR_TESTS);
content::StoragePartition* partition =
content::BrowserContext::GetDefaultStoragePartition(browser()->profile());
network::SharedURLLoaderFactory* loader_factory =
partition->GetURLLoaderFactoryForBrowserProcess().get();
content::SimpleURLLoaderTestHelper loader_helper;
loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
loader_factory, loader_helper.GetCallback());
// Wait for the response to complete.
loader_helper.WaitForCallback();
EXPECT_EQ(net::OK, loader->NetError());
EXPECT_TRUE(loader_helper.response_body());
EXPECT_TRUE(HasReceivedHeader(GetGoogleRedirectUrl1(), "X-Client-Data"));
EXPECT_TRUE(HasReceivedHeader(GetGoogleRedirectUrl2(), "X-Client-Data"));
EXPECT_TRUE(HasReceivedHeader(GetExampleUrl(), "Host"));
EXPECT_FALSE(HasReceivedHeader(GetExampleUrl(), "X-Client-Data"));
}
IN_PROC_BROWSER_TEST_F(
VariationsHttpHeadersBrowserTest,
TestStrippingHeadersFromRequestUsingSimpleURLLoaderWithGlobalSystemNetworkContext) {
GURL url = GetGoogleRedirectUrl1();
auto resource_request = std::make_unique<network::ResourceRequest>();
resource_request->url = url;
std::unique_ptr<network::SimpleURLLoader> loader =
variations::CreateSimpleURLLoaderWithVariationsHeaderUnknownSignedIn(
std::move(resource_request), variations::InIncognito::kNo,
TRAFFIC_ANNOTATION_FOR_TESTS);
network::SharedURLLoaderFactory* loader_factory =
g_browser_process->system_network_context_manager()
->GetSharedURLLoaderFactory()
.get();
content::SimpleURLLoaderTestHelper loader_helper;
loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
loader_factory, loader_helper.GetCallback());
// Wait for the response to complete.
loader_helper.WaitForCallback();
EXPECT_EQ(net::OK, loader->NetError());
EXPECT_TRUE(loader_helper.response_body());
EXPECT_TRUE(HasReceivedHeader(GetGoogleRedirectUrl1(), "X-Client-Data"));
EXPECT_TRUE(HasReceivedHeader(GetGoogleRedirectUrl2(), "X-Client-Data"));
EXPECT_TRUE(HasReceivedHeader(GetExampleUrl(), "Host"));
EXPECT_FALSE(HasReceivedHeader(GetExampleUrl(), "X-Client-Data"));
}
// Verify in an integration test that the variations header (X-Client-Data) is
// attached to service worker navigation preload requests. Regression test
// for https://crbug.com/873061.
IN_PROC_BROWSER_TEST_F(VariationsHttpHeadersBrowserTest,
ServiceWorkerNavigationPreload) {
// Register a service worker that uses navigation preload.
RegisterServiceWorker("/service_worker/navigation_preload_worker.js");
// Verify "X-Client-Data" is present on the navigation to Google.
// Also test that "Service-Worker-Navigation-Preload" is present to verify
// we are really testing the navigation preload request.
ui_test_utils::NavigateToURL(browser(), GetGoogleUrl());
EXPECT_TRUE(HasReceivedHeader(GetGoogleUrl(), "X-Client-Data"));
EXPECT_TRUE(
HasReceivedHeader(GetGoogleUrl(), "Service-Worker-Navigation-Preload"));
}
// Verify in an integration test that the variations header (X-Client-Data) is
// attached to requests after the service worker falls back to network.
IN_PROC_BROWSER_TEST_F(VariationsHttpHeadersBrowserTest,
ServiceWorkerNetworkFallback) {
ServiceWorkerTest("/service_worker/network_fallback_worker.js");
}
// Verify in an integration test that the variations header (X-Client-Data) is
// attached to requests after the service worker does
// respondWith(fetch(request)).
IN_PROC_BROWSER_TEST_F(VariationsHttpHeadersBrowserTest,
ServiceWorkerRespondWithFetch) {
ServiceWorkerTest("/service_worker/respond_with_fetch_worker.js");
}
// Verify in an integration test that the variations header (X-Client-Data) is
// attached to requests for service worker scripts when installing and updating.
IN_PROC_BROWSER_TEST_F(VariationsHttpHeadersBrowserTest, ServiceWorkerScript) {
// Register a service worker that imports scripts.
GURL absolute_import = GetExampleUrlWithPath("/service_worker/empty.js");
const std::string worker_path =
"/service_worker/import_scripts_worker.js?import=" +
net::EscapeQueryParamValue(absolute_import.spec(), false);
RegisterServiceWorker(worker_path);
// Test that the header is present on the main script request.
EXPECT_TRUE(
HasReceivedHeader(GetGoogleUrlWithPath(worker_path), "X-Client-Data"));
// And on import script requests to Google.
EXPECT_TRUE(HasReceivedHeader(
GetGoogleUrlWithPath("/service_worker/empty.js"), "X-Client-Data"));
// But not on requests not to Google.
EXPECT_FALSE(HasReceivedHeader(absolute_import, "X-Client-Data"));
// Prepare for the update case.
ClearReceivedHeaders();
// Tries to update the service worker.
EXPECT_EQ("DONE", EvalJs(GetWebContents(), "update();"));
// Test that the header is present on the main script request.
EXPECT_TRUE(
HasReceivedHeader(GetGoogleUrlWithPath(worker_path), "X-Client-Data"));
// And on import script requests to Google.
EXPECT_TRUE(HasReceivedHeader(
GetGoogleUrlWithPath("/service_worker/empty.js"), "X-Client-Data"));
// But not on requests not to Google.
EXPECT_FALSE(HasReceivedHeader(absolute_import, "X-Client-Data"));
}
// Verify in an integration test that the variations header (X-Client-Data) is
// attached to requests for shared worker scripts.
IN_PROC_BROWSER_TEST_F(VariationsHttpHeadersBrowserTest, SharedWorkerScript) {
WorkerScriptTest("/workers/create_shared_worker.html",
"/workers/import_scripts_shared_worker.js");
}
// Verify in an integration test that the variations header (X-Client-Data) is
// attached to requests for dedicated worker scripts.
IN_PROC_BROWSER_TEST_F(VariationsHttpHeadersBrowserTest,
DedicatedWorkerScript) {
WorkerScriptTest("/workers/create_dedicated_worker.html",
"/workers/import_scripts_dedicated_worker.js");
}
namespace {
// A test fixture for testing prefetches from the Loading Predictor.
class VariationsHttpHeadersBrowserTestWithOptimizationGuide
: public VariationsHttpHeadersBrowserTest {
public:
VariationsHttpHeadersBrowserTestWithOptimizationGuide() {
std::vector<base::test::ScopedFeatureList::FeatureAndParams> enabled = {
{features::kLoadingPredictorPrefetch, {}},
{features::kLoadingPredictorUseOptimizationGuide,
{{"use_predictions_for_preconnect", "true"}}},
{optimization_guide::features::kOptimizationHints, {}}};
std::vector<base::Feature> disabled = {
features::kLoadingPredictorUseLocalPredictions};
feature_list_.InitWithFeaturesAndParameters(enabled, disabled);
}
void SetUpCommandLine(base::CommandLine* command_line) override {
VariationsHttpHeadersBrowserTest::SetUpCommandLine(command_line);
command_line->AppendSwitch(
switches::kLoadingPredictorAllowLocalRequestForTesting);
}
std::unique_ptr<content::TestNavigationManager> NavigateToURLAsync(
const GURL& url) {
chrome::NewTab(browser());
content::WebContents* tab =
browser()->tab_strip_model()->GetActiveWebContents();
DCHECK(tab);
auto observer = std::make_unique<content::TestNavigationManager>(tab, url);
tab->GetController().LoadURL(url, content::Referrer(),
ui::PAGE_TRANSITION_TYPED, std::string());
return observer;
}
void SetUpOptimizationHint(
const GURL& url,
const std::vector<std::string>& predicted_subresource_urls) {
auto* optimization_guide_keyed_service =
OptimizationGuideKeyedServiceFactory::GetForProfile(
browser()->profile());
ASSERT_TRUE(optimization_guide_keyed_service);
optimization_guide::proto::LoadingPredictorMetadata
loading_predictor_metadata;
for (const auto& subresource_url : predicted_subresource_urls) {
loading_predictor_metadata.add_subresources()->set_url(subresource_url);
}
optimization_guide::OptimizationMetadata optimization_metadata;
optimization_metadata.set_loading_predictor_metadata(
loading_predictor_metadata);
optimization_guide_keyed_service->AddHintForTesting(
url, optimization_guide::proto::LOADING_PREDICTOR,
optimization_metadata);
}
private:
base::test::ScopedFeatureList feature_list_;
};
} // namespace
// Verify in an integration test that that the variations header (X-Client-Data)
// is correctly attached to prefetch requests from the Loading Predictor.
IN_PROC_BROWSER_TEST_F(VariationsHttpHeadersBrowserTestWithOptimizationGuide,
Prefetch) {
GURL url = server()->GetURL("test.com", "/simple_page.html");
GURL google_url = GetGoogleSubresourceUrl();
GURL non_google_url = GetExampleUrl();
// Set up optimization hints.
std::vector<std::string> hints = {google_url.spec(), non_google_url.spec()};
SetUpOptimizationHint(url, hints);
// Navigate.
auto observer = NavigateToURLAsync(url);
EXPECT_TRUE(observer->WaitForRequestStart());
WaitForRequest(google_url);
WaitForRequest(non_google_url);
// Expect header on google urls only.
EXPECT_TRUE(HasReceivedHeader(google_url, "X-Client-Data"));
EXPECT_FALSE(HasReceivedHeader(non_google_url, "X-Client-Data"));
}