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
InitProxyResolver(ProxyResolver * resolver,ProxyScriptFetcher * proxy_script_fetcher)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
~InitProxyResolver()30 InitProxyResolver::~InitProxyResolver() {
31 if (next_state_ != STATE_NONE)
32 Cancel();
33 }
34
Init(const ProxyConfig & config,CompletionCallback * callback,LoadLog * load_log)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.
BuildPacUrlsFallbackList(const ProxyConfig & config) const64 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
OnIOCompletion(int result)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
DoLoop(int result)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
DoCallback(int result)116 void InitProxyResolver::DoCallback(int result) {
117 DCHECK_NE(ERR_IO_PENDING, result);
118 DCHECK(user_callback_);
119 user_callback_->Run(result);
120 }
121
DoFetchPacScript()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
DoFetchPacScriptComplete(int result)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
DoSetPacScript()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
DoSetPacScriptComplete(int result)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
TryToFallbackPacUrl(int error)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
GetStartState() const210 InitProxyResolver::State InitProxyResolver::GetStartState() const {
211 return resolver_->expects_pac_bytes() ?
212 STATE_FETCH_PAC_SCRIPT : STATE_SET_PAC_SCRIPT;
213 }
214
current_pac_url() const215 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
DidCompleteInit()220 void InitProxyResolver::DidCompleteInit() {
221 LoadLog::EndEvent(load_log_, LoadLog::TYPE_INIT_PROXY_RESOLVER);
222 }
223
Cancel()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