Commit 58d942fb authored by Alex Clarke's avatar Alex Clarke Committed by Commit Bot

WebLayer to support variations headers for google requests

Based on the foundation from https://crrev.com/c/1967245 this patch
adds support for variations headers to WebLayer along with a simple
browser test.

Bug: 1025612
Change-Id: Ic1dda5ccd2581c15a5d51094ece277e3035a866e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2041663
Commit-Queue: Alex Clarke <alexclarke@chromium.org>
Reviewed-by: default avatarJohn Abd-El-Malek <jam@chromium.org>
Cr-Commit-Position: refs/heads/master@{#745424}
parent b732b43f
......@@ -259,6 +259,7 @@ jumbo_static_library("weblayer_lib") {
"//components/startup_metric_utils/browser",
"//components/user_prefs",
"//components/variations",
"//components/variations/net",
"//components/variations/service",
"//components/version_info",
"//components/web_cache/browser",
......
......@@ -15,6 +15,8 @@
#include "components/safe_browsing/core/common/safe_browsing_prefs.h"
#include "components/security_interstitials/content/stateful_ssl_host_state_delegate.h"
#include "components/user_prefs/user_prefs.h"
#include "components/variations/variations_client.h"
#include "components/variations/variations_http_header_provider.h"
#include "content/public/browser/device_service.h"
#include "content/public/browser/download_request_utils.h"
#include "content/public/browser/resource_context.h"
......@@ -223,4 +225,38 @@ void BrowserContextImpl::RegisterPrefs(
safe_browsing::RegisterProfilePrefs(pref_registry);
}
class BrowserContextImpl::WebLayerVariationsClient
: public variations::VariationsClient {
public:
explicit WebLayerVariationsClient(content::BrowserContext* browser_context)
: browser_context_(browser_context) {}
~WebLayerVariationsClient() override = default;
bool IsIncognito() const override {
return browser_context_->IsOffTheRecord();
}
std::string GetVariationsHeader() const override {
return variations::VariationsHttpHeaderProvider::GetInstance()
->GetClientDataHeader(IsSignedIn());
}
private:
bool IsSignedIn() const {
// TODO(weblayer-dev): Update when signin is supported.
return false;
}
content::BrowserContext* browser_context_;
};
variations::VariationsClient* BrowserContextImpl::GetVariationsClient() {
if (!weblayer_variations_client_) {
weblayer_variations_client_ =
std::make_unique<WebLayerVariationsClient>(this);
}
return weblayer_variations_client_.get();
}
} // namespace weblayer
......@@ -37,6 +37,7 @@ class BrowserContextImpl : public content::BrowserContext {
#endif // !defined(OS_ANDROID)
base::FilePath GetPath() override;
bool IsOffTheRecord() override;
variations::VariationsClient* GetVariationsClient() override;
content::DownloadManagerDelegate* GetDownloadManagerDelegate() override;
content::ResourceContext* GetResourceContext() override;
......@@ -60,6 +61,8 @@ class BrowserContextImpl : public content::BrowserContext {
ProfileImpl* profile_impl() const { return profile_impl_; }
private:
class WebLayerVariationsClient;
// Creates a simple in-memory pref service.
// TODO(timvolodine): Investigate whether WebLayer needs persistent pref
// service.
......@@ -83,6 +86,7 @@ class BrowserContextImpl : public content::BrowserContext {
std::unique_ptr<PrefService> user_pref_service_;
std::unique_ptr<content::PermissionControllerDelegate>
permission_controller_delegate_;
std::unique_ptr<WebLayerVariationsClient> weblayer_variations_client_;
};
} // namespace weblayer
......
......@@ -22,6 +22,7 @@
#include "components/security_interstitials/content/ssl_cert_reporter.h"
#include "components/security_interstitials/content/ssl_error_handler.h"
#include "components/security_interstitials/content/ssl_error_navigation_throttle.h"
#include "components/variations/net/variations_http_headers.h"
#include "components/version_info/version_info.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/devtools_manager_delegate.h"
......@@ -323,6 +324,7 @@ ContentBrowserClientImpl::CreateNetworkContext(
proxy_config,
net::DefineNetworkTrafficAnnotation("undefined", "Nothing here yet."));
}
variations::UpdateCorsExemptHeaderForVariations(context_params.get());
content::GetNetworkService()->CreateNetworkContext(
network_context.BindNewPipeAndPassReceiver(), std::move(context_params));
return network_context;
......
......@@ -5,6 +5,7 @@
#include "weblayer/browser/system_network_context_manager.h"
#include "build/build_config.h"
#include "components/variations/net/variations_http_headers.h"
#include "content/public/browser/network_service_instance.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
......@@ -20,6 +21,7 @@ network::mojom::NetworkContextParamsPtr CreateDefaultNetworkContextParams(
network::mojom::NetworkContextParamsPtr network_context_params =
network::mojom::NetworkContextParams::New();
network_context_params->user_agent = user_agent;
variations::UpdateCorsExemptHeaderForVariations(network_context_params.get());
return network_context_params;
}
......
// Copyright 2020 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 "weblayer/test/weblayer_browser_test.h"
#include "components/variations/variations_http_header_provider.h"
#include "content/public/test/network_connection_change_simulator.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 "weblayer/public/navigation.h"
#include "weblayer/public/navigation_controller.h"
#include "weblayer/public/navigation_observer.h"
#include "weblayer/public/tab.h"
#include "weblayer/shell/browser/shell.h"
#include "weblayer/test/weblayer_browser_test_utils.h"
namespace weblayer {
namespace {
class OneShotNavigationObserver : public NavigationObserver {
public:
explicit OneShotNavigationObserver(Shell* shell) : tab_(shell->tab()) {
tab_->GetNavigationController()->AddObserver(this);
}
~OneShotNavigationObserver() override {
tab_->GetNavigationController()->RemoveObserver(this);
}
void WaitForNavigation() { run_loop_.Run(); }
bool completed() { return completed_; }
bool is_error_page() { return is_error_page_; }
Navigation::LoadError load_error() { return load_error_; }
int http_status_code() { return http_status_code_; }
NavigationState navigation_state() { return navigation_state_; }
private:
// NavigationObserver implementation:
void NavigationCompleted(Navigation* navigation) override {
completed_ = true;
Finish(navigation);
}
void NavigationFailed(Navigation* navigation) override { Finish(navigation); }
void Finish(Navigation* navigation) {
is_error_page_ = navigation->IsErrorPage();
load_error_ = navigation->GetLoadError();
http_status_code_ = navigation->GetHttpStatusCode();
navigation_state_ = navigation->GetState();
run_loop_.Quit();
}
base::RunLoop run_loop_;
Tab* tab_;
bool completed_ = false;
bool is_error_page_ = false;
Navigation::LoadError load_error_ = Navigation::kNoError;
int http_status_code_ = 0;
NavigationState navigation_state_ = NavigationState::kWaitingResponse;
};
} // namespace
// The purpose of this test is to verify Variations code is correctly wired up
// for WebLayer. It's not intended to replicate VariationsHttpHeadersBrowserTest
class WebLayerVariationsHttpBrowserTest : public WebLayerBrowserTest {
public:
WebLayerVariationsHttpBrowserTest()
: https_server_(net::test_server::EmbeddedTestServer::TYPE_HTTPS) {}
~WebLayerVariationsHttpBrowserTest() override = default;
void SetUp() override {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
// HTTPS server only serves a valid cert for localhost, so this is needed to
// load pages from "www.google.com" without an interstitial.
command_line->AppendSwitch("ignore-certificate-errors");
WebLayerBrowserTest::SetUp();
}
void SetUpOnMainThread() override {
auto* variations_provider =
variations::VariationsHttpHeaderProvider::GetInstance();
variations_provider->ForceVariationIds({"12", "456", "t789"}, "");
// The test makes requests to google.com which we want to redirect to the
// test server.
host_resolver()->AddRule("*", "127.0.0.1");
https_server_.RegisterRequestHandler(
base::BindRepeating(&WebLayerVariationsHttpBrowserTest::RequestHandler,
base::Unretained(this)));
ASSERT_TRUE(https_server_.Start());
}
GURL GetGoogleUrlWithPath(const std::string& path) const {
return https_server_.GetURL("www.google.com", path);
}
GURL GetGoogleRedirectUrl1() const {
return GetGoogleUrlWithPath("/redirect");
}
GURL GetGoogleRedirectUrl2() const {
return GetGoogleUrlWithPath("/redirect2");
}
GURL GetExampleUrlWithPath(const std::string& path) const {
return https_server_.GetURL("www.example.com", path);
}
GURL GetExampleUrl() const { return GetExampleUrlWithPath("/landing.html"); }
// 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();
}
std::unique_ptr<net::test_server::HttpResponse> 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;
// 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 {
return nullptr;
}
return http_response;
}
protected:
net::EmbeddedTestServer https_server_;
// Stores the observed HTTP Request headers.
std::map<GURL, net::test_server::HttpRequest::HeaderMap> received_headers_;
};
// 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(WebLayerVariationsHttpBrowserTest,
TestStrippingHeadersFromResourceRequest) {
OneShotNavigationObserver observer(shell());
shell()->tab()->GetNavigationController()->Navigate(GetGoogleRedirectUrl1());
observer.WaitForNavigation();
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"));
}
} // namespace weblayer
......@@ -94,6 +94,7 @@ test("weblayer_browsertests") {
"//components/security_interstitials/content:security_interstitial_page",
"//components/sessions:test_support",
"//components/strings",
"//components/variations",
"//content/public/browser",
"//content/test:test_support",
"//net:test_support",
......@@ -108,6 +109,7 @@ test("weblayer_browsertests") {
"../browser/errorpage_browsertest.cc",
"../browser/navigation_browsertest.cc",
"../browser/ssl_browsertest.cc",
"../browser/weblayer_variations_http_browsertest.cc",
"../browser/webui/webui_browsertest.cc",
"browsertests_main.cc",
"interstitial_utils.cc",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment