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/load_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 InitProxyResolver::InitProxyResolver(ProxyResolver* resolver, 20 ProxyScriptFetcher* proxy_script_fetcher) 21 : resolver_(resolver), 22 proxy_script_fetcher_(proxy_script_fetcher), 23 ALLOW_THIS_IN_INITIALIZER_LIST(io_callback_( 24 this, &InitProxyResolver::OnIOCompletion)), 25 user_callback_(NULL), 26 current_pac_url_index_(0u), 27 next_state_(STATE_NONE) { 28 } 29 30 InitProxyResolver::~InitProxyResolver() { 31 if (next_state_ != STATE_NONE) 32 Cancel(); 33 } 34 35 int InitProxyResolver::Init(const ProxyConfig& config, 36 CompletionCallback* callback, 37 LoadLog* load_log) { 38 DCHECK_EQ(STATE_NONE, next_state_); 39 DCHECK(callback); 40 DCHECK(config.MayRequirePACResolver()); 41 DCHECK(!load_log_); 42 43 load_log_ = load_log; 44 45 LoadLog::BeginEvent(load_log_, LoadLog::TYPE_INIT_PROXY_RESOLVER); 46 47 pac_urls_ = BuildPacUrlsFallbackList(config); 48 DCHECK(!pac_urls_.empty()); 49 50 next_state_ = GetStartState(); 51 52 int rv = DoLoop(OK); 53 if (rv == ERR_IO_PENDING) 54 user_callback_ = callback; 55 else 56 DidCompleteInit(); 57 58 return rv; 59 } 60 61 // Initialize the fallback rules. 62 // (1) WPAD 63 // (2) Custom PAC URL. 64 InitProxyResolver::UrlList InitProxyResolver::BuildPacUrlsFallbackList( 65 const ProxyConfig& config) const { 66 UrlList pac_urls; 67 if (config.auto_detect) { 68 GURL pac_url = resolver_->expects_pac_bytes() ? 69 GURL("http://wpad/wpad.dat") : GURL(); 70 pac_urls.push_back(pac_url); 71 } 72 if (config.pac_url.is_valid()) 73 pac_urls.push_back(config.pac_url); 74 return pac_urls; 75 } 76 77 void InitProxyResolver::OnIOCompletion(int result) { 78 DCHECK_NE(STATE_NONE, next_state_); 79 int rv = DoLoop(result); 80 if (rv != ERR_IO_PENDING) { 81 DidCompleteInit(); 82 DoCallback(rv); 83 } 84 } 85 86 int InitProxyResolver::DoLoop(int result) { 87 DCHECK_NE(next_state_, STATE_NONE); 88 int rv = result; 89 do { 90 State state = next_state_; 91 next_state_ = STATE_NONE; 92 switch (state) { 93 case STATE_FETCH_PAC_SCRIPT: 94 DCHECK_EQ(OK, rv); 95 rv = DoFetchPacScript(); 96 break; 97 case STATE_FETCH_PAC_SCRIPT_COMPLETE: 98 rv = DoFetchPacScriptComplete(rv); 99 break; 100 case STATE_SET_PAC_SCRIPT: 101 DCHECK_EQ(OK, rv); 102 rv = DoSetPacScript(); 103 break; 104 case STATE_SET_PAC_SCRIPT_COMPLETE: 105 rv = DoSetPacScriptComplete(rv); 106 break; 107 default: 108 NOTREACHED() << "bad state"; 109 rv = ERR_UNEXPECTED; 110 break; 111 } 112 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); 113 return rv; 114 } 115 116 void InitProxyResolver::DoCallback(int result) { 117 DCHECK_NE(ERR_IO_PENDING, result); 118 DCHECK(user_callback_); 119 user_callback_->Run(result); 120 } 121 122 int InitProxyResolver::DoFetchPacScript() { 123 DCHECK(resolver_->expects_pac_bytes()); 124 125 LoadLog::BeginEvent(load_log_, 126 LoadLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT); 127 128 next_state_ = STATE_FETCH_PAC_SCRIPT_COMPLETE; 129 130 const GURL& pac_url = current_pac_url(); 131 132 LoadLog::AddString(load_log_, pac_url.spec()); 133 134 if (!proxy_script_fetcher_) { 135 LoadLog::AddStringLiteral(load_log_, 136 "Can't download PAC script, because no fetcher was specified"); 137 return ERR_UNEXPECTED; 138 } 139 140 return proxy_script_fetcher_->Fetch(pac_url, &pac_bytes_, &io_callback_); 141 } 142 143 int InitProxyResolver::DoFetchPacScriptComplete(int result) { 144 DCHECK(resolver_->expects_pac_bytes()); 145 146 LoadLog::AddString(load_log_, 147 StringPrintf( 148 "Completed fetch with result %s. Received %" PRIuS " bytes", 149 ErrorToString(result), 150 pac_bytes_.size())); 151 152 LoadLog::EndEvent(load_log_, 153 LoadLog::TYPE_INIT_PROXY_RESOLVER_FETCH_PAC_SCRIPT); 154 155 if (result != OK) 156 return TryToFallbackPacUrl(result); 157 158 next_state_ = STATE_SET_PAC_SCRIPT; 159 return result; 160 } 161 162 int InitProxyResolver::DoSetPacScript() { 163 LoadLog::BeginEvent(load_log_, 164 LoadLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT); 165 166 const GURL& pac_url = current_pac_url(); 167 168 next_state_ = STATE_SET_PAC_SCRIPT_COMPLETE; 169 170 return resolver_->expects_pac_bytes() ? 171 resolver_->SetPacScriptByData(pac_bytes_, &io_callback_) : 172 resolver_->SetPacScriptByUrl(pac_url, &io_callback_); 173 } 174 175 int InitProxyResolver::DoSetPacScriptComplete(int result) { 176 if (result != OK) { 177 LoadLog::AddString(load_log_, 178 StringPrintf("Failed initializing the PAC script with error: %s", 179 ErrorToString(result))); 180 LoadLog::EndEvent(load_log_, 181 LoadLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT); 182 return TryToFallbackPacUrl(result); 183 } 184 185 LoadLog::AddStringLiteral(load_log_, "Successfully initialized PAC script."); 186 187 LoadLog::EndEvent(load_log_, 188 LoadLog::TYPE_INIT_PROXY_RESOLVER_SET_PAC_SCRIPT); 189 return result; 190 } 191 192 int InitProxyResolver::TryToFallbackPacUrl(int error) { 193 DCHECK_LT(error, 0); 194 195 if (current_pac_url_index_ + 1 >= pac_urls_.size()) { 196 // Nothing left to fall back to. 197 return error; 198 } 199 200 // Advance to next URL in our list. 201 ++current_pac_url_index_; 202 203 LoadLog::AddStringLiteral(load_log_, "Falling back to next PAC URL..."); 204 205 next_state_ = GetStartState(); 206 207 return OK; 208 } 209 210 InitProxyResolver::State InitProxyResolver::GetStartState() const { 211 return resolver_->expects_pac_bytes() ? 212 STATE_FETCH_PAC_SCRIPT : STATE_SET_PAC_SCRIPT; 213 } 214 215 const GURL& InitProxyResolver::current_pac_url() const { 216 DCHECK_LT(current_pac_url_index_, pac_urls_.size()); 217 return pac_urls_[current_pac_url_index_]; 218 } 219 220 void InitProxyResolver::DidCompleteInit() { 221 LoadLog::EndEvent(load_log_, LoadLog::TYPE_INIT_PROXY_RESOLVER); 222 } 223 224 void InitProxyResolver::Cancel() { 225 DCHECK_NE(STATE_NONE, next_state_); 226 227 LoadLog::AddEvent(load_log_, LoadLog::TYPE_CANCELLED); 228 229 switch (next_state_) { 230 case STATE_FETCH_PAC_SCRIPT_COMPLETE: 231 proxy_script_fetcher_->Cancel(); 232 break; 233 case STATE_SET_PAC_SCRIPT_COMPLETE: 234 resolver_->CancelSetPacScript(); 235 break; 236 default: 237 NOTREACHED(); 238 break; 239 } 240 241 DidCompleteInit(); 242 } 243 244 } // namespace net 245