Home | History | Annotate | Download | only in proxy
      1 // Copyright (c) 2009 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 "net/proxy/init_proxy_resolver.h"
      6 
      7 #include "base/compiler_specific.h"
      8 #include "base/format_macros.h"
      9 #include "base/logging.h"
     10 #include "base/string_util.h"
     11 #include "net/base/net_log.h"
     12 #include "net/base/net_errors.h"
     13 #include "net/proxy/proxy_config.h"
     14 #include "net/proxy/proxy_resolver.h"
     15 #include "net/proxy/proxy_script_fetcher.h"
     16 
     17 namespace net {
     18 
     19 // This is the hard-coded location used by the DNS portion of web proxy
     20 // auto-discovery.
     21 //
     22 // Note that we not use DNS devolution to find the WPAD host, since that could
     23 // be dangerous should our top level domain registry  become out of date.
     24 //
     25 // Instead we directly resolve "wpad", and let the operating system apply the
     26 // DNS suffix search paths. This is the same approach taken by Firefox, and
     27 // compatibility hasn't been an issue.
     28 //
     29 // For more details, also check out this comment:
     30 // http://code.google.com/p/chromium/issues/detail?id=18575#c20
     31 static const char kWpadUrl[] = "http://wpad/wpad.dat";
     32 
     33 InitProxyResolver::InitProxyResolver(ProxyResolver* resolver,
     34                                      ProxyScriptFetcher* proxy_script_fetcher,
     35                                      NetLog* net_log)
     36     : resolver_(resolver),
     37       proxy_script_fetcher_(proxy_script_fetcher),
     38       ALLOW_THIS_IN_INITIALIZER_LIST(io_callback_(
     39           this, &InitProxyResolver::OnIOCompletion)),
     40       user_callback_(NULL),
     41       current_pac_url_index_(0u),
     42       next_state_(STATE_NONE),
     43       net_log_(BoundNetLog::Make(
     44           net_log, NetLog::SOURCE_INIT_PROXY_RESOLVER)),
     45       effective_config_(NULL) {
     46 }
     47 
     48 InitProxyResolver::~InitProxyResolver() {
     49   if (next_state_ != STATE_NONE)
     50     Cancel();
     51 }
     52 
     53 int InitProxyResolver::Init(const ProxyConfig& config,
     54                             const base::TimeDelta wait_delay,
     55                             ProxyConfig* effective_config,
     56                             CompletionCallback* callback) {
     57   DCHECK_EQ(STATE_NONE, next_state_);
     58   DCHECK(callback);
     59   DCHECK(config.HasAutomaticSettings());
     60 
     61   net_log_.BeginEvent(NetLog::TYPE_INIT_PROXY_RESOLVER, NULL);
     62 
     63   // Save the |wait_delay| as a non-negative value.
     64   wait_delay_ = wait_delay;
     65   if (wait_delay_ < base::TimeDelta())
     66     wait_delay_ = base::TimeDelta();
     67 
     68   effective_config_ = effective_config;
     69 
     70   pac_urls_ = BuildPacUrlsFallbackList(config);
     71   DCHECK(!pac_urls_.empty());
     72 
     73   next_state_ = STATE_WAIT;
     74 
     75   int rv = DoLoop(OK);
     76   if (rv == ERR_IO_PENDING)
     77     user_callback_ = callback;
     78   else
     79     DidCompleteInit();
     80 
     81   return rv;
     82 }
     83 
     84 // Initialize the fallback rules.
     85 // (1) WPAD (DNS).
     86 // (2) Custom PAC URL.
     87 InitProxyResolver::UrlList InitProxyResolver::BuildPacUrlsFallbackList(
     88     const ProxyConfig& config) const {
     89   UrlList pac_urls;
     90   if (config.auto_detect())
     91     pac_urls.push_back(PacURL(true, GURL()));
     92   if (config.has_pac_url())
     93     pac_urls.push_back(PacURL(false, config.pac_url()));
     94   return pac_urls;
     95 }
     96 
     97 void InitProxyResolver::OnIOCompletion(int result) {
     98   DCHECK_NE(STATE_NONE, next_state_);
     99   int rv = DoLoop(result);
    100   if (rv != ERR_IO_PENDING) {
    101     DidCompleteInit();
    102     DoCallback(rv);
    103   }
    104 }
    105 
    106 int InitProxyResolver::DoLoop(int result) {
    107   DCHECK_NE(next_state_, STATE_NONE);
    108   int rv = result;
    109   do {
    110     State state = next_state_;
    111     next_state_ = STATE_NONE;
    112     switch (state) {
    113       case STATE_WAIT:
    114         DCHECK_EQ(OK, rv);
    115         rv = DoWait();
    116         break;
    117       case STATE_WAIT_COMPLETE:
    118         rv = DoWaitComplete(rv);
    119         break;
    120       case STATE_FETCH_PAC_SCRIPT:
    121         DCHECK_EQ(OK, rv);
    122         rv = DoFetchPacScript();
    123         break;
    124       case STATE_FETCH_PAC_SCRIPT_COMPLETE:
    125         rv = DoFetchPacScriptComplete(rv);
    126         break;
    127       case STATE_SET_PAC_SCRIPT:
    128         DCHECK_EQ(OK, rv);
    129         rv = DoSetPacScript();
    130         break;
    131       case STATE_SET_PAC_SCRIPT_COMPLETE:
    132         rv = DoSetPacScriptComplete(rv);
    133         break;
    134       default:
    135         NOTREACHED() << "bad state";
    136         rv = ERR_UNEXPECTED;
    137         break;
    138     }
    139   } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
    140   return rv;
    141 }
    142 
    143 void InitProxyResolver::DoCallback(int result) {
    144   DCHECK_NE(ERR_IO_PENDING, result);
    145   DCHECK(user_callback_);
    146   user_callback_->Run(result);
    147 }
    148 
    149 int InitProxyResolver::DoWait() {
    150   next_state_ = STATE_WAIT_COMPLETE;
    151 
    152   // If no waiting is required, continue on to the next state.
    153   if (wait_delay_.ToInternalValue() == 0)
    154     return OK;
    155 
    156   // Otherwise wait the specified amount of time.
    157   wait_timer_.Start(wait_delay_, this, &InitProxyResolver::OnWaitTimerFired);
    158   net_log_.BeginEvent(NetLog::TYPE_INIT_PROXY_RESOLVER_WAIT, NULL);
    159   return ERR_IO_PENDING;
    160 }
    161 
    162 int InitProxyResolver::DoWaitComplete(int result) {
    163   DCHECK_EQ(OK, result);
    164   if (wait_delay_.ToInternalValue() != 0) {
    165     net_log_.EndEventWithNetErrorCode(NetLog::TYPE_INIT_PROXY_RESOLVER_WAIT,
    166                                       result);
    167   }
    168   next_state_ = GetStartState();
    169   return OK;
    170 }
    171 
    172 int InitProxyResolver::DoFetchPacScript() {
    173   DCHECK(resolver_->expects_pac_bytes());
    174 
    175   next_state_ = STATE_FETCH_PAC_SCRIPT_COMPLETE;
    176 
    177   const PacURL& pac_url = current_pac_url();
    178 
    179   const GURL effective_pac_url =
    180       pac_url.auto_detect ? GURL(kWpadUrl) : pac_url.url;
    181 
    182   net_log_.BeginEvent(
    183       NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT,
    184       make_scoped_refptr(new NetLogStringParameter(
    185           "url", effective_pac_url.possibly_invalid_spec())));
    186 
    187   if (!proxy_script_fetcher_) {
    188     net_log_.AddEvent(NetLog::TYPE_INIT_PROXY_RESOLVER_HAS_NO_FETCHER, NULL);
    189     return ERR_UNEXPECTED;
    190   }
    191 
    192   return proxy_script_fetcher_->Fetch(effective_pac_url,
    193                                       &pac_script_,
    194                                       &io_callback_);
    195 }
    196 
    197 int InitProxyResolver::DoFetchPacScriptComplete(int result) {
    198   DCHECK(resolver_->expects_pac_bytes());
    199 
    200   net_log_.EndEventWithNetErrorCode(
    201       NetLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT, result);
    202   if (result != OK)
    203     return TryToFallbackPacUrl(result);
    204 
    205   next_state_ = STATE_SET_PAC_SCRIPT;
    206   return result;
    207 }
    208 
    209 int InitProxyResolver::DoSetPacScript() {
    210   net_log_.BeginEvent(NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT, NULL);
    211 
    212   const PacURL& pac_url = current_pac_url();
    213 
    214   next_state_ = STATE_SET_PAC_SCRIPT_COMPLETE;
    215 
    216   scoped_refptr<ProxyResolverScriptData> script_data;
    217 
    218   if (resolver_->expects_pac_bytes()) {
    219     script_data = ProxyResolverScriptData::FromUTF16(pac_script_);
    220   } else {
    221     script_data = pac_url.auto_detect ?
    222         ProxyResolverScriptData::ForAutoDetect() :
    223         ProxyResolverScriptData::FromURL(pac_url.url);
    224   }
    225 
    226   return resolver_->SetPacScript(script_data, &io_callback_);
    227 }
    228 
    229 int InitProxyResolver::DoSetPacScriptComplete(int result) {
    230   net_log_.EndEventWithNetErrorCode(
    231       NetLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT, result);
    232   if (result != OK)
    233     return TryToFallbackPacUrl(result);
    234 
    235   // Let the caller know which automatic setting we ended up initializing the
    236   // resolver for (there may have been multiple fallbacks to choose from.)
    237   if (effective_config_) {
    238     if (current_pac_url().auto_detect && resolver_->expects_pac_bytes()) {
    239       *effective_config_ =
    240           ProxyConfig::CreateFromCustomPacURL(GURL(kWpadUrl));
    241     } else if (current_pac_url().auto_detect) {
    242       *effective_config_ = ProxyConfig::CreateAutoDetect();
    243     } else {
    244       *effective_config_ =
    245           ProxyConfig::CreateFromCustomPacURL(current_pac_url().url);
    246     }
    247   }
    248 
    249   return result;
    250 }
    251 
    252 int InitProxyResolver::TryToFallbackPacUrl(int error) {
    253   DCHECK_LT(error, 0);
    254 
    255   if (current_pac_url_index_ + 1 >= pac_urls_.size()) {
    256     // Nothing left to fall back to.
    257     return error;
    258   }
    259 
    260   // Advance to next URL in our list.
    261   ++current_pac_url_index_;
    262 
    263   net_log_.AddEvent(
    264       NetLog::TYPE_INIT_PROXY_RESOLVER_FALLING_BACK_TO_NEXT_PAC_URL, NULL);
    265 
    266   next_state_ = GetStartState();
    267 
    268   return OK;
    269 }
    270 
    271 InitProxyResolver::State InitProxyResolver::GetStartState() const {
    272   return resolver_->expects_pac_bytes() ?
    273       STATE_FETCH_PAC_SCRIPT : STATE_SET_PAC_SCRIPT;
    274 }
    275 
    276 const InitProxyResolver::PacURL& InitProxyResolver::current_pac_url() const {
    277   DCHECK_LT(current_pac_url_index_, pac_urls_.size());
    278   return pac_urls_[current_pac_url_index_];
    279 }
    280 
    281 void InitProxyResolver::OnWaitTimerFired() {
    282   OnIOCompletion(OK);
    283 }
    284 
    285 void InitProxyResolver::DidCompleteInit() {
    286   net_log_.EndEvent(NetLog::TYPE_INIT_PROXY_RESOLVER, NULL);
    287 }
    288 
    289 void InitProxyResolver::Cancel() {
    290   DCHECK_NE(STATE_NONE, next_state_);
    291 
    292   net_log_.AddEvent(NetLog::TYPE_CANCELLED, NULL);
    293 
    294   switch (next_state_) {
    295     case STATE_WAIT_COMPLETE:
    296       wait_timer_.Stop();
    297       break;
    298     case STATE_FETCH_PAC_SCRIPT_COMPLETE:
    299       proxy_script_fetcher_->Cancel();
    300       break;
    301     case STATE_SET_PAC_SCRIPT_COMPLETE:
    302       resolver_->CancelSetPacScript();
    303       break;
    304     default:
    305       NOTREACHED();
    306       break;
    307   }
    308 
    309   DidCompleteInit();
    310 }
    311 
    312 }  // namespace net
    313