1 // Copyright 2013 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 "chrome/browser/net/spdyproxy/proxy_advisor.h" 6 7 #include "base/command_line.h" 8 #include "base/logging.h" 9 #include "base/memory/scoped_ptr.h" 10 #include "base/prefs/pref_service.h" 11 #include "base/stl_util.h" 12 #include "chrome/common/chrome_switches.h" 13 #include "chrome/common/pref_names.h" 14 #include "components/data_reduction_proxy/browser/data_reduction_proxy_params.h" 15 #include "components/data_reduction_proxy/browser/data_reduction_proxy_settings.h" 16 #include "content/public/browser/browser_thread.h" 17 #include "net/base/load_flags.h" 18 #include "net/base/request_priority.h" 19 #include "net/http/http_status_code.h" 20 #include "net/proxy/proxy_info.h" 21 #include "net/proxy/proxy_service.h" 22 #include "net/url_request/url_request_context.h" 23 #include "net/url_request/url_request_context_getter.h" 24 25 // TODO(marq): Remove this class because it is not being used. 26 27 // Ensure data reduction features are available. 28 #if !defined(OS_ANDROID) && !defined(OS_IOS) 29 #error proxy_advisor should only be included in Android or iOS builds. 30 #endif 31 32 using content::BrowserThread; 33 using data_reduction_proxy::DataReductionProxySettings; 34 35 namespace { 36 const char kOmniboxMotivation[] = "omnibox"; 37 const char kLowThresholdOmniboxMotivation[] = "low_threshold_omnibox"; 38 const char kStartupDNSMotivation[] = "startup_dns"; 39 const char kEarlyLoadMotivation[] = "early_load"; 40 const char kLearnedReferralMotivation[] = "learned_referral"; 41 const char kLowThresholdLearnedReferralMotivation[] = 42 "low_threshold_learned_referral"; 43 const char kSelfReferralMotivation[] = "self_referral"; 44 const char kPageScanMotivation[] = "page_scan"; 45 46 // Maps a ResolutionMotivation to a string for use in the advisory HEAD 47 // request. 48 const char* MotivationName( 49 chrome_browser_net::UrlInfo::ResolutionMotivation motivation, 50 bool is_preconnect) { 51 switch (motivation) { 52 case chrome_browser_net::UrlInfo::OMNIBOX_MOTIVATED: 53 return 54 is_preconnect ? kOmniboxMotivation : kLowThresholdOmniboxMotivation; 55 case chrome_browser_net::UrlInfo::STARTUP_LIST_MOTIVATED: 56 return kStartupDNSMotivation; 57 case chrome_browser_net::UrlInfo::EARLY_LOAD_MOTIVATED: 58 return kEarlyLoadMotivation; 59 case chrome_browser_net::UrlInfo::LEARNED_REFERAL_MOTIVATED: 60 return is_preconnect ? 61 kLearnedReferralMotivation : kLowThresholdLearnedReferralMotivation; 62 case chrome_browser_net::UrlInfo::SELF_REFERAL_MOTIVATED: 63 return kSelfReferralMotivation; 64 case chrome_browser_net::UrlInfo::PAGE_SCAN_MOTIVATED: 65 return kPageScanMotivation; 66 default: 67 // Other motivations should never be passed to here. 68 NOTREACHED(); 69 break; 70 } 71 NOTREACHED(); 72 return ""; 73 } 74 75 } // namespace 76 77 ProxyAdvisor::ProxyAdvisor(PrefService* pref_service, 78 net::URLRequestContextGetter* context_getter) 79 : context_getter_(context_getter) { 80 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 81 82 // pref_service may be null in mock test subclasses. 83 if (pref_service) { 84 proxy_pref_member_.Init( 85 data_reduction_proxy::prefs::kDataReductionProxyEnabled, 86 pref_service, 87 base::Bind(&ProxyAdvisor::UpdateProxyState, base::Unretained(this))); 88 proxy_pref_member_.MoveToThread( 89 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO)); 90 } 91 } 92 93 ProxyAdvisor::~ProxyAdvisor() { 94 STLDeleteElements(&inflight_requests_); 95 } 96 97 void ProxyAdvisor::OnResponseStarted(net::URLRequest* request) { 98 const net::URLRequestStatus& status(request->status()); 99 if (!status.is_success()) { 100 DLOG(WARNING) << "Proxy advisory failed " 101 << "status:" << status.status() 102 << " error:" << status.error(); 103 } else if (request->GetResponseCode() != net::HTTP_OK) { 104 DLOG(WARNING) << "Proxy advisory status: " << request->GetResponseCode(); 105 } 106 RequestComplete(request); 107 } 108 109 void ProxyAdvisor::OnReadCompleted(net::URLRequest* request, int bytes_read) { 110 // No-op for now, as we don't care yet. 111 } 112 113 void ProxyAdvisor::Advise( 114 const GURL& url, 115 chrome_browser_net::UrlInfo::ResolutionMotivation motivation, 116 bool is_preconnect) { 117 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 118 119 if (!WouldProxyURL(url)) 120 return; 121 122 std::string motivation_name(MotivationName(motivation, is_preconnect)); 123 std::string header_value = motivation_name + " " + url.spec(); 124 net::URLRequestContext* context = context_getter_->GetURLRequestContext(); 125 data_reduction_proxy::DataReductionProxyParams params( 126 data_reduction_proxy::DataReductionProxyParams::kAllowed | 127 data_reduction_proxy::DataReductionProxyParams::kFallbackAllowed | 128 data_reduction_proxy::DataReductionProxyParams::kPromoAllowed); 129 std::string endpoint = 130 params.origin().spec() + "preconnect"; 131 scoped_ptr<net::URLRequest> request = context->CreateRequest( 132 GURL(endpoint), net::DEFAULT_PRIORITY, this, NULL); 133 request->set_method("HEAD"); 134 request->SetExtraRequestHeaderByName( 135 "Proxy-Host-Advisory", header_value, false); 136 request->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES | 137 net::LOAD_DO_NOT_SAVE_COOKIES | 138 net::LOAD_BYPASS_PROXY | 139 net::LOAD_DISABLE_CACHE); 140 net::URLRequest* raw_request = request.get(); 141 inflight_requests_.insert(request.release()); 142 raw_request->Start(); 143 } 144 145 // TODO(marq): Move this into DataReductionProxySettings, and add support for 146 // inspecting the current proxy configs -- if ResolveProxy on |url| can be 147 // done synchronously, then this is no longer an approximation. 148 bool ProxyAdvisor::WouldProxyURL(const GURL& url) { 149 if (!proxy_pref_member_.GetValue()) 150 return false; 151 152 if (url.SchemeIsSecure()) 153 return false; 154 155 return true; 156 } 157 158 void ProxyAdvisor::RequestComplete(net::URLRequest* request) { 159 DCHECK_EQ(1u, inflight_requests_.count(request)); 160 scoped_ptr<net::URLRequest> scoped_request_for_deletion(request); 161 inflight_requests_.erase(request); 162 // |scoped_request_for_deletion| will delete |request| 163 } 164 165 void ProxyAdvisor::UpdateProxyState() { 166 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 167 // Delete all inflight requests. Each request's destructor will call Cancel(). 168 STLDeleteElements(&inflight_requests_); 169 } 170