Home | History | Annotate | Download | only in spdyproxy
      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