1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "components/data_reduction_proxy/browser/data_reduction_proxy_params.h" 6 7 #include <vector> 8 9 #include "base/command_line.h" 10 #include "base/memory/scoped_ptr.h" 11 #include "base/metrics/field_trial.h" 12 #include "base/time/time.h" 13 #include "components/data_reduction_proxy/common/data_reduction_proxy_switches.h" 14 #include "net/base/host_port_pair.h" 15 #include "net/proxy/proxy_config.h" 16 #include "net/proxy/proxy_info.h" 17 #include "net/proxy/proxy_retry_info.h" 18 #include "net/proxy/proxy_server.h" 19 #include "net/proxy/proxy_service.h" 20 #include "net/url_request/url_request.h" 21 #include "net/url_request/url_request_context.h" 22 #include "url/url_constants.h" 23 24 using base::FieldTrialList; 25 26 namespace { 27 28 const char kEnabled[] = "Enabled"; 29 const char kDefaultOrigin[] = "https://proxy.googlezip.net:443/"; 30 const char kDevOrigin[] = "https://proxy-dev.googlezip.net:443/"; 31 const char kDevFallbackOrigin[] = "http://proxy-dev.googlezip.net:80/"; 32 const char kDefaultFallbackOrigin[] = "http://compress.googlezip.net:80/"; 33 // This is for a proxy that supports HTTP CONNECT to tunnel SSL traffic. 34 // The proxy listens on port 443, but uses the HTTP protocol to set up 35 // the tunnel, not HTTPS. 36 const char kDefaultSslOrigin[] = "http://ssl.googlezip.net:443/"; 37 const char kDefaultAltOrigin[] = "http://ssl.googlezip.net:80/"; 38 const char kDefaultAltFallbackOrigin[] = "http://ssl.googlezip.net:80/"; 39 const char kDefaultProbeUrl[] = "http://check.googlezip.net/connect"; 40 const char kDefaultWarmupUrl[] = "http://www.gstatic.com/generate_204"; 41 42 } // namespace 43 44 namespace data_reduction_proxy { 45 46 // static 47 bool DataReductionProxyParams::IsIncludedInAlternativeFieldTrial() { 48 const std::string group_name = base::FieldTrialList::FindFullName( 49 "DataCompressionProxyAlternativeConfiguration"); 50 if (CommandLine::ForCurrentProcess()->HasSwitch( 51 data_reduction_proxy::switches::kEnableDataReductionProxyAlt)) { 52 return true; 53 } 54 return group_name == kEnabled; 55 } 56 57 // static 58 bool DataReductionProxyParams::IsIncludedInPromoFieldTrial() { 59 return FieldTrialList::FindFullName( 60 "DataCompressionProxyPromoVisibility") == kEnabled; 61 } 62 63 // static 64 bool DataReductionProxyParams::IsIncludedInPreconnectHintingFieldTrial() { 65 return FieldTrialList::FindFullName( 66 "DataCompressionProxyPreconnectHints") == kEnabled; 67 } 68 69 // static 70 bool DataReductionProxyParams::IsIncludedInCriticalPathBypassFieldTrial() { 71 return FieldTrialList::FindFullName( 72 "DataCompressionProxyCriticalBypass") == kEnabled; 73 } 74 75 // static 76 bool DataReductionProxyParams::IsIncludedInHoldbackFieldTrial() { 77 return FieldTrialList::FindFullName( 78 "DataCompressionProxyHoldback") == kEnabled; 79 } 80 81 // static 82 bool DataReductionProxyParams:: 83 IsIncludedInRemoveMissingViaHeaderOtherBypassFieldTrial() { 84 return FieldTrialList::FindFullName( 85 "DataReductionProxyRemoveMissingViaHeaderOtherBypass") == kEnabled; 86 } 87 88 DataReductionProxyTypeInfo::DataReductionProxyTypeInfo() 89 : proxy_servers(), 90 is_fallback(false), 91 is_alternative(false), 92 is_ssl(false) { 93 } 94 95 DataReductionProxyTypeInfo::~DataReductionProxyTypeInfo(){ 96 } 97 98 DataReductionProxyParams::DataReductionProxyParams(int flags) 99 : allowed_((flags & kAllowed) == kAllowed), 100 fallback_allowed_((flags & kFallbackAllowed) == kFallbackAllowed), 101 alt_allowed_((flags & kAlternativeAllowed) == kAlternativeAllowed), 102 alt_fallback_allowed_( 103 (flags & kAlternativeFallbackAllowed) == kAlternativeFallbackAllowed), 104 promo_allowed_((flags & kPromoAllowed) == kPromoAllowed), 105 holdback_((flags & kHoldback) == kHoldback), 106 configured_on_command_line_(false) { 107 bool result = Init( 108 allowed_, fallback_allowed_, alt_allowed_, alt_fallback_allowed_); 109 DCHECK(result); 110 } 111 112 scoped_ptr<DataReductionProxyParams> DataReductionProxyParams::Clone() { 113 return scoped_ptr<DataReductionProxyParams>( 114 new DataReductionProxyParams(*this)); 115 } 116 117 DataReductionProxyParams::DataReductionProxyParams( 118 const DataReductionProxyParams& other) 119 : origin_(other.origin_), 120 fallback_origin_(other.fallback_origin_), 121 ssl_origin_(other.ssl_origin_), 122 alt_origin_(other.alt_origin_), 123 alt_fallback_origin_(other.alt_fallback_origin_), 124 probe_url_(other.probe_url_), 125 warmup_url_(other.warmup_url_), 126 allowed_(other.allowed_), 127 fallback_allowed_(other.fallback_allowed_), 128 alt_allowed_(other.alt_allowed_), 129 alt_fallback_allowed_(other.alt_fallback_allowed_), 130 promo_allowed_(other.promo_allowed_), 131 holdback_(other.holdback_), 132 configured_on_command_line_(other.configured_on_command_line_) { 133 } 134 135 DataReductionProxyParams::~DataReductionProxyParams() { 136 } 137 138 DataReductionProxyParams::DataReductionProxyList 139 DataReductionProxyParams::GetAllowedProxies() const { 140 DataReductionProxyList list; 141 if (allowed_) { 142 list.push_back(origin_); 143 } 144 if (allowed_ && fallback_allowed_) 145 list.push_back(fallback_origin_); 146 if (alt_allowed_) { 147 list.push_back(alt_origin_); 148 list.push_back(ssl_origin_); 149 } 150 if (alt_allowed_ && alt_fallback_allowed_) 151 list.push_back(alt_fallback_origin_); 152 return list; 153 } 154 155 DataReductionProxyParams::DataReductionProxyParams(int flags, 156 bool should_call_init) 157 : allowed_((flags & kAllowed) == kAllowed), 158 fallback_allowed_((flags & kFallbackAllowed) == kFallbackAllowed), 159 alt_allowed_((flags & kAlternativeAllowed) == kAlternativeAllowed), 160 alt_fallback_allowed_( 161 (flags & kAlternativeFallbackAllowed) == kAlternativeFallbackAllowed), 162 promo_allowed_((flags & kPromoAllowed) == kPromoAllowed), 163 holdback_((flags & kHoldback) == kHoldback), 164 configured_on_command_line_(false) { 165 if (should_call_init) { 166 bool result = Init( 167 allowed_, fallback_allowed_, alt_allowed_, alt_fallback_allowed_); 168 DCHECK(result); 169 } 170 } 171 172 bool DataReductionProxyParams::Init(bool allowed, 173 bool fallback_allowed, 174 bool alt_allowed, 175 bool alt_fallback_allowed) { 176 InitWithoutChecks(); 177 // Verify that all necessary params are set. 178 if (allowed) { 179 if (!origin_.is_valid()) { 180 DVLOG(1) << "Invalid data reduction proxy origin: " << origin_.spec(); 181 return false; 182 } 183 } 184 185 if (allowed && fallback_allowed) { 186 if (!fallback_origin_.is_valid()) { 187 DVLOG(1) << "Invalid data reduction proxy fallback origin: " 188 << fallback_origin_.spec(); 189 return false; 190 } 191 } 192 193 if (alt_allowed) { 194 if (!allowed) { 195 DVLOG(1) << "Alternative data reduction proxy configuration cannot " 196 << "be allowed if the regular configuration is not allowed"; 197 return false; 198 } 199 if (!alt_origin_.is_valid()) { 200 DVLOG(1) << "Invalid alternative origin:" << alt_origin_.spec(); 201 return false; 202 } 203 if (!ssl_origin_.is_valid()) { 204 DVLOG(1) << "Invalid ssl origin: " << ssl_origin_.spec(); 205 return false; 206 } 207 } 208 209 if (alt_allowed && alt_fallback_allowed) { 210 if (!alt_fallback_origin_.is_valid()) { 211 DVLOG(1) << "Invalid alternative fallback origin:" 212 << alt_fallback_origin_.spec(); 213 return false; 214 } 215 } 216 217 if (allowed && !probe_url_.is_valid()) { 218 DVLOG(1) << "Invalid probe url: <null>"; 219 return false; 220 } 221 222 if (fallback_allowed_ && !allowed_) { 223 DVLOG(1) << "The data reduction proxy fallback cannot be allowed if " 224 << "the data reduction proxy is not allowed"; 225 return false; 226 } 227 if (alt_fallback_allowed_ && !alt_allowed_) { 228 DVLOG(1) << "The data reduction proxy alternative fallback cannot be " 229 << "allowed if the alternative data reduction proxy is not allowed"; 230 return false; 231 } 232 if (promo_allowed_ && !allowed_) { 233 DVLOG(1) << "The data reduction proxy promo cannot be allowed if the " 234 << "data reduction proxy is not allowed"; 235 return false; 236 } 237 return true; 238 239 } 240 241 void DataReductionProxyParams::InitWithoutChecks() { 242 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 243 std::string origin; 244 if (!command_line.HasSwitch(switches::kDisableDataReductionProxyDev)) { 245 origin = command_line.GetSwitchValueASCII( 246 switches::kDataReductionProxyDev); 247 } 248 if (origin.empty()) 249 origin = command_line.GetSwitchValueASCII(switches::kDataReductionProxy); 250 std::string fallback_origin = 251 command_line.GetSwitchValueASCII(switches::kDataReductionProxyFallback); 252 std::string ssl_origin = 253 command_line.GetSwitchValueASCII(switches::kDataReductionSSLProxy); 254 std::string alt_origin = 255 command_line.GetSwitchValueASCII(switches::kDataReductionProxyAlt); 256 std::string alt_fallback_origin = command_line.GetSwitchValueASCII( 257 switches::kDataReductionProxyAltFallback); 258 259 configured_on_command_line_ = 260 !(origin.empty() && fallback_origin.empty() && ssl_origin.empty() && 261 alt_origin.empty() && alt_fallback_origin.empty()); 262 263 264 // Configuring the proxy on the command line overrides the values of 265 // |allowed_| and |alt_allowed_|. 266 if (configured_on_command_line_) 267 allowed_ = true; 268 if (!(ssl_origin.empty() && 269 alt_origin.empty())) 270 alt_allowed_ = true; 271 272 std::string probe_url = command_line.GetSwitchValueASCII( 273 switches::kDataReductionProxyProbeURL); 274 std::string warmup_url = command_line.GetSwitchValueASCII( 275 switches::kDataReductionProxyWarmupURL); 276 277 // Set from preprocessor constants those params that are not specified on the 278 // command line. 279 if (origin.empty()) 280 origin = GetDefaultDevOrigin(); 281 if (origin.empty()) 282 origin = GetDefaultOrigin(); 283 if (fallback_origin.empty()) 284 fallback_origin = GetDefaultDevFallbackOrigin(); 285 if (fallback_origin.empty()) 286 fallback_origin = GetDefaultFallbackOrigin(); 287 if (ssl_origin.empty()) 288 ssl_origin = GetDefaultSSLOrigin(); 289 if (alt_origin.empty()) 290 alt_origin = GetDefaultAltOrigin(); 291 if (alt_fallback_origin.empty()) 292 alt_fallback_origin = GetDefaultAltFallbackOrigin(); 293 if (probe_url.empty()) 294 probe_url = GetDefaultProbeURL(); 295 if (warmup_url.empty()) 296 warmup_url = GetDefaultWarmupURL(); 297 298 origin_ = GURL(origin); 299 fallback_origin_ = GURL(fallback_origin); 300 ssl_origin_ = GURL(ssl_origin); 301 alt_origin_ = GURL(alt_origin); 302 alt_fallback_origin_ = GURL(alt_fallback_origin); 303 probe_url_ = GURL(probe_url); 304 warmup_url_ = GURL(warmup_url); 305 306 } 307 308 bool DataReductionProxyParams::WasDataReductionProxyUsed( 309 const net::URLRequest* request, 310 DataReductionProxyTypeInfo* proxy_info) const { 311 DCHECK(request); 312 return IsDataReductionProxy(request->proxy_server(), proxy_info); 313 } 314 315 bool DataReductionProxyParams::IsDataReductionProxy( 316 const net::HostPortPair& host_port_pair, 317 DataReductionProxyTypeInfo* proxy_info) const { 318 if (net::HostPortPair::FromURL(origin()).Equals(host_port_pair)) { 319 if (proxy_info) { 320 proxy_info->proxy_servers.first = origin(); 321 if (fallback_allowed()) 322 proxy_info->proxy_servers.second = fallback_origin(); 323 } 324 return true; 325 } 326 327 if (fallback_allowed() && 328 net::HostPortPair::FromURL(fallback_origin()).Equals(host_port_pair)) { 329 if (proxy_info) { 330 proxy_info->proxy_servers.first = fallback_origin(); 331 proxy_info->proxy_servers.second = GURL(); 332 proxy_info->is_fallback = true; 333 } 334 return true; 335 } 336 if (net::HostPortPair::FromURL(alt_origin()).Equals(host_port_pair)) { 337 if (proxy_info) { 338 proxy_info->proxy_servers.first = alt_origin(); 339 proxy_info->is_alternative = true; 340 if (alternative_fallback_allowed()) 341 proxy_info->proxy_servers.second = alt_fallback_origin(); 342 } 343 return true; 344 } 345 if (alternative_fallback_allowed() && 346 net::HostPortPair::FromURL(alt_fallback_origin()).Equals( 347 host_port_pair)) { 348 if (proxy_info) { 349 proxy_info->proxy_servers.first = alt_fallback_origin(); 350 proxy_info->proxy_servers.second = GURL(); 351 proxy_info->is_fallback = true; 352 proxy_info->is_alternative = true; 353 } 354 return true; 355 } 356 if (net::HostPortPair::FromURL(ssl_origin()).Equals(host_port_pair)) { 357 if (proxy_info) { 358 proxy_info->proxy_servers.first = ssl_origin(); 359 proxy_info->proxy_servers.second = GURL(); 360 proxy_info->is_ssl = true; 361 } 362 return true; 363 } 364 return false; 365 } 366 367 bool DataReductionProxyParams::IsBypassedByDataReductionProxyLocalRules( 368 const net::URLRequest& request, 369 const net::ProxyConfig& data_reduction_proxy_config) const { 370 DCHECK(request.context()); 371 DCHECK(request.context()->proxy_service()); 372 net::ProxyInfo result; 373 data_reduction_proxy_config.proxy_rules().Apply( 374 request.url(), &result); 375 if (!result.proxy_server().is_valid()) 376 return true; 377 if (result.proxy_server().is_direct()) 378 return true; 379 return !IsDataReductionProxy(result.proxy_server().host_port_pair(), NULL); 380 } 381 382 std::string DataReductionProxyParams::GetDefaultDevOrigin() const { 383 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 384 if (command_line.HasSwitch(switches::kDisableDataReductionProxyDev)) 385 return std::string(); 386 if (command_line.HasSwitch(switches::kEnableDataReductionProxyDev) || 387 (FieldTrialList::FindFullName("DataCompressionProxyDevRollout") == 388 kEnabled)) { 389 return kDevOrigin; 390 } 391 return std::string(); 392 } 393 394 std::string DataReductionProxyParams::GetDefaultDevFallbackOrigin() const { 395 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); 396 if (command_line.HasSwitch(switches::kDisableDataReductionProxyDev)) 397 return std::string(); 398 if (command_line.HasSwitch(switches::kEnableDataReductionProxyDev) || 399 (FieldTrialList::FindFullName("DataCompressionProxyDevRollout") == 400 kEnabled)) { 401 return kDevFallbackOrigin; 402 } 403 return std::string(); 404 } 405 406 bool DataReductionProxyParams::AreDataReductionProxiesBypassed( 407 const net::URLRequest& request, base::TimeDelta* min_retry_delay) const { 408 if (request.context() != NULL && 409 request.context()->proxy_service() != NULL) { 410 return AreProxiesBypassed( 411 request.context()->proxy_service()->proxy_retry_info(), 412 request.url().SchemeIs(url::kHttpsScheme), 413 min_retry_delay); 414 } 415 416 return false; 417 } 418 419 bool DataReductionProxyParams::AreProxiesBypassed( 420 const net::ProxyRetryInfoMap& retry_map, 421 bool is_https, 422 base::TimeDelta* min_retry_delay) const { 423 if (retry_map.size() == 0) 424 return false; 425 426 // If the request is https, consider only the ssl proxy. 427 if (is_https) { 428 if (alt_allowed_) { 429 return ArePrimaryAndFallbackBypassed( 430 retry_map, ssl_origin_, GURL(), min_retry_delay); 431 } 432 NOTREACHED(); 433 return false; 434 } 435 436 if (allowed_ && ArePrimaryAndFallbackBypassed( 437 retry_map, origin_, fallback_origin_, min_retry_delay)) { 438 return true; 439 } 440 441 if (alt_allowed_ && ArePrimaryAndFallbackBypassed( 442 retry_map, alt_origin_, alt_fallback_origin_, min_retry_delay)) { 443 return true; 444 } 445 446 return false; 447 } 448 449 bool DataReductionProxyParams::ArePrimaryAndFallbackBypassed( 450 const net::ProxyRetryInfoMap& retry_map, 451 const GURL& primary, 452 const GURL& fallback, 453 base::TimeDelta* min_retry_delay) const { 454 net::ProxyRetryInfoMap::const_iterator found = retry_map.end(); 455 if (min_retry_delay) 456 *min_retry_delay = base::TimeDelta::Max(); 457 458 // Look for the primary proxy in the retry map. This must be done before 459 // looking for the fallback in order to assign |min_retry_delay| if the 460 // primary proxy has a shorter delay. 461 if (!fallback_allowed_ || !fallback.is_valid() || min_retry_delay) { 462 found = retry_map.find( 463 net::ProxyServer(primary.SchemeIs(url::kHttpsScheme) ? 464 net::ProxyServer::SCHEME_HTTPS : 465 net::ProxyServer::SCHEME_HTTP, 466 net::HostPortPair::FromURL(primary)).ToURI()); 467 if (found != retry_map.end() && min_retry_delay) { 468 *min_retry_delay = found->second.current_delay; 469 } 470 } 471 472 if (fallback_allowed_ && fallback.is_valid()) { 473 // If fallback is allowed, only the fallback proxy needs to be on the retry 474 // map to know if there was a bypass. We can reset found and forget if the 475 // primary was on the retry map. 476 found = retry_map.find( 477 net::ProxyServer(fallback.SchemeIs(url::kHttpsScheme) ? 478 net::ProxyServer::SCHEME_HTTPS : 479 net::ProxyServer::SCHEME_HTTP, 480 net::HostPortPair::FromURL(fallback)).ToURI()); 481 if (found != retry_map.end() && 482 min_retry_delay && 483 *min_retry_delay > found->second.current_delay) { 484 *min_retry_delay = found->second.current_delay; 485 } 486 } 487 488 return found != retry_map.end(); 489 } 490 491 // TODO(kundaji): Remove tests for macro definitions. 492 std::string DataReductionProxyParams::GetDefaultOrigin() const { 493 return kDefaultOrigin; 494 } 495 496 std::string DataReductionProxyParams::GetDefaultFallbackOrigin() const { 497 return kDefaultFallbackOrigin; 498 } 499 500 std::string DataReductionProxyParams::GetDefaultSSLOrigin() const { 501 return kDefaultSslOrigin; 502 } 503 504 std::string DataReductionProxyParams::GetDefaultAltOrigin() const { 505 return kDefaultAltOrigin; 506 } 507 508 std::string DataReductionProxyParams::GetDefaultAltFallbackOrigin() const { 509 return kDefaultAltFallbackOrigin; 510 } 511 512 std::string DataReductionProxyParams::GetDefaultProbeURL() const { 513 return kDefaultProbeUrl; 514 } 515 516 std::string DataReductionProxyParams::GetDefaultWarmupURL() const { 517 return kDefaultWarmupUrl; 518 } 519 520 } // namespace data_reduction_proxy 521