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