| // Copyright 2014 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/data_reduction_proxy/core/browser/data_reduction_proxy_delegate.h" |
| |
| #include <cmath> |
| |
| #include "base/command_line.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/time/default_tick_clock.h" |
| #include "base/time/tick_clock.h" |
| #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_bypass_stats.h" |
| #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_config.h" |
| #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_configurator.h" |
| #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_io_data.h" |
| #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_request_options.h" |
| #include "components/data_reduction_proxy/core/browser/data_reduction_proxy_util.h" |
| #include "components/data_reduction_proxy/core/common/data_reduction_proxy_event_creator.h" |
| #include "components/data_reduction_proxy/core/common/data_reduction_proxy_params.h" |
| #include "components/data_reduction_proxy/core/common/data_reduction_proxy_switches.h" |
| #include "net/base/host_port_pair.h" |
| #include "net/base/proxy_server.h" |
| #include "net/base/url_util.h" |
| #include "net/http/http_request_headers.h" |
| #include "net/http/http_response_headers.h" |
| #include "net/proxy_resolution/proxy_info.h" |
| |
| namespace data_reduction_proxy { |
| |
| namespace { |
| |
| static const char kDataReductionCoreProxy[] = "proxy.googlezip.net"; |
| |
| } // namespace |
| |
| DataReductionProxyDelegate::DataReductionProxyDelegate( |
| DataReductionProxyConfig* config, |
| const DataReductionProxyConfigurator* configurator, |
| DataReductionProxyEventCreator* event_creator, |
| DataReductionProxyBypassStats* bypass_stats, |
| net::NetLog* net_log) |
| : config_(config), |
| configurator_(configurator), |
| event_creator_(event_creator), |
| bypass_stats_(bypass_stats), |
| tick_clock_(base::DefaultTickClock::GetInstance()), |
| io_data_(nullptr), |
| net_log_(net_log) { |
| DCHECK(config_); |
| DCHECK(configurator_); |
| DCHECK(event_creator_); |
| DCHECK(bypass_stats_); |
| DCHECK(net_log_); |
| // Constructed on the UI thread, but should be checked on the IO thread. |
| thread_checker_.DetachFromThread(); |
| } |
| |
| DataReductionProxyDelegate::~DataReductionProxyDelegate() { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| net::NetworkChangeNotifier::RemoveNetworkChangeObserver(this); |
| } |
| |
| void DataReductionProxyDelegate::InitializeOnIOThread( |
| DataReductionProxyIOData* io_data) { |
| DCHECK(io_data); |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| net::NetworkChangeNotifier::AddNetworkChangeObserver(this); |
| io_data_ = io_data; |
| } |
| |
| void DataReductionProxyDelegate::OnResolveProxy( |
| const GURL& url, |
| const std::string& method, |
| const net::ProxyRetryInfoMap& proxy_retry_info, |
| net::ProxyInfo* result) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| DCHECK(result); |
| DCHECK(result->is_empty() || result->is_direct() || |
| !config_->FindConfiguredDataReductionProxy(result->proxy_server())); |
| |
| if (!params::IsIncludedInQuicFieldTrial()) |
| RecordQuicProxyStatus(QUIC_PROXY_DISABLED_VIA_FIELD_TRIAL); |
| |
| if (!util::EligibleForDataReductionProxy(*result, url, method)) |
| return; |
| |
| ResourceTypeProvider::ContentType content_type = |
| ResourceTypeProvider::CONTENT_TYPE_UNKNOWN; |
| if (io_data_ && io_data_->resource_type_provider()) |
| content_type = io_data_->resource_type_provider()->GetContentType(url); |
| |
| std::vector<DataReductionProxyServer> proxies_for_http = |
| params::IsIncludedInHoldbackFieldTrial() |
| ? std::vector<DataReductionProxyServer>() |
| : config_->GetProxiesForHttp(); |
| |
| // Remove the proxies that are unsupported for this request. |
| proxies_for_http.erase( |
| std::remove_if(proxies_for_http.begin(), proxies_for_http.end(), |
| [content_type](const DataReductionProxyServer& proxy) { |
| return !proxy.SupportsResourceType(content_type); |
| }), |
| proxies_for_http.end()); |
| |
| base::Optional<std::pair<bool /* is_secure_proxy */, bool /*is_core_proxy */>> |
| warmup_proxy = config_->GetInFlightWarmupProxyDetails(); |
| |
| bool is_warmup_url = warmup_proxy && |
| url.host() == params::GetWarmupURL().host() && |
| url.path() == params::GetWarmupURL().path(); |
| |
| if (is_warmup_url) { |
| // This is a request to fetch the warmup (aka probe) URL. |
| // |is_secure_proxy| and |is_core_proxy| indicate the properties of the |
| // proxy that is being currently probed. |
| bool is_secure_proxy = warmup_proxy->first; |
| bool is_core_proxy = warmup_proxy->second; |
| // Remove the proxies with properties that do not match the properties of |
| // the proxy that is being probed. |
| proxies_for_http.erase( |
| std::remove_if(proxies_for_http.begin(), proxies_for_http.end(), |
| [is_secure_proxy, |
| is_core_proxy](const DataReductionProxyServer& proxy) { |
| return proxy.IsSecureProxy() != is_secure_proxy || |
| proxy.IsCoreProxy() != is_core_proxy; |
| }), |
| proxies_for_http.end()); |
| } |
| |
| // If the proxy is disabled due to warmup URL fetch failing in the past, |
| // then enable it temporarily. This ensures that |configurator_| includes |
| // this proxy type when generating the |proxy_config|. |
| net::ProxyConfig proxy_config = configurator_->CreateProxyConfig( |
| is_warmup_url, config_->GetNetworkPropertiesManager(), proxies_for_http); |
| |
| net::ProxyInfo data_reduction_proxy_info; |
| if (util::ApplyProxyConfigToProxyInfo(proxy_config, proxy_retry_info, url, |
| &data_reduction_proxy_info)) { |
| DCHECK(!data_reduction_proxy_info.is_empty() && |
| !data_reduction_proxy_info.is_direct()); |
| result->OverrideProxyList(data_reduction_proxy_info.proxy_list()); |
| |
| GetAlternativeProxy(url, proxy_retry_info, result); |
| } |
| |
| DCHECK_GT(ResourceTypeProvider::CONTENT_TYPE_MAX, content_type); |
| |
| if (config_->enabled_by_user_and_reachable() && |
| url.SchemeIs(url::kHttpScheme) && !net::IsLocalhost(url) && |
| !params::IsIncludedInHoldbackFieldTrial()) { |
| UMA_HISTOGRAM_BOOLEAN("DataReductionProxy.ConfigService.HTTPRequests", |
| !config_->GetProxiesForHttp().empty()); |
| if (content_type == ResourceTypeProvider::CONTENT_TYPE_MAIN_FRAME) { |
| UMA_HISTOGRAM_BOOLEAN("DataReductionProxy.ConfigService.MainFrames", |
| !config_->GetProxiesForHttp().empty()); |
| } |
| } |
| } |
| |
| void DataReductionProxyDelegate::OnFallback(const net::ProxyServer& bad_proxy, |
| int net_error) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| if (bad_proxy.is_valid() && |
| config_->FindConfiguredDataReductionProxy(bad_proxy)) { |
| event_creator_->AddProxyFallbackEvent(net_log_, bad_proxy.ToURI(), |
| net_error); |
| } |
| |
| if (bypass_stats_) |
| bypass_stats_->OnProxyFallback(bad_proxy, net_error); |
| } |
| |
| void DataReductionProxyDelegate::SetTickClockForTesting( |
| const base::TickClock* tick_clock) { |
| tick_clock_ = tick_clock; |
| // Update |last_network_change_time_| to the provided tick clock's current |
| // time for testing. |
| last_network_change_time_ = tick_clock_->NowTicks(); |
| } |
| |
| void DataReductionProxyDelegate::GetAlternativeProxy( |
| const GURL& url, |
| const net::ProxyRetryInfoMap& proxy_retry_info, |
| net::ProxyInfo* result) const { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| net::ProxyServer resolved_proxy_server = result->proxy_server(); |
| DCHECK(resolved_proxy_server.is_valid()); |
| DCHECK(config_->FindConfiguredDataReductionProxy(resolved_proxy_server)); |
| |
| if (!url.is_valid() || !url.SchemeIsHTTPOrHTTPS() || |
| url.SchemeIsCryptographic()) { |
| return; |
| } |
| |
| if (!params::IsIncludedInQuicFieldTrial()) { |
| // RecordQuicProxyStatus already called by OnResolveProxy. |
| return; |
| } |
| |
| if (!resolved_proxy_server.is_https()) |
| return; |
| |
| if (!SupportsQUIC(resolved_proxy_server)) { |
| RecordQuicProxyStatus(QUIC_PROXY_NOT_SUPPORTED); |
| return; |
| } |
| |
| net::ProxyInfo alternative_proxy_info; |
| alternative_proxy_info.UseProxyServer(net::ProxyServer( |
| net::ProxyServer::SCHEME_QUIC, resolved_proxy_server.host_port_pair())); |
| alternative_proxy_info.DeprioritizeBadProxies(proxy_retry_info); |
| |
| if (alternative_proxy_info.is_empty()) { |
| RecordQuicProxyStatus(QUIC_PROXY_STATUS_MARKED_AS_BROKEN); |
| return; |
| } |
| |
| RecordQuicProxyStatus(QUIC_PROXY_STATUS_AVAILABLE); |
| result->SetAlternativeProxy(alternative_proxy_info.proxy_server()); |
| } |
| |
| bool DataReductionProxyDelegate::SupportsQUIC( |
| const net::ProxyServer& proxy_server) const { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| // Enable QUIC for whitelisted proxies. |
| return params::IsQuicEnabledForNonCoreProxies() || |
| proxy_server == |
| net::ProxyServer(net::ProxyServer::SCHEME_HTTPS, |
| net::HostPortPair(kDataReductionCoreProxy, 443)); |
| } |
| |
| void DataReductionProxyDelegate::RecordQuicProxyStatus( |
| QuicProxyStatus status) const { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| UMA_HISTOGRAM_ENUMERATION("DataReductionProxy.Quic.ProxyStatus", status, |
| QUIC_PROXY_STATUS_BOUNDARY); |
| } |
| |
| void DataReductionProxyDelegate::OnNetworkChanged( |
| net::NetworkChangeNotifier::ConnectionType type) { |
| DCHECK(thread_checker_.CalledOnValidThread()); |
| |
| if (type == net::NetworkChangeNotifier::CONNECTION_NONE) |
| return; |
| |
| last_network_change_time_ = tick_clock_->NowTicks(); |
| } |
| |
| } // namespace data_reduction_proxy |