| // Copyright 2018 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 <tuple> |
| |
| #include "base/bind.h" |
| #include "base/metrics/field_trial_param_associator.h" |
| #include "base/strings/strcat.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_split.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/synchronization/lock.h" |
| #include "base/test/bind_test_util.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings.h" |
| #include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings_factory.h" |
| #include "chrome/browser/metrics/subprocess_metrics_provider.h" |
| #include "chrome/browser/page_load_metrics/page_load_metrics_test_waiter.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/browser.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_config_service_client_test_utils.h" |
| #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_pingback_client.h" |
| #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_service.h" |
| #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_util.h" |
| #include "components/data_reduction_proxy/core/common/data_reduction_proxy_features.h" |
| #include "components/data_reduction_proxy/core/common/data_reduction_proxy_headers.h" |
| #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h" |
| #include "components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h" |
| #include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h" |
| #include "components/data_reduction_proxy/core/common/uma_util.h" |
| #include "components/data_reduction_proxy/proto/client_config.pb.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/proxy_config/proxy_config_dictionary.h" |
| #include "components/proxy_config/proxy_config_pref_names.h" |
| #include "components/variations/variations_associated_data.h" |
| #include "content/public/browser/browser_context.h" |
| #include "content/public/browser/network_service_instance.h" |
| #include "content/public/browser/storage_partition.h" |
| #include "content/public/common/network_service_util.h" |
| #include "content/public/common/service_manager_connection.h" |
| #include "content/public/common/service_names.mojom.h" |
| #include "content/public/test/browser_test_utils.h" |
| #include "content/public/test/content_browser_test.h" |
| #include "content/public/test/test_navigation_observer.h" |
| #include "net/base/host_port_pair.h" |
| #include "net/dns/mock_host_resolver.h" |
| #include "net/http/http_request_headers.h" |
| #include "net/test/embedded_test_server/controllable_http_response.h" |
| #include "net/test/embedded_test_server/default_handlers.h" |
| #include "net/test/embedded_test_server/embedded_test_server_connection_listener.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/network_switches.h" |
| #include "services/network/public/cpp/simple_url_loader.h" |
| #include "services/network/public/mojom/network_service_test.mojom.h" |
| #include "services/service_manager/public/cpp/connector.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| |
| namespace data_reduction_proxy { |
| namespace { |
| |
| using testing::HasSubstr; |
| using testing::Not; |
| |
| constexpr char kSessionKey[] = "TheSessionKeyYay!"; |
| constexpr char kMockHost[] = "mock.host"; |
| constexpr char kDummyBody[] = "dummy"; |
| constexpr char kPrimaryResponse[] = "primary"; |
| constexpr char kSecondaryResponse[] = "secondary"; |
| |
| std::unique_ptr<net::test_server::HttpResponse> BasicResponse( |
| const std::string& content, |
| const net::test_server::HttpRequest& request) { |
| auto response = std::make_unique<net::test_server::BasicHttpResponse>(); |
| response->set_content(content); |
| response->set_content_type("text/plain"); |
| return response; |
| } |
| |
| std::unique_ptr<net::test_server::HttpResponse> CreateDRPResponseWithHeader( |
| const net::test_server::HttpRequest& request) { |
| auto response = std::make_unique<net::test_server::BasicHttpResponse>(); |
| const auto chrome_proxy_header = request.headers.find("chrome-proxy"); |
| if (chrome_proxy_header != request.headers.end()) |
| response->set_content(chrome_proxy_header->second); |
| response->set_content_type("text/html"); |
| response->AddCustomHeader("chrome-proxy", "ofcl=10"); |
| response->AddCustomHeader("via", "1.1 Chrome-Compression-Proxy"); |
| return response; |
| } |
| |
| std::unique_ptr<net::test_server::HttpResponse> IncrementRequestCount( |
| const std::string& relative_url, |
| int* request_count, |
| const net::test_server::HttpRequest& request) { |
| if (request.relative_url == relative_url) |
| (*request_count)++; |
| return std::make_unique<net::test_server::BasicHttpResponse>(); |
| } |
| |
| // Given a |request| to a proxy server, returns the destination host name. |
| std::string GetDestinationHost(const net::test_server::HttpRequest& request) { |
| const auto it = request.headers.find("Host"); |
| if (it == request.headers.end()) |
| return {}; |
| return it->second; |
| } |
| |
| void SimulateNetworkChange(network::mojom::ConnectionType type) { |
| if (base::FeatureList::IsEnabled(network::features::kNetworkService) && |
| !content::IsInProcessNetworkService()) { |
| network::mojom::NetworkServiceTestPtr network_service_test; |
| content::ServiceManagerConnection::GetForProcess() |
| ->GetConnector() |
| ->BindInterface(content::mojom::kNetworkServiceName, |
| &network_service_test); |
| base::RunLoop run_loop; |
| network_service_test->SimulateNetworkChange(type, run_loop.QuitClosure()); |
| run_loop.Run(); |
| return; |
| } |
| net::NetworkChangeNotifier::NotifyObserversOfNetworkChangeForTests( |
| net::NetworkChangeNotifier::ConnectionType(type)); |
| } |
| |
| ClientConfig CreateConfigForServer(const net::EmbeddedTestServer& server) { |
| net::HostPortPair host_port_pair = server.host_port_pair(); |
| return CreateConfig(kSessionKey, 1000, 0, ProxyServer_ProxyScheme_HTTP, |
| host_port_pair.host(), host_port_pair.port(), |
| ProxyServer_ProxyScheme_HTTP, "fallback.net", 80, 0.5f, |
| false); |
| } |
| |
| ClientConfig CreateEmptyConfig() { |
| return CreateEmptyProxyConfig(kSessionKey, 1000, 0, 0.5f, false); |
| } |
| |
| } // namespace |
| |
| #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_CHROMEOS) |
| #define DISABLE_ON_WIN_MAC_CHROMEOS(x) DISABLED_##x |
| #else |
| #define DISABLE_ON_WIN_MAC_CHROMEOS(x) x |
| #endif |
| |
| class DataReductionProxyBrowsertestBase : public InProcessBrowserTest { |
| public: |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| command_line->AppendSwitchASCII( |
| network::switches::kForceEffectiveConnectionType, "4G"); |
| |
| secure_proxy_check_server_.RegisterRequestHandler( |
| base::BindRepeating(&BasicResponse, "OK")); |
| ASSERT_TRUE(secure_proxy_check_server_.Start()); |
| command_line->AppendSwitchASCII( |
| switches::kDataReductionProxySecureProxyCheckURL, |
| secure_proxy_check_server_.base_url().spec()); |
| |
| config_server_.RegisterRequestHandler(base::BindRepeating( |
| &DataReductionProxyBrowsertestBase::GetConfigResponse, |
| base::Unretained(this))); |
| ASSERT_TRUE(config_server_.Start()); |
| command_line->AppendSwitchASCII(switches::kDataReductionProxyConfigURL, |
| config_server_.base_url().spec()); |
| } |
| |
| void SetUp() override { |
| scoped_feature_list_.InitAndEnableFeature( |
| features::kDataReductionProxyEnabledWithNetworkService); |
| InProcessBrowserTest::SetUp(); |
| } |
| |
| void SetUpOnMainThread() override { |
| // Make sure the favicon doesn't mess with the tests. |
| favicon_catcher_ = |
| std::make_unique<net::test_server::ControllableHttpResponse>( |
| embedded_test_server(), "/favicon.ico"); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| // Set a default proxy config if one isn't set yet. |
| if (!config_.has_proxy_config()) |
| SetConfig(CreateConfigForServer(*embedded_test_server())); |
| |
| host_resolver()->AddRule(kMockHost, "127.0.0.1"); |
| |
| EnableDataSaver(true); |
| // Make sure initial config has been loaded. Config is fetched only if |
| // network service is not enabled, or if both network service and |
| // kDataReductionProxyEnabledWithNetworkService are enabled. |
| if (!IsNetworkServiceEnabled() || |
| base::FeatureList::IsEnabled( |
| data_reduction_proxy::features:: |
| kDataReductionProxyEnabledWithNetworkService)) { |
| WaitForConfig(); |
| } |
| } |
| |
| bool IsNetworkServiceEnabled() const { |
| return base::FeatureList::IsEnabled(network::features::kNetworkService); |
| } |
| |
| // Verifies that the |request| has the Chrome-Proxy headers, and caches the |
| // URL of |request| in a local container. This can be added to any test that |
| // needs to verify if the proxy server is receiving the correct set of |
| // headers. |
| void MonitorAndVerifyRequestsToProxyServer( |
| const net::test_server::HttpRequest& request) { |
| ++count_proxy_server_requests_received_; |
| // All requests to proxy server should have at least these headers. |
| EXPECT_NE(request.headers.end(), |
| request.headers.find(data_reduction_proxy::chrome_proxy_header())) |
| << " url=" << request.GetURL() << " path=" << request.GetURL().path(); |
| |
| VerifyChromeProxyRequestHeader( |
| request.headers.at(data_reduction_proxy::chrome_proxy_header())); |
| |
| EXPECT_NE( |
| request.headers.end(), |
| request.headers.find(data_reduction_proxy::chrome_proxy_ect_header())) |
| << " url=" << request.GetURL() << " path=" << request.GetURL().path(); |
| |
| base::AutoLock lock(lock_); |
| monitored_urls_.push_back(request.GetURL()); |
| } |
| |
| void VerifyChromeProxyRequestHeader( |
| const std::string& chrome_proxy_header_value) const { |
| bool exp_found = false; |
| for (const auto& attributes : base::SplitStringPiece( |
| chrome_proxy_header_value, ",", base::TRIM_WHITESPACE, |
| base::SPLIT_WANT_NONEMPTY)) { |
| if (base::StartsWith(attributes, |
| "exp=", base::CompareCase::INSENSITIVE_ASCII)) { |
| const auto attribute_split = base::SplitStringPiece( |
| attributes, "=", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); |
| EXPECT_EQ(2u, attribute_split.size()); |
| EXPECT_EQ(expect_exp_value_in_request_header_, attribute_split[1]); |
| exp_found = true; |
| } |
| } |
| EXPECT_EQ(!expect_exp_value_in_request_header_.empty(), exp_found) |
| << " expect_exp_value_in_request_header_=" |
| << expect_exp_value_in_request_header_; |
| } |
| |
| // Returns true if a request for URL with path |url_path| was observed by |
| // |this|. The method only compares the path instead of the full URL since the |
| // hostname may be different due to the use of mock host in the browsertests |
| // below. |
| bool WasUrlPathMonitored(const std::string& url_path) { |
| base::AutoLock lock(lock_); |
| for (const auto monitored_url : monitored_urls_) |
| if (monitored_url.path() == url_path) |
| return true; |
| |
| return false; |
| } |
| |
| void ResetMonitoredUrls() { |
| base::AutoLock lock(lock_); |
| monitored_urls_.clear(); |
| } |
| |
| protected: |
| void EnableDataSaver(bool enabled) { |
| data_reduction_proxy::DataReductionProxySettings:: |
| SetDataSaverEnabledForTesting(browser()->profile()->GetPrefs(), |
| enabled); |
| } |
| |
| std::string GetBody() { return GetBody(browser()); } |
| |
| std::string GetBody(Browser* browser) { |
| std::string body; |
| EXPECT_TRUE(content::ExecuteScriptAndExtractString( |
| browser->tab_strip_model()->GetActiveWebContents(), |
| "window.domAutomationController.send(document.body.textContent);", |
| &body)); |
| return body; |
| } |
| |
| GURL GetURLWithMockHost(const net::EmbeddedTestServer& server, |
| const std::string& relative_url) { |
| GURL server_base_url = server.base_url(); |
| GURL base_url = |
| GURL(base::StrCat({server_base_url.scheme(), "://", kMockHost, ":", |
| server_base_url.port()})); |
| EXPECT_TRUE(base_url.is_valid()) << base_url.possibly_invalid_spec(); |
| return base_url.Resolve(relative_url); |
| } |
| |
| void SetConfig(const ClientConfig& config) { |
| config_run_loop_ = std::make_unique<base::RunLoop>(); |
| config_ = config; |
| } |
| |
| void WaitForConfig() { config_run_loop_->Run(); } |
| |
| std::string expect_exp_value_in_request_header_; |
| |
| size_t count_proxy_server_requests_received_ = 0u; |
| |
| private: |
| std::unique_ptr<net::test_server::HttpResponse> GetConfigResponse( |
| const net::test_server::HttpRequest& request) { |
| auto response = std::make_unique<net::test_server::BasicHttpResponse>(); |
| response->set_content(config_.SerializeAsString()); |
| response->set_content_type("text/plain"); |
| if (config_run_loop_) |
| config_run_loop_->Quit(); |
| return response; |
| } |
| |
| ClientConfig config_; |
| std::unique_ptr<base::RunLoop> config_run_loop_; |
| base::test::ScopedFeatureList scoped_feature_list_; |
| base::test::ScopedFeatureList param_feature_list_; |
| net::EmbeddedTestServer secure_proxy_check_server_; |
| net::EmbeddedTestServer config_server_; |
| std::unique_ptr<net::test_server::ControllableHttpResponse> favicon_catcher_; |
| |
| std::vector<GURL> monitored_urls_; |
| |
| // |lock_| guards access to |monitored_urls_|. |
| base::Lock lock_; |
| }; |
| |
| class DataReductionProxyBrowsertest : public DataReductionProxyBrowsertestBase { |
| public: |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| DataReductionProxyBrowsertestBase::SetUpCommandLine(command_line); |
| command_line->AppendSwitch( |
| switches::kDisableDataReductionProxyWarmupURLFetch); |
| } |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(DataReductionProxyBrowsertest, UpdateConfig) { |
| net::EmbeddedTestServer original_server; |
| original_server.RegisterRequestHandler( |
| base::BindRepeating(&BasicResponse, kPrimaryResponse)); |
| ASSERT_TRUE(original_server.Start()); |
| |
| SetConfig(CreateConfigForServer(original_server)); |
| // A network change forces the config to be fetched. |
| SimulateNetworkChange(network::mojom::ConnectionType::CONNECTION_3G); |
| WaitForConfig(); |
| |
| ui_test_utils::NavigateToURL( |
| browser(), |
| GetURLWithMockHost(original_server, "/echoheader?Chrome-Proxy")); |
| |
| EXPECT_EQ(GetBody(), kPrimaryResponse); |
| |
| net::EmbeddedTestServer new_server; |
| new_server.RegisterRequestHandler( |
| base::BindRepeating(&BasicResponse, kSecondaryResponse)); |
| ASSERT_TRUE(new_server.Start()); |
| |
| SetConfig(CreateConfigForServer(new_server)); |
| // A network change forces the config to be fetched. |
| SimulateNetworkChange(network::mojom::ConnectionType::CONNECTION_2G); |
| WaitForConfig(); |
| |
| ui_test_utils::NavigateToURL( |
| browser(), |
| GetURLWithMockHost(original_server, "/echoheader?Chrome-Proxy")); |
| |
| EXPECT_EQ(GetBody(), kSecondaryResponse); |
| } |
| |
| // Verify that when a client config with no proxies is provided to Chrome, |
| // then usage of proxy is disabled. Later, when the client config with valid |
| // proxies is fetched, then the specified proxies are used. |
| IN_PROC_BROWSER_TEST_F(DataReductionProxyBrowsertest, EmptyConfig) { |
| net::EmbeddedTestServer origin_server; |
| origin_server.RegisterRequestHandler( |
| base::BindRepeating(&BasicResponse, kDummyBody)); |
| ASSERT_TRUE(origin_server.Start()); |
| |
| // Set config to empty, and verify that the response comes from the |
| // |origin_server|. |
| SetConfig(CreateEmptyConfig()); |
| // A network change forces the config to be fetched. |
| SimulateNetworkChange(network::mojom::ConnectionType::CONNECTION_2G); |
| WaitForConfig(); |
| ui_test_utils::NavigateToURL( |
| browser(), GetURLWithMockHost(origin_server, "/echoheader?Chrome-Proxy")); |
| ASSERT_EQ(GetBody(), kDummyBody); |
| |
| net::EmbeddedTestServer proxy_server; |
| proxy_server.RegisterRequestHandler( |
| base::BindRepeating(&BasicResponse, kPrimaryResponse)); |
| ASSERT_TRUE(proxy_server.Start()); |
| |
| // Set config to |proxy_server|, and verify that the response comes from |
| // |proxy_server|. |
| SetConfig(CreateConfigForServer(proxy_server)); |
| // A network change forces the config to be fetched. |
| SimulateNetworkChange(network::mojom::ConnectionType::CONNECTION_3G); |
| WaitForConfig(); |
| ui_test_utils::NavigateToURL( |
| browser(), GetURLWithMockHost(origin_server, "/echoheader?Chrome-Proxy")); |
| EXPECT_EQ(GetBody(), kPrimaryResponse); |
| |
| // Set config to empty again, and verify that the response comes from the |
| // |origin_server|. |
| SetConfig(CreateEmptyConfig()); |
| // A network change forces the config to be fetched. |
| SimulateNetworkChange(network::mojom::ConnectionType::CONNECTION_2G); |
| WaitForConfig(); |
| ui_test_utils::NavigateToURL( |
| browser(), GetURLWithMockHost(origin_server, "/echoheader?Chrome-Proxy")); |
| ASSERT_EQ(GetBody(), kDummyBody); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DataReductionProxyBrowsertest, ChromeProxyHeaderSet) { |
| // Proxy will be used, so it shouldn't matter if the host cannot be resolved. |
| ui_test_utils::NavigateToURL( |
| browser(), GURL("http://does.not.resolve/echoheader?Chrome-Proxy")); |
| |
| std::string body = GetBody(); |
| EXPECT_THAT(body, HasSubstr(kSessionKey)); |
| EXPECT_THAT(body, HasSubstr("pid=")); |
| EXPECT_THAT(body, Not(HasSubstr("pid=0"))); |
| EXPECT_THAT(body, HasSubstr("s=")); |
| EXPECT_THAT(body, HasSubstr("c=")); |
| EXPECT_THAT(body, HasSubstr("b=")); |
| EXPECT_THAT(body, HasSubstr("p=")); |
| } |
| |
| // Gets the response body for an XHR to |url| (as seen by the renderer). |
| std::string ReadSubresourceFromRenderer(Browser* browser, const GURL& url) { |
| std::string script = R"((url => { |
| var xhr = new XMLHttpRequest(); |
| xhr.open('GET', url, true); |
| xhr.onload = () => domAutomationController.send(xhr.responseText); |
| xhr.send(); |
| }))"; |
| std::string result; |
| EXPECT_TRUE(ExecuteScriptAndExtractString( |
| browser->tab_strip_model()->GetActiveWebContents(), |
| script + "('" + url.spec() + "')", &result)); |
| return result; |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DataReductionProxyBrowsertest, DisabledOnIncognito) { |
| net::EmbeddedTestServer test_server; |
| test_server.RegisterRequestHandler( |
| base::BindRepeating(&BasicResponse, kDummyBody)); |
| ASSERT_TRUE(test_server.Start()); |
| |
| Browser* incognito = CreateIncognitoBrowser(); |
| ui_test_utils::NavigateToURL( |
| incognito, GetURLWithMockHost(test_server, "/echoheader?Chrome-Proxy")); |
| EXPECT_EQ(GetBody(incognito), kDummyBody); |
| |
| // Make sure subresource doesn't use DRP either. |
| std::string result = ReadSubresourceFromRenderer( |
| incognito, GetURLWithMockHost(test_server, "/echoheader?Chrome-Proxy")); |
| |
| EXPECT_EQ(result, kDummyBody); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DataReductionProxyBrowsertest, |
| ChromeProxyHeaderSetForSubresource) { |
| net::EmbeddedTestServer test_server; |
| test_server.RegisterRequestHandler( |
| base::BindRepeating(&BasicResponse, kDummyBody)); |
| ASSERT_TRUE(test_server.Start()); |
| |
| ui_test_utils::NavigateToURL(browser(), |
| GetURLWithMockHost(test_server, "/echo")); |
| |
| std::string result = ReadSubresourceFromRenderer( |
| browser(), GetURLWithMockHost(test_server, "/echoheader?Chrome-Proxy")); |
| |
| EXPECT_THAT(result, HasSubstr(kSessionKey)); |
| EXPECT_THAT(result, Not(HasSubstr("pid="))); |
| EXPECT_THAT(result, HasSubstr("s=")); |
| EXPECT_THAT(result, HasSubstr("c=")); |
| EXPECT_THAT(result, HasSubstr("b=")); |
| EXPECT_THAT(result, HasSubstr("p=")); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DataReductionProxyBrowsertest, ChromeProxyEctHeaderSet) { |
| // Proxy will be used, so it shouldn't matter if the host cannot be resolved. |
| ui_test_utils::NavigateToURL( |
| browser(), GURL("http://does.not.resolve/echoheader?Chrome-Proxy-Ect")); |
| |
| EXPECT_EQ(GetBody(), "4G"); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DataReductionProxyBrowsertest, |
| ProxyNotUsedWhenDisabled) { |
| net::EmbeddedTestServer test_server; |
| test_server.RegisterRequestHandler( |
| base::BindRepeating(&BasicResponse, kDummyBody)); |
| ASSERT_TRUE(test_server.Start()); |
| |
| ui_test_utils::NavigateToURL( |
| browser(), GetURLWithMockHost(test_server, "/echoheader?Chrome-Proxy")); |
| EXPECT_THAT(GetBody(), testing::HasSubstr(kSessionKey)); |
| |
| EnableDataSaver(false); |
| |
| // |test_server| only has the BasicResponse handler, so should return the |
| // dummy response no matter what the URL if it is not being proxied. |
| ui_test_utils::NavigateToURL( |
| browser(), GetURLWithMockHost(test_server, "/echoheader?Chrome-Proxy")); |
| EXPECT_EQ(GetBody(), kDummyBody); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DataReductionProxyBrowsertest, |
| ProxyNotUsedForWebSocket) { |
| // Expect the WebSocket handshake to be attempted with |test_server| |
| // directly. |
| base::RunLoop web_socket_handshake_loop; |
| net::EmbeddedTestServer test_server; |
| test_server.RegisterRequestHandler( |
| base::BindRepeating(&BasicResponse, kDummyBody)); |
| test_server.RegisterRequestMonitor(base::BindLambdaForTesting( |
| [&web_socket_handshake_loop]( |
| const net::test_server::HttpRequest& request) { |
| if (request.headers.count("upgrade") > 0u) |
| web_socket_handshake_loop.Quit(); |
| })); |
| ASSERT_TRUE(test_server.Start()); |
| |
| // If the DRP client (erroneously) decides to proxy the WebSocket handshake, |
| // it will attempt to establish a tunnel through |drp_server|. |
| net::EmbeddedTestServer drp_server; |
| drp_server.AddDefaultHandlers(GetChromeTestDataDir()); |
| bool tunnel_attempted = false; |
| drp_server.RegisterRequestMonitor(base::BindLambdaForTesting( |
| [&tunnel_attempted, &web_socket_handshake_loop]( |
| const net::test_server::HttpRequest& request) { |
| if (request.method == net::test_server::METHOD_CONNECT) { |
| tunnel_attempted = true; |
| web_socket_handshake_loop.Quit(); |
| } |
| })); |
| ASSERT_TRUE(drp_server.Start()); |
| SetConfig(CreateConfigForServer(drp_server)); |
| // A network change forces the config to be fetched. |
| SimulateNetworkChange(network::mojom::ConnectionType::CONNECTION_3G); |
| WaitForConfig(); |
| |
| ui_test_utils::NavigateToURL(browser(), |
| GetURLWithMockHost(test_server, "/echo")); |
| |
| const std::string url = |
| base::StrCat({"ws://", kMockHost, ":", test_server.base_url().port()}); |
| const std::string script = R"((url => { |
| var ws = new WebSocket(url); |
| }))"; |
| EXPECT_TRUE( |
| ExecuteScript(browser()->tab_strip_model()->GetActiveWebContents(), |
| script + "('" + url + "')")); |
| web_socket_handshake_loop.Run(); |
| EXPECT_FALSE(tunnel_attempted); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DataReductionProxyBrowsertest, |
| DoesNotOverrideExistingProxyConfig) { |
| // When there's a proxy configuration provided to the browser already (system |
| // proxy, command line, etc.), the DRP proxy must not override it. |
| net::EmbeddedTestServer existing_proxy_server; |
| existing_proxy_server.RegisterRequestHandler( |
| base::BindRepeating(&BasicResponse, kDummyBody)); |
| ASSERT_TRUE(existing_proxy_server.Start()); |
| |
| browser()->profile()->GetPrefs()->Set( |
| proxy_config::prefs::kProxy, |
| ProxyConfigDictionary::CreateFixedServers( |
| existing_proxy_server.host_port_pair().ToString(), "")); |
| |
| EnableDataSaver(true); |
| |
| // Proxy will be used, so it shouldn't matter if the host cannot be resolved. |
| ui_test_utils::NavigateToURL(browser(), |
| GURL("http://does.not.resolve.com/echo")); |
| |
| EXPECT_EQ(GetBody(), kDummyBody); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DataReductionProxyBrowsertest, UMAMetricsRecorded) { |
| base::HistogramTester histogram_tester; |
| |
| // Make sure we wait for timing information. |
| page_load_metrics::PageLoadMetricsTestWaiter waiter( |
| browser()->tab_strip_model()->GetActiveWebContents()); |
| waiter.AddPageExpectation( |
| page_load_metrics::PageLoadMetricsTestWaiter::TimingField::kFirstPaint); |
| |
| // Proxy will be used, so it shouldn't matter if the host cannot be resolved. |
| ui_test_utils::NavigateToURL(browser(), GURL("http://does.not.resolve/echo")); |
| waiter.Wait(); |
| |
| SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| histogram_tester.ExpectUniqueSample("DataReductionProxy.ProxySchemeUsed", |
| ProxyScheme::PROXY_SCHEME_HTTP, 1); |
| histogram_tester.ExpectTotalCount( |
| "PageLoad.Clients.DataReductionProxy.PaintTiming." |
| "NavigationToFirstContentfulPaint", |
| 1); |
| } |
| |
| // Test that enabling the holdback disables the proxy. |
| class DataReductionProxyWithHoldbackBrowsertest |
| : public ::testing::WithParamInterface<bool>, |
| public DataReductionProxyBrowsertest { |
| public: |
| void SetUp() override { |
| if (GetParam()) { |
| scoped_feature_list_.InitWithFeatures( |
| {features::kDataReductionProxyEnabledWithNetworkService, |
| data_reduction_proxy::features::kDataReductionProxyHoldback}, |
| {}); |
| } else { |
| scoped_feature_list_.InitWithFeatures( |
| {features::kDataReductionProxyEnabledWithNetworkService}, {}); |
| } |
| |
| InProcessBrowserTest::SetUp(); |
| } |
| |
| private: |
| base::test::ScopedFeatureList scoped_feature_list_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_P(DataReductionProxyWithHoldbackBrowsertest, |
| UpdateConfig) { |
| net::EmbeddedTestServer proxy_server; |
| proxy_server.RegisterRequestMonitor(base::BindRepeating( |
| &DataReductionProxyBrowsertest::MonitorAndVerifyRequestsToProxyServer, |
| base::Unretained(this))); |
| proxy_server.RegisterRequestHandler( |
| base::BindRepeating(&BasicResponse, kPrimaryResponse)); |
| ASSERT_TRUE(proxy_server.Start()); |
| |
| SetConfig(CreateConfigForServer(proxy_server)); |
| // A network change forces the config to be fetched. |
| SimulateNetworkChange(network::mojom::ConnectionType::CONNECTION_3G); |
| WaitForConfig(); |
| |
| ui_test_utils::NavigateToURL(browser(), GURL("http://does.not.resolve/foo")); |
| |
| if (GetParam()) { |
| EXPECT_NE(GetBody(), kPrimaryResponse); |
| } else { |
| EXPECT_EQ(GetBody(), kPrimaryResponse); |
| } |
| } |
| |
| // Parameter is true if the data reduction proxy holdback should be enabled. |
| INSTANTIATE_TEST_SUITE_P(, |
| DataReductionProxyWithHoldbackBrowsertest, |
| ::testing::Values(false, true)); |
| |
| class DataReductionProxyExpBrowsertest : public DataReductionProxyBrowsertest { |
| public: |
| void SetUp() override { |
| base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( |
| data_reduction_proxy::switches::kDataReductionProxyExperiment, |
| "foo_experiment"); |
| |
| DataReductionProxyBrowsertest::SetUp(); |
| } |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(DataReductionProxyExpBrowsertest, |
| ChromeProxyExpHeaderSet) { |
| expect_exp_value_in_request_header_ = "foo_experiment"; |
| |
| net::EmbeddedTestServer proxy_server; |
| proxy_server.RegisterRequestMonitor(base::BindRepeating( |
| &DataReductionProxyBrowsertest::MonitorAndVerifyRequestsToProxyServer, |
| base::Unretained(this))); |
| proxy_server.RegisterRequestHandler( |
| base::BindRepeating(&BasicResponse, kPrimaryResponse)); |
| ASSERT_TRUE(proxy_server.Start()); |
| SetConfig(CreateConfigForServer(proxy_server)); |
| // A network change forces the config to be fetched. |
| SimulateNetworkChange(network::mojom::ConnectionType::CONNECTION_3G); |
| WaitForConfig(); |
| |
| ui_test_utils::NavigateToURL(browser(), GURL("http://does.not.resolve/foo")); |
| EXPECT_LE(1u, count_proxy_server_requests_received_); |
| } |
| |
| class DataReductionProxyBrowsertestWithNetworkService |
| : public DataReductionProxyBrowsertest { |
| public: |
| void SetUp() override { |
| scoped_feature_list_.InitAndEnableFeature( |
| network::features::kNetworkService); |
| DataReductionProxyBrowsertest::SetUp(); |
| } |
| base::test::ScopedFeatureList scoped_feature_list_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(DataReductionProxyBrowsertestWithNetworkService, |
| DataUsePrefsRecorded) { |
| PrefService* prefs = browser()->profile()->GetPrefs(); |
| |
| // Make sure we wait for timing information. |
| page_load_metrics::PageLoadMetricsTestWaiter waiter( |
| browser()->tab_strip_model()->GetActiveWebContents()); |
| waiter.AddPageExpectation( |
| page_load_metrics::PageLoadMetricsTestWaiter::TimingField::kFirstPaint); |
| |
| // Proxy will be used, so it shouldn't matter if the host cannot be resolved. |
| ui_test_utils::NavigateToURL(browser(), GURL("http://does.not.resolve/echo")); |
| waiter.Wait(); |
| |
| ASSERT_GE(0, prefs->GetInt64( |
| data_reduction_proxy::prefs::kHttpReceivedContentLength)); |
| ASSERT_GE(0, prefs->GetInt64( |
| data_reduction_proxy::prefs::kHttpOriginalContentLength)); |
| } |
| |
| class DataReductionProxyFallbackBrowsertest |
| : public DataReductionProxyBrowsertest { |
| public: |
| using ResponseHook = |
| base::RepeatingCallback<void(net::test_server::BasicHttpResponse*)>; |
| |
| void SetUpOnMainThread() override { |
| // Set up a primary server which will return the Chrome-Proxy header set by |
| // SetHeader() and status set by SetStatusCode(). Secondary server will just |
| // return the secondary response. |
| primary_server_.RegisterRequestHandler(base::BindRepeating( |
| &DataReductionProxyFallbackBrowsertest::AddChromeProxyHeader, |
| base::Unretained(this))); |
| primary_server_.RegisterRequestMonitor(base::BindRepeating( |
| &DataReductionProxyBrowsertest::MonitorAndVerifyRequestsToProxyServer, |
| base::Unretained(this))); |
| ASSERT_TRUE(primary_server_.Start()); |
| |
| secondary_server_.RegisterRequestHandler( |
| base::BindRepeating(&BasicResponse, kSecondaryResponse)); |
| secondary_server_.RegisterRequestMonitor(base::BindRepeating( |
| &DataReductionProxyBrowsertest::MonitorAndVerifyRequestsToProxyServer, |
| base::Unretained(this))); |
| ASSERT_TRUE(secondary_server_.Start()); |
| |
| net::HostPortPair primary_host_port_pair = primary_server_.host_port_pair(); |
| net::HostPortPair secondary_host_port_pair = |
| secondary_server_.host_port_pair(); |
| SetConfig(CreateConfig( |
| kSessionKey, 1000, 0, ProxyServer_ProxyScheme_HTTP, |
| primary_host_port_pair.host(), primary_host_port_pair.port(), |
| ProxyServer_ProxyScheme_HTTP, secondary_host_port_pair.host(), |
| secondary_host_port_pair.port(), 0.5f, false)); |
| |
| DataReductionProxyBrowsertest::SetUpOnMainThread(); |
| } |
| |
| void SetResponseHook(ResponseHook response_hook) { |
| base::AutoLock auto_lock(lock_); |
| response_hook_ = response_hook; |
| } |
| |
| void SetHeader(const std::string& header) { |
| base::AutoLock auto_lock(lock_); |
| header_ = header; |
| } |
| |
| void SetStatusCode(net::HttpStatusCode status_code) { |
| base::AutoLock auto_lock(lock_); |
| status_code_ = status_code; |
| } |
| |
| // If the request is for the URL from |host_port_pair|, then response |
| // status code would be set to |status_code|. |
| void SetStatusCodeForURLsFromHostPortPair( |
| const net::HostPortPair& host_port_pair, |
| net::HttpStatusCode status_code) { |
| base::AutoLock auto_lock(lock_); |
| special_host_port_pair_ = host_port_pair; |
| special_status_code_ = status_code; |
| } |
| |
| void SetLocationHeader(const std::string& header) { |
| base::AutoLock auto_lock(lock_); |
| location_header_ = header; |
| } |
| |
| void TearDown() override { |
| if (IsNetworkServiceEnabled()) { |
| EXPECT_LE(1u, count_proxy_server_requests_received_); |
| } |
| } |
| |
| private: |
| std::unique_ptr<net::test_server::HttpResponse> AddChromeProxyHeader( |
| const net::test_server::HttpRequest& request) { |
| base::AutoLock auto_lock(lock_); |
| auto response = std::make_unique<net::test_server::BasicHttpResponse>(); |
| if (!header_.empty()) |
| response->AddCustomHeader(chrome_proxy_header(), header_); |
| if (!location_header_.empty()) |
| response->AddCustomHeader("Location", location_header_); |
| if (response_hook_) |
| response_hook_.Run(response.get()); |
| |
| // Compute the requested URL from the "Host" header. It's not possible |
| // to use the request URL directly since that contains the hostname of the |
| // proxy server. |
| bool use_special_status_code = false; |
| if (request.headers.find("Host") != request.headers.end()) { |
| const GURL kOriginUrl( |
| base::StrCat({"http://", request.headers.find("Host")->second + |
| request.GetURL().path()})); |
| |
| if (!special_host_port_pair_.IsEmpty() && |
| net::HostPortPair::FromURL(kOriginUrl) == special_host_port_pair_) { |
| use_special_status_code = true; |
| } |
| } |
| |
| if (use_special_status_code) { |
| response->set_code(special_status_code_); |
| } else { |
| response->set_code(status_code_); |
| } |
| response->set_content(kPrimaryResponse); |
| response->set_content_type("text/plain"); |
| return response; |
| } |
| |
| // |lock_| guards access to all the local variables except the embedded test |
| // servers directly. |
| base::Lock lock_; |
| std::string header_; |
| std::string location_header_; |
| |
| // If the request is for the URL from |special_host_port_pair_|, then response |
| // status code is set to |special_status_code_|. Otherwise, it is set to |
| // |status_code_|. |
| net::HostPortPair special_host_port_pair_; |
| net::HttpStatusCode special_status_code_ = net::HTTP_OK; |
| net::HttpStatusCode status_code_ = net::HTTP_OK; |
| |
| ResponseHook response_hook_; |
| net::EmbeddedTestServer primary_server_; |
| net::EmbeddedTestServer secondary_server_; |
| }; |
| |
| class TestDataReductionProxyPingbackClient |
| : public DataReductionProxyPingbackClient { |
| public: |
| bool received_valid_pingback() const { return received_valid_pingback_; } |
| uint64_t received_page_id() const { return received_page_id_; } |
| |
| private: |
| void SendPingback(const DataReductionProxyData& data, |
| const DataReductionProxyPageLoadTiming& timing) override { |
| EXPECT_TRUE(data.used_data_reduction_proxy()); |
| EXPECT_EQ(kSessionKey, data.session_key()); |
| EXPECT_GT(*data.page_id(), 0U); |
| received_valid_pingback_ = true; |
| received_page_id_ = *data.page_id(); |
| } |
| |
| void SetPingbackReportingFraction( |
| float pingback_reporting_fraction) override {} |
| |
| bool received_valid_pingback_ = false; |
| uint64_t received_page_id_ = 0; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(DataReductionProxyBrowsertest, PingbackSent) { |
| net::EmbeddedTestServer primary_server; |
| primary_server.RegisterRequestHandler( |
| base::BindRepeating(&CreateDRPResponseWithHeader)); |
| ASSERT_TRUE(primary_server.Start()); |
| SetConfig(CreateConfigForServer(primary_server)); |
| // A network change forces the config to be fetched. |
| SimulateNetworkChange(network::mojom::ConnectionType::CONNECTION_3G); |
| WaitForConfig(); |
| |
| // Pingback client is owned by the DRP service. |
| TestDataReductionProxyPingbackClient* pingback_client = |
| new TestDataReductionProxyPingbackClient(); |
| DataReductionProxyChromeSettingsFactory::GetForBrowserContext( |
| browser()->profile()) |
| ->data_reduction_proxy_service() |
| ->SetPingbackClientForTesting(pingback_client); |
| |
| // Proxy will be used, so it shouldn't matter if the host cannot be resolved. |
| ui_test_utils::NavigateToURL( |
| browser(), GURL("http://does.not.resolve.com/echo_chromeproxy_header")); |
| std::string body = GetBody(); |
| |
| // Navigate away for the metrics to be recorded. |
| ui_test_utils::NavigateToURL(browser(), GURL("about:blank")); |
| |
| EXPECT_TRUE(pingback_client->received_valid_pingback()); |
| EXPECT_THAT(body, HasSubstr(base::StringPrintf("s=%s,", kSessionKey))); |
| EXPECT_THAT(body, HasSubstr("pid=")); |
| |
| // Parse the page ID from chrom-proxy request header and compare with page ID |
| // sent in pingback. |
| uint64_t request_page_id = 0; |
| base::StringPairs kv_pairs; |
| EXPECT_TRUE(base::SplitStringIntoKeyValuePairs(body, '=', ',', &kv_pairs)); |
| for (const auto& kv_pair : kv_pairs) { |
| if (kv_pair.first == "pid") |
| EXPECT_TRUE(base::HexStringToUInt64(kv_pair.second, &request_page_id)); |
| } |
| |
| EXPECT_NE(0u, request_page_id); |
| EXPECT_EQ(pingback_client->received_page_id(), request_page_id); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DataReductionProxyFallbackBrowsertest, |
| FallbackProxyUsedOnNetError) { |
| SetResponseHook( |
| base::BindRepeating([](net::test_server::BasicHttpResponse* response) { |
| response->AddCustomHeader("Content-Disposition", "inline"); |
| response->AddCustomHeader("Content-Disposition", "form-data"); |
| })); |
| base::HistogramTester histogram_tester; |
| ui_test_utils::NavigateToURL( |
| browser(), GURL("http://does.not.resolve/echoheader?Chrome-Proxy")); |
| EXPECT_THAT(GetBody(), kSecondaryResponse); |
| histogram_tester.ExpectUniqueSample( |
| "DataReductionProxy.InvalidResponseHeadersReceived.NetError", |
| -net::ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_DISPOSITION, 1); |
| |
| // Bad proxy should still be bypassed. |
| SetResponseHook(ResponseHook()); |
| ui_test_utils::NavigateToURL( |
| browser(), GURL("http://does.not.resolve/echoheader?Chrome-Proxy")); |
| EXPECT_THAT(GetBody(), kSecondaryResponse); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DataReductionProxyFallbackBrowsertest, |
| FallbackProxyUsedOn500Status) { |
| base::HistogramTester histogram_tester; |
| // Should fall back to the secondary proxy if a 500 error occurs. |
| SetStatusCode(net::HTTP_INTERNAL_SERVER_ERROR); |
| ui_test_utils::NavigateToURL( |
| browser(), GURL("http://does.not.resolve/echoheader?Chrome-Proxy")); |
| EXPECT_THAT(GetBody(), kSecondaryResponse); |
| histogram_tester.ExpectUniqueSample( |
| "DataReductionProxy.BypassTypePrimary", |
| BYPASS_EVENT_TYPE_STATUS_500_HTTP_INTERNAL_SERVER_ERROR, 1); |
| |
| // Bad proxy should still be bypassed. |
| SetStatusCode(net::HTTP_OK); |
| ui_test_utils::NavigateToURL( |
| browser(), GURL("http://does.not.resolve/echoheader?Chrome-Proxy")); |
| EXPECT_THAT(GetBody(), kSecondaryResponse); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DataReductionProxyFallbackBrowsertest, |
| FallbackProxyUsedWhenBypassHeaderSent) { |
| base::HistogramTester histogram_tester; |
| // Should fall back to the secondary proxy if the bypass header is set. |
| SetHeader("bypass=100"); |
| ui_test_utils::NavigateToURL( |
| browser(), GURL("http://does.not.resolve/echoheader?Chrome-Proxy")); |
| EXPECT_THAT(GetBody(), kSecondaryResponse); |
| histogram_tester.ExpectUniqueSample("DataReductionProxy.BypassTypePrimary", |
| BYPASS_EVENT_TYPE_MEDIUM, 1); |
| |
| // Bad proxy should still be bypassed. |
| SetHeader(""); |
| ui_test_utils::NavigateToURL( |
| browser(), GURL("http://does.not.resolve/echoheader?Chrome-Proxy")); |
| EXPECT_THAT(GetBody(), kSecondaryResponse); |
| } |
| |
| IN_PROC_BROWSER_TEST_F( |
| DataReductionProxyFallbackBrowsertest, |
| DISABLE_ON_WIN_MAC_CHROMEOS(BadProxiesResetWhenDisabled)) { |
| base::HistogramTester histogram_tester; |
| SetHeader("bypass=100"); |
| ui_test_utils::NavigateToURL( |
| browser(), GURL("http://does.not.resolve/echoheader?Chrome-Proxy")); |
| EXPECT_THAT(GetBody(), kSecondaryResponse); |
| histogram_tester.ExpectUniqueSample("DataReductionProxy.BypassTypePrimary", |
| BYPASS_EVENT_TYPE_MEDIUM, 1); |
| |
| // Disabling and enabling DRP should clear the bypass. |
| EnableDataSaver(false); |
| EnableDataSaver(true); |
| |
| SetHeader(""); |
| ui_test_utils::NavigateToURL( |
| browser(), GURL("http://does.not.resolve/echoheader?Chrome-Proxy")); |
| EXPECT_THAT(GetBody(), kPrimaryResponse); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DataReductionProxyFallbackBrowsertest, |
| NoProxyUsedWhenBlockOnceHeaderSent) { |
| base::HistogramTester histogram_tester; |
| net::EmbeddedTestServer test_server; |
| test_server.RegisterRequestHandler( |
| base::BindRepeating(&BasicResponse, kDummyBody)); |
| ASSERT_TRUE(test_server.Start()); |
| |
| // Request should not use a proxy. |
| SetHeader("block-once"); |
| ui_test_utils::NavigateToURL(browser(), |
| GetURLWithMockHost(test_server, "/echo")); |
| EXPECT_THAT(GetBody(), kDummyBody); |
| EXPECT_LE( |
| 1, histogram_tester.GetBucketCount("DataReductionProxy.BlockTypePrimary", |
| BYPASS_EVENT_TYPE_CURRENT)); |
| |
| // Proxy should no longer be blocked, and use first proxy. |
| SetHeader(""); |
| ui_test_utils::NavigateToURL(browser(), |
| GetURLWithMockHost(test_server, "/echo")); |
| EXPECT_EQ(GetBody(), kPrimaryResponse); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DataReductionProxyFallbackBrowsertest, |
| FallbackProxyUsedWhenBlockHeaderSent) { |
| base::HistogramTester histogram_tester; |
| net::EmbeddedTestServer test_server; |
| test_server.RegisterRequestHandler( |
| base::BindRepeating(&BasicResponse, kDummyBody)); |
| ASSERT_TRUE(test_server.Start()); |
| |
| // Request should not use a proxy. |
| SetHeader("block=100"); |
| ui_test_utils::NavigateToURL(browser(), |
| GetURLWithMockHost(test_server, "/echo")); |
| EXPECT_THAT(GetBody(), kDummyBody); |
| histogram_tester.ExpectUniqueSample("DataReductionProxy.BlockTypePrimary", |
| BYPASS_EVENT_TYPE_MEDIUM, 1); |
| |
| // Request should still not use proxy. |
| SetHeader(""); |
| ui_test_utils::NavigateToURL(browser(), |
| GetURLWithMockHost(test_server, "/echo")); |
| EXPECT_THAT(GetBody(), kDummyBody); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DataReductionProxyFallbackBrowsertest, |
| FallbackProxyUsedWhenBlockZeroHeaderSent) { |
| base::HistogramTester histogram_tester; |
| net::EmbeddedTestServer test_server; |
| test_server.RegisterRequestHandler( |
| base::BindRepeating(&BasicResponse, kDummyBody)); |
| ASSERT_TRUE(test_server.Start()); |
| |
| // Request should not use a proxy. Sending 0 for the block param will block |
| // requests for a random duration between 1 and 5 minutes. |
| SetHeader("block=0"); |
| ui_test_utils::NavigateToURL(browser(), |
| GetURLWithMockHost(test_server, "/echo")); |
| EXPECT_THAT(GetBody(), kDummyBody); |
| histogram_tester.ExpectUniqueSample("DataReductionProxy.BlockTypePrimary", |
| BYPASS_EVENT_TYPE_MEDIUM, 1); |
| |
| // Request should still not use proxy. |
| SetHeader(""); |
| ui_test_utils::NavigateToURL(browser(), |
| GetURLWithMockHost(test_server, "/echo")); |
| EXPECT_THAT(GetBody(), kDummyBody); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DataReductionProxyFallbackBrowsertest, |
| FallbackProxyUsedWhenBlockForLargeDurationSent) { |
| base::HistogramTester histogram_tester; |
| net::EmbeddedTestServer test_server; |
| test_server.RegisterRequestHandler( |
| base::BindRepeating(&BasicResponse, kDummyBody)); |
| ASSERT_TRUE(test_server.Start()); |
| |
| // Sending block=86400 triggers a long bypass event that blocks requests for |
| // a day. |
| SetHeader("block=86400"); |
| ui_test_utils::NavigateToURL(browser(), |
| GetURLWithMockHost(test_server, "/echo")); |
| EXPECT_THAT(GetBody(), kDummyBody); |
| histogram_tester.ExpectUniqueSample("DataReductionProxy.BlockTypePrimary", |
| BYPASS_EVENT_TYPE_LONG, 1); |
| |
| // Request should still not use proxy. |
| SetHeader(""); |
| ui_test_utils::NavigateToURL(browser(), |
| GetURLWithMockHost(test_server, "/echo")); |
| EXPECT_THAT(GetBody(), kDummyBody); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DataReductionProxyFallbackBrowsertest, |
| DISABLE_ON_WIN_MAC_CHROMEOS(ProxyBlockedOnAuthError)) { |
| if (!IsNetworkServiceEnabled()) |
| return; |
| base::HistogramTester histogram_tester; |
| net::EmbeddedTestServer test_server; |
| test_server.RegisterRequestHandler( |
| base::BindRepeating(&BasicResponse, kDummyBody)); |
| ASSERT_TRUE(test_server.Start()); |
| |
| SetStatusCode(net::HTTP_PROXY_AUTHENTICATION_REQUIRED); |
| |
| ui_test_utils::NavigateToURL(browser(), |
| GetURLWithMockHost(test_server, "/echo")); |
| EXPECT_THAT(GetBody(), kDummyBody); |
| histogram_tester.ExpectUniqueSample("DataReductionProxy.BlockTypePrimary", |
| BYPASS_EVENT_TYPE_MALFORMED_407, 1); |
| } |
| |
| // Tests that if using data reduction proxy results in redirect loop, then |
| // the proxy is bypassed, and the request is fetched directly. |
| IN_PROC_BROWSER_TEST_F(DataReductionProxyFallbackBrowsertest, |
| DISABLE_ON_WIN_MAC_CHROMEOS(RedirectCycle)) { |
| if (!IsNetworkServiceEnabled()) |
| return; |
| base::HistogramTester histogram_tester; |
| net::EmbeddedTestServer test_server; |
| test_server.RegisterRequestHandler( |
| base::BindRepeating(&BasicResponse, kDummyBody)); |
| ASSERT_TRUE(test_server.Start()); |
| |
| const GURL kUrl(GetURLWithMockHost(test_server, "/echo")); |
| SetStatusCodeForURLsFromHostPortPair(net::HostPortPair::FromURL(kUrl), |
| net::HTTP_TEMPORARY_REDIRECT); |
| SetLocationHeader(kUrl.spec()); |
| ui_test_utils::NavigateToURL(browser(), kUrl); |
| EXPECT_THAT(GetBody(), kDummyBody); |
| |
| // Request should still not use proxy. |
| ui_test_utils::NavigateToURL(browser(), kUrl); |
| EXPECT_THAT(GetBody(), kDummyBody); |
| } |
| |
| class DataReductionProxyResourceTypeBrowsertest |
| : public DataReductionProxyBrowsertest { |
| public: |
| void SetUpOnMainThread() override { |
| unspecified_server_.RegisterRequestHandler(base::BindRepeating( |
| &IncrementRequestCount, "/video", &first_proxy_request_count_)); |
| ASSERT_TRUE(unspecified_server_.Start()); |
| |
| core_server_.RegisterRequestHandler(base::BindRepeating( |
| &IncrementRequestCount, "/video", &second_proxy_request_count_)); |
| ASSERT_TRUE(core_server_.Start()); |
| |
| net::HostPortPair unspecified_host_port_pair = |
| unspecified_server_.host_port_pair(); |
| net::HostPortPair core_host_port_pair = core_server_.host_port_pair(); |
| SetConfig(CreateConfig( |
| kSessionKey, 1000, 0, ProxyServer_ProxyScheme_HTTP, |
| unspecified_host_port_pair.host(), unspecified_host_port_pair.port(), |
| ProxyServer_ProxyScheme_HTTP, core_host_port_pair.host(), |
| core_host_port_pair.port(), 0.5f, false)); |
| |
| DataReductionProxyBrowsertest::SetUpOnMainThread(); |
| } |
| |
| int first_proxy_request_count_ = 0; |
| int second_proxy_request_count_ = 0; |
| |
| private: |
| net::EmbeddedTestServer unspecified_server_; |
| net::EmbeddedTestServer core_server_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_F(DataReductionProxyFallbackBrowsertest, |
| ProxyBypassedOn502Error) { |
| base::HistogramTester histogram_tester; |
| net::EmbeddedTestServer test_server; |
| test_server.RegisterRequestHandler( |
| base::BindRepeating(&BasicResponse, kDummyBody)); |
| ASSERT_TRUE(test_server.Start()); |
| |
| SetStatusCode(net::HTTP_BAD_GATEWAY); |
| |
| ui_test_utils::NavigateToURL(browser(), |
| GetURLWithMockHost(test_server, "/echo")); |
| EXPECT_THAT(GetBody(), kSecondaryResponse); |
| histogram_tester.ExpectUniqueSample( |
| "DataReductionProxy.BypassTypePrimary", |
| BYPASS_EVENT_TYPE_STATUS_502_HTTP_BAD_GATEWAY, 1); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DataReductionProxyFallbackBrowsertest, |
| ProxyShortBypassedOn502ErrorWithFeature) { |
| base::test::ScopedFeatureList scoped_feature_list; |
| scoped_feature_list.InitAndEnableFeatureWithParameters( |
| features::kDataReductionProxyBlockOnBadGatewayResponse, |
| {{"block_duration_seconds", "10"}}); |
| base::HistogramTester histogram_tester; |
| net::EmbeddedTestServer test_server; |
| test_server.RegisterRequestHandler( |
| base::BindRepeating(&BasicResponse, kDummyBody)); |
| ASSERT_TRUE(test_server.Start()); |
| |
| SetStatusCode(net::HTTP_BAD_GATEWAY); |
| |
| ui_test_utils::NavigateToURL(browser(), |
| GetURLWithMockHost(test_server, "/echo")); |
| // Both the proxies should be blocked. |
| EXPECT_THAT(GetBody(), kDummyBody); |
| histogram_tester.ExpectUniqueSample("DataReductionProxy.BlockTypePrimary", |
| BYPASS_EVENT_TYPE_SHORT, 1); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DataReductionProxyResourceTypeBrowsertest, |
| FirstProxyUsedForMedia) { |
| ui_test_utils::NavigateToURL( |
| browser(), GetURLWithMockHost(*embedded_test_server(), "/echo")); |
| |
| std::string script = R"((url => { |
| var video = document.createElement('video'); |
| // Use onerror since the response is not a valid video. |
| video.onerror = () => domAutomationController.send('done'); |
| video.src = url; |
| video.load(); |
| document.body.appendChild(video); |
| }))"; |
| std::string result; |
| ASSERT_TRUE(ExecuteScriptAndExtractString( |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| script + "('" + |
| GetURLWithMockHost(*embedded_test_server(), "/video").spec() + "')", |
| &result)); |
| EXPECT_EQ(result, "done"); |
| |
| EXPECT_EQ(first_proxy_request_count_, 1); |
| EXPECT_EQ(second_proxy_request_count_, 0); |
| } |
| |
| class DataReductionProxyWarmupURLBrowsertest |
| : public ::testing::WithParamInterface< |
| std::tuple<ProxyServer_ProxyScheme, bool, bool>>, |
| public DataReductionProxyBrowsertestBase { |
| public: |
| DataReductionProxyWarmupURLBrowsertest() |
| : via_header_(std::get<1>(GetParam()) ? "1.1 Chrome-Compression-Proxy" |
| : "bad"), |
| primary_server_(GetTestServerType()), |
| secondary_server_(GetTestServerType()) {} |
| |
| void SetUpOnMainThread() override { |
| if (!std::get<2>(GetParam())) { |
| scoped_feature_list_.InitAndDisableFeature( |
| features::kDataReductionProxyDisableProxyFailedWarmup); |
| } |
| primary_server_loop_ = std::make_unique<base::RunLoop>(); |
| primary_server_.RegisterRequestHandler(base::BindRepeating( |
| &DataReductionProxyWarmupURLBrowsertest::WaitForWarmupRequest, |
| base::Unretained(this), primary_server_loop_.get())); |
| primary_server_.RegisterRequestMonitor(base::BindRepeating( |
| &DataReductionProxyBrowsertest::MonitorAndVerifyRequestsToProxyServer, |
| base::Unretained(this))); |
| ASSERT_TRUE(primary_server_.Start()); |
| |
| secondary_server_loop_ = std::make_unique<base::RunLoop>(); |
| secondary_server_.RegisterRequestHandler(base::BindRepeating( |
| &DataReductionProxyWarmupURLBrowsertest::WaitForWarmupRequest, |
| base::Unretained(this), secondary_server_loop_.get())); |
| secondary_server_.RegisterRequestMonitor(base::BindRepeating( |
| &DataReductionProxyBrowsertest::MonitorAndVerifyRequestsToProxyServer, |
| base::Unretained(this))); |
| ASSERT_TRUE(secondary_server_.Start()); |
| |
| net::HostPortPair primary_host_port_pair = primary_server_.host_port_pair(); |
| net::HostPortPair secondary_host_port_pair = |
| secondary_server_.host_port_pair(); |
| SetConfig(CreateConfig( |
| kSessionKey, 1000, 0, std::get<0>(GetParam()), |
| primary_host_port_pair.host(), primary_host_port_pair.port(), |
| std::get<0>(GetParam()), secondary_host_port_pair.host(), |
| secondary_host_port_pair.port(), 0.5f, false)); |
| |
| DataReductionProxyBrowsertestBase::SetUpOnMainThread(); |
| } |
| |
| // Retries fetching |histogram_name| until it contains at least |count| |
| // samples. |
| void RetryForHistogramUntilCountReached( |
| base::HistogramTester* histogram_tester, |
| const std::string& histogram_name, |
| size_t count) { |
| base::RunLoop().RunUntilIdle(); |
| for (size_t attempt = 0; attempt < 3; ++attempt) { |
| const std::vector<base::Bucket> buckets = |
| histogram_tester->GetAllSamples(histogram_name); |
| size_t total_count = 0; |
| for (const auto& bucket : buckets) |
| total_count += bucket.count; |
| if (total_count >= count) |
| return; |
| content::FetchHistogramsFromChildProcesses(); |
| SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| base::RunLoop().RunUntilIdle(); |
| } |
| } |
| |
| std::string GetHistogramName() { |
| return base::StrCat( |
| {"DataReductionProxy.WarmupURLFetcherCallback.SuccessfulFetch.", |
| std::get<0>(GetParam()) == ProxyServer_ProxyScheme_HTTP ? "Insecure" |
| : "Secure", |
| "Proxy.Core"}); |
| } |
| |
| std::unique_ptr<base::RunLoop> primary_server_loop_; |
| std::unique_ptr<base::RunLoop> secondary_server_loop_; |
| base::HistogramTester histogram_tester_; |
| |
| private: |
| net::EmbeddedTestServer::Type GetTestServerType() { |
| if (std::get<0>(GetParam()) == ProxyServer_ProxyScheme_HTTP) |
| return net::EmbeddedTestServer::TYPE_HTTP; |
| return net::EmbeddedTestServer::TYPE_HTTPS; |
| } |
| |
| std::unique_ptr<net::test_server::HttpResponse> WaitForWarmupRequest( |
| base::RunLoop* run_loop, |
| const net::test_server::HttpRequest& request) { |
| auto response = std::make_unique<net::test_server::BasicHttpResponse>(); |
| if (base::StartsWith(request.relative_url, "/e2e_probe", |
| base::CompareCase::SENSITIVE)) { |
| run_loop->Quit(); |
| response->set_content("content"); |
| response->AddCustomHeader("via", via_header_); |
| const auto user_agent = |
| request.headers.find(net::HttpRequestHeaders::kUserAgent); |
| EXPECT_TRUE(user_agent != request.headers.end()); |
| EXPECT_THAT(user_agent->second, HasSubstr("Chrome/")); |
| } else if (base::StartsWith(request.relative_url, "/echoheader", |
| base::CompareCase::SENSITIVE)) { |
| const auto chrome_proxy_header = request.headers.find("chrome-proxy"); |
| if (chrome_proxy_header != request.headers.end()) { |
| response->set_content(chrome_proxy_header->second); |
| response->AddCustomHeader("chrome-proxy", "ofcl=1000"); |
| } |
| } |
| return response; |
| } |
| |
| const std::string via_header_; |
| net::EmbeddedTestServer primary_server_; |
| net::EmbeddedTestServer secondary_server_; |
| base::test::ScopedFeatureList scoped_feature_list_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_P( |
| DataReductionProxyWarmupURLBrowsertest, |
| DISABLE_ON_WIN_MAC_CHROMEOS(WarmupURLsFetchedForEachProxy)) { |
| if (!IsNetworkServiceEnabled()) |
| return; |
| net::EmbeddedTestServer test_server; |
| test_server.RegisterRequestHandler( |
| base::BindRepeating(&BasicResponse, kDummyBody)); |
| ASSERT_TRUE(test_server.Start()); |
| |
| bool is_warmup_fetch_successful = std::get<1>(GetParam()); |
| bool disallow_proxy_failed_warmup_feature_enabled = std::get<2>(GetParam()); |
| primary_server_loop_->Run(); |
| |
| SubprocessMetricsProvider::MergeHistogramDeltasForTesting(); |
| RetryForHistogramUntilCountReached(&histogram_tester_, GetHistogramName(), 1); |
| |
| histogram_tester_.ExpectUniqueSample(GetHistogramName(), |
| is_warmup_fetch_successful, 1); |
| |
| base::RunLoop().RunUntilIdle(); |
| |
| // Navigate to some URL to see if the proxy is only used when warmup URL fetch |
| // was successful. |
| ui_test_utils::NavigateToURL( |
| browser(), GetURLWithMockHost(test_server, "/echoheader?Chrome-Proxy")); |
| std::string body = GetBody(); |
| if (is_warmup_fetch_successful) { |
| EXPECT_THAT(body, HasSubstr(kSessionKey)); |
| } else { |
| if (disallow_proxy_failed_warmup_feature_enabled) { |
| EXPECT_THAT(body, kDummyBody); |
| } else { |
| // When the feature is disabled, the proxy is still being used |
| EXPECT_THAT(body, HasSubstr(kSessionKey)); |
| } |
| } |
| EXPECT_TRUE(WasUrlPathMonitored("/e2e_probe")); |
| } |
| |
| // First parameter indicate proxy scheme for proxies that are being tested. |
| // Second parameter is true if the test proxy server should set via header |
| // correctly on the response headers. |
| INSTANTIATE_TEST_SUITE_P( |
| , |
| DataReductionProxyWarmupURLBrowsertest, |
| ::testing::Combine( |
| testing::Values(ProxyServer_ProxyScheme_HTTP, |
| ProxyServer_ProxyScheme_HTTPS), |
| ::testing::Bool(), // is_warmup_fetch_successful |
| ::testing::Bool() // kDataReductionProxyDisallowProxyFailedWarmup |
| // active |
| )); |
| |
| // Threadsafe log for recording a sequence of events as newline separated text. |
| class EventLog { |
| public: |
| void Add(const std::string& event) { |
| base::AutoLock lock(lock_); |
| log_ += event + "\n"; |
| } |
| |
| std::string GetAndReset() { |
| base::AutoLock lock(lock_); |
| return std::move(log_); |
| } |
| |
| private: |
| base::Lock lock_; |
| std::string log_; |
| }; |
| |
| // Responds to requests for |path| with a 502 and "Chrome-Proxy: block-once", |
| // and logs the request into |event_log|. |
| std::unique_ptr<net::test_server::HttpResponse> DrpBlockOnceHandler( |
| const std::string& server_name, |
| EventLog* event_log, |
| const net::test_server::HttpRequest& request) { |
| if (request.relative_url == "/favicon.ico") |
| return nullptr; |
| |
| event_log->Add(server_name + " responded 502 for " + request.relative_url); |
| auto response = std::make_unique<net::test_server::BasicHttpResponse>(); |
| response->set_content_type("text/plain"); |
| response->set_code(net::HTTP_BAD_GATEWAY); |
| response->AddCustomHeader(chrome_proxy_header(), "block-once"); |
| return response; |
| } |
| |
| // Responds to requests with the path as response body, and logs the request |
| // into |event_log|. |
| std::unique_ptr<net::test_server::HttpResponse> RespondWithRequestPathHandler( |
| const std::string& server_name, |
| EventLog* event_log, |
| const net::test_server::HttpRequest& request) { |
| if (request.relative_url == "/favicon.ico") |
| return nullptr; |
| |
| event_log->Add(server_name + " responded 200 for " + request.relative_url); |
| auto response = std::make_unique<net::test_server::BasicHttpResponse>(); |
| response->set_content_type("text/plain"); |
| response->set_code(net::HTTP_OK); |
| response->set_content(request.relative_url); |
| return response; |
| } |
| |
| // Verify that requests initiated by SimpleURLLoader use the proxy only if |
| // render frame ID is set. |
| IN_PROC_BROWSER_TEST_F(DataReductionProxyBrowsertest, SimpleURLLoader) { |
| if (!IsNetworkServiceEnabled()) |
| return; |
| |
| net::EmbeddedTestServer origin_server; |
| origin_server.RegisterRequestHandler( |
| base::BindRepeating(&BasicResponse, kDummyBody)); |
| ASSERT_TRUE(origin_server.Start()); |
| |
| net::EmbeddedTestServer proxy_server; |
| proxy_server.RegisterRequestMonitor(base::BindRepeating( |
| &DataReductionProxyBrowsertest::MonitorAndVerifyRequestsToProxyServer, |
| base::Unretained(this))); |
| proxy_server.RegisterRequestHandler( |
| base::BindRepeating(&BasicResponse, kPrimaryResponse)); |
| ASSERT_TRUE(proxy_server.Start()); |
| |
| for (const bool set_render_frame_id : {false, true}) { |
| // Set config to |proxy_server|. |
| SetConfig(CreateConfigForServer(proxy_server)); |
| // A network change forces the config to be fetched. |
| SimulateNetworkChange(network::mojom::ConnectionType::CONNECTION_3G); |
| WaitForConfig(); |
| |
| auto resource_request = std::make_unique<network::ResourceRequest>(); |
| if (set_render_frame_id) |
| resource_request->render_frame_id = MSG_ROUTING_CONTROL; |
| // Change the URL for different test cases because a bypassed URL may be |
| // added to the local cache by network service proxy delegate. |
| if (set_render_frame_id) { |
| resource_request->url = GetURLWithMockHost( |
| origin_server, "/echoheader?Chrome-Proxy&random=1"); |
| } else { |
| resource_request->url = GetURLWithMockHost( |
| origin_server, "/echoheader?Chrome-Proxy&random=2"); |
| } |
| |
| auto simple_loader = network::SimpleURLLoader::Create( |
| std::move(resource_request), TRAFFIC_ANNOTATION_FOR_TESTS); |
| |
| auto* storage_partition = |
| content::BrowserContext::GetDefaultStoragePartition( |
| browser()->profile()); |
| auto url_loader_factory = |
| storage_partition->GetURLLoaderFactoryForBrowserProcess(); |
| |
| base::RunLoop loop; |
| simple_loader->DownloadToStringOfUnboundedSizeUntilCrashAndDie( |
| url_loader_factory.get(), |
| base::BindLambdaForTesting( |
| [&](std::unique_ptr<std::string> response_body) { |
| loop.Quit(); |
| ASSERT_TRUE(response_body); |
| EXPECT_EQ(set_render_frame_id ? kPrimaryResponse : kDummyBody, |
| *response_body); |
| })); |
| loop.Run(); |
| } |
| } |
| |
| // Ensure that renderer initiated same-site navigations work. |
| IN_PROC_BROWSER_TEST_F(DataReductionProxyBrowsertest, |
| RendererInitiatedSameSiteNavigation) { |
| // Perform a browser-initiated navigation. |
| net::EmbeddedTestServer origin_server; |
| origin_server.ServeFilesFromSourceDirectory( |
| base::FilePath(FILE_PATH_LITERAL("content/test/data"))); |
| ASSERT_TRUE(origin_server.Start()); |
| |
| net::EmbeddedTestServer proxy_server; |
| proxy_server.ServeFilesFromSourceDirectory( |
| base::FilePath(FILE_PATH_LITERAL("content/test/data"))); |
| proxy_server.RegisterRequestMonitor(base::BindRepeating( |
| &DataReductionProxyBrowsertest::MonitorAndVerifyRequestsToProxyServer, |
| base::Unretained(this))); |
| ASSERT_TRUE(proxy_server.Start()); |
| // Set config to |proxy_server|. |
| SetConfig(CreateConfigForServer(proxy_server)); |
| // A network change forces the config to be fetched. |
| SimulateNetworkChange(network::mojom::ConnectionType::CONNECTION_3G); |
| WaitForConfig(); |
| |
| { |
| content::TestNavigationObserver observer( |
| browser()->tab_strip_model()->GetActiveWebContents()); |
| GURL url(GetURLWithMockHost(origin_server, "/simple_links.html")); |
| ui_test_utils::NavigateToURL(browser(), url); |
| EXPECT_EQ(url, observer.last_navigation_url()); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| EXPECT_FALSE(observer.last_initiator_origin().has_value()); |
| EXPECT_TRUE(WasUrlPathMonitored(url.path())); |
| } |
| |
| ResetMonitoredUrls(); |
| |
| // Simulate clicking on a same-site link. |
| { |
| content::TestNavigationObserver observer( |
| browser()->tab_strip_model()->GetActiveWebContents()); |
| GURL url(GetURLWithMockHost(origin_server, "/title2.html")); |
| bool success = false; |
| EXPECT_TRUE(ExecuteScriptAndExtractBool( |
| browser()->tab_strip_model()->GetActiveWebContents(), |
| "window.domAutomationController.send(clickSameSiteLink());", &success)); |
| EXPECT_TRUE(success); |
| EXPECT_TRUE( |
| WaitForLoadStop(browser()->tab_strip_model()->GetActiveWebContents())); |
| EXPECT_EQ(url, observer.last_navigation_url()); |
| EXPECT_TRUE(observer.last_navigation_succeeded()); |
| EXPECT_EQ(browser() |
| ->tab_strip_model() |
| ->GetActiveWebContents() |
| ->GetMainFrame() |
| ->GetLastCommittedOrigin(), |
| observer.last_initiator_origin()); |
| EXPECT_TRUE(WasUrlPathMonitored(url.path())); |
| } |
| } |
| |
| // Tests that Chrome-Proxy response headers are respected after the |
| // configuration is updated. |
| // |
| // When run under NetworkService, the DataReductionProxyURLLoaderThrottle |
| // decides whether to block-once based on the configured DRP servers. This |
| // config is in turn synchronized through the DataReductionProxyThrottleManager. |
| // |
| // The goal of this test is to ensure that this throttle sees the correct |
| // configuration when processing response headers (the UpdateConfig() test |
| // already checks that the network service sees the updated config). |
| IN_PROC_BROWSER_TEST_F(DataReductionProxyBrowsertest, |
| BlockOnceWorksAfterUpdateConfig) { |
| EventLog event_log; |
| |
| // Setup a DRP server that will reply with "Chrome-Proxy: block-once". |
| net::EmbeddedTestServer drp_server1; |
| drp_server1.RegisterRequestHandler(base::BindRepeating( |
| &DrpBlockOnceHandler, "drp_server1", base::Unretained(&event_log))); |
| ASSERT_TRUE(drp_server1.Start()); |
| |
| // Setup a DRP server that will reply with "Chrome-Proxy: block-once". |
| net::EmbeddedTestServer drp_server2; |
| drp_server2.RegisterRequestHandler(base::BindRepeating( |
| &DrpBlockOnceHandler, "drp_server2", base::Unretained(&event_log))); |
| ASSERT_TRUE(drp_server2.Start()); |
| |
| // Regular server that will respond with the request path as body. |
| net::EmbeddedTestServer direct_server; |
| direct_server.RegisterRequestHandler(base::BindRepeating( |
| &RespondWithRequestPathHandler, "direct_server", &event_log)); |
| ASSERT_TRUE(direct_server.Start()); |
| |
| // Change the DRP configuration so that |drp_server1| is the current DRP. |
| SetConfig(CreateConfigForServer(drp_server1)); |
| SimulateNetworkChange(network::mojom::ConnectionType::CONNECTION_3G); |
| WaitForConfig(); |
| |
| // When issuing request /x1, it should first go to |drp_server1|, and then get |
| // restarted on |direct_server|. |
| const char kExpectedLog1[] = |
| "drp_server1 responded 502 for /x1\n" |
| "direct_server responded 200 for /x1\n"; |
| |
| GURL x1_url = GetURLWithMockHost(direct_server, "/x1"); |
| |
| // Test a browser-initiated request. |
| ui_test_utils::NavigateToURL(browser(), x1_url); |
| EXPECT_EQ("/x1", GetBody()); |
| EXPECT_EQ(kExpectedLog1, event_log.GetAndReset()); |
| |
| // Test a renderer initiated request. |
| EXPECT_EQ("/x1", ReadSubresourceFromRenderer(browser(), x1_url)); |
| EXPECT_EQ(kExpectedLog1, event_log.GetAndReset()); |
| |
| // Change the DRP configuration so that |drp_server2| is the current DRP. |
| SetConfig(CreateConfigForServer(drp_server2)); |
| SimulateNetworkChange(network::mojom::ConnectionType::CONNECTION_3G); |
| WaitForConfig(); |
| |
| // When issuing request /x2, it should first go to |drp_server2|, and then get |
| // restarted on |direct_server|. |
| const char kExpectedLog2[] = |
| "drp_server2 responded 502 for /x2\n" |
| "direct_server responded 200 for /x2\n"; |
| |
| // Test a browser-initiated request. |
| GURL x2_url = GetURLWithMockHost(direct_server, "/x2"); |
| ui_test_utils::NavigateToURL(browser(), x2_url); |
| EXPECT_EQ("/x2", GetBody()); |
| EXPECT_EQ(kExpectedLog2, event_log.GetAndReset()); |
| |
| // Test a renderer initiated request. |
| EXPECT_EQ("/x2", ReadSubresourceFromRenderer(browser(), x2_url)); |
| EXPECT_EQ(kExpectedLog2, event_log.GetAndReset()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(DataReductionProxyBrowsertest, NestedWebWorker) { |
| // Nested Web Workers exercise URLLoaderThrottles in interesting ways. Each |
| // worker runs on a separate thread and each one has an associated |
| // URLLoaderThrottleProvider. When spawning a nested worker, the outer |
| // worker's throttle provider is cloned on the outer worker's thread and then |
| // used on the inner worker's thread. This test verifies that the mojo |
| // connections between the throttles and DRP are set up correctly given the |
| // non-trivial threading scenario. |
| constexpr char kHtml[] = R"( |
| <html><body><script language="javascript"> |
| function workerImpl() { |
| function nestedWorkerImpl() { |
| postMessage('done'); |
| } |
| var blob = new Blob(['(' + nestedWorkerImpl.toString() + ')()'], |
| { type: 'application/javascript' }); |
| var nestedWorker = new Worker(URL.createObjectURL(blob)); |
| nestedWorker.onmessage = (event) => postMessage(event.data); |
| } |
| var blob = new Blob(['(' + workerImpl.toString() + ')()'], |
| { type: 'application/javascript' }); |
| var worker = new Worker(URL.createObjectURL(blob)); |
| worker.onmessage = (event) => document.title = event.data; |
| </script></body></html> |
| )"; |
| constexpr char kDestinationHost[] = "some.host"; |
| |
| net::EmbeddedTestServer drp_server; |
| drp_server.RegisterRequestHandler(base::BindLambdaForTesting( |
| [&kHtml, &kDestinationHost](const net::test_server::HttpRequest& request) |
| -> std::unique_ptr<net::test_server::HttpResponse> { |
| if (GetDestinationHost(request) != kDestinationHost) |
| return nullptr; |
| |
| auto response = std::make_unique<net::test_server::BasicHttpResponse>(); |
| response->set_content(kHtml); |
| return response; |
| })); |
| ASSERT_TRUE(drp_server.Start()); |
| |
| // Change the DRP configuration so that |drp_server| is the current DRP. |
| SetConfig(CreateConfigForServer(drp_server)); |
| SimulateNetworkChange(network::mojom::ConnectionType::CONNECTION_3G); |
| WaitForConfig(); |
| |
| ui_test_utils::NavigateToURL(browser(), |
| GURL(std::string("http://") + kDestinationHost)); |
| |
| const auto kExpectedTitle = base::ASCIIToUTF16("done"); |
| content::TitleWatcher title_watcher( |
| browser()->tab_strip_model()->GetActiveWebContents(), kExpectedTitle); |
| EXPECT_EQ(title_watcher.WaitAndGetTitle(), kExpectedTitle); |
| } |
| |
| // Test that disabling the kDataReductionProxyEnabledWithNetworkService disables |
| // the proxy. |
| class DataReductionProxyEnabledWithNetworkServiceHoldbackBrowserTest |
| : public ::testing::WithParamInterface<bool>, |
| public DataReductionProxyBrowsertest { |
| public: |
| void SetUp() override { |
| if (GetParam()) { |
| // Enable NetworkService, and disable |
| // kDataReductionProxyEnabledWithNetworkService. |
| scoped_feature_list_.InitWithFeatures( |
| {network::features::kNetworkService}, |
| {data_reduction_proxy::features:: |
| kDataReductionProxyEnabledWithNetworkService}); |
| } else { |
| // Enable both NetworkService and |
| // kDataReductionProxyEnabledWithNetworkService. |
| scoped_feature_list_.InitWithFeatures( |
| {network::features::kNetworkService, |
| data_reduction_proxy::features:: |
| kDataReductionProxyEnabledWithNetworkService}, |
| {}); |
| } |
| |
| InProcessBrowserTest::SetUp(); |
| } |
| |
| private: |
| base::test::ScopedFeatureList scoped_feature_list_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_P( |
| DataReductionProxyEnabledWithNetworkServiceHoldbackBrowserTest, |
| ProxyUsed) { |
| const bool holdback_enabled = GetParam(); |
| |
| net::EmbeddedTestServer origin_server; |
| origin_server.RegisterRequestHandler( |
| base::BindRepeating(&BasicResponse, kDummyBody)); |
| ASSERT_TRUE(origin_server.Start()); |
| |
| net::EmbeddedTestServer proxy_server; |
| proxy_server.RegisterRequestHandler( |
| base::BindRepeating(&BasicResponse, kPrimaryResponse)); |
| ASSERT_TRUE(proxy_server.Start()); |
| |
| SetConfig(CreateConfigForServer(proxy_server)); |
| // A network change forces the config to be fetched. |
| SimulateNetworkChange(network::mojom::ConnectionType::CONNECTION_3G); |
| if (!holdback_enabled) { |
| WaitForConfig(); |
| } |
| |
| ui_test_utils::NavigateToURL( |
| browser(), GetURLWithMockHost(origin_server, "/echoheader?Chrome-Proxy")); |
| |
| if (holdback_enabled) { |
| // Proxy should not be used. |
| EXPECT_EQ(GetBody(), kDummyBody); |
| } else { |
| // Proxy should be used. |
| EXPECT_EQ(GetBody(), kPrimaryResponse); |
| } |
| } |
| |
| // Parameter is true if kDataReductionProxyEnabledWithNetworkService is |
| // disabled. Disabling kDataReductionProxyEnabledWithNetworkService should |
| // disable data reduction proxy when network servicification is enabled. |
| INSTANTIATE_TEST_SUITE_P( |
| , |
| DataReductionProxyEnabledWithNetworkServiceHoldbackBrowserTest, |
| ::testing::Bool()); |
| |
| } // namespace data_reduction_proxy |