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