OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2011 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/dhcp_proxy_script_fetcher_win.h" | |
6 | |
7 #include "net/base/net_errors.h" | |
8 #include "net/proxy/dhcp_proxy_script_adapter_fetcher_win.h" | |
9 | |
10 #include <winsock2.h> | |
11 #include <iphlpapi.h> | |
12 #pragma comment(lib, "iphlpapi.lib") | |
13 | |
14 namespace { | |
15 | |
16 // How long to wait at maximum after we get results (a PAC file or | |
17 // knowledge that no PAC file is configured) from whichever network | |
18 // adapter finishes first. | |
19 const int kMaxWaitAfterFirstResultMs = 400; | |
20 | |
21 } // namespace | |
22 | |
23 namespace net { | |
24 | |
25 WindowsDhcpProxyScriptFetcher::WindowsDhcpProxyScriptFetcher( | |
26 URLRequestContext* url_request_context) | |
27 : state_(STATE_START), | |
28 ALLOW_THIS_IN_INITIALIZER_LIST(fetcher_callback_( | |
29 this, &WindowsDhcpProxyScriptFetcher::OnFetcherDone)), | |
30 num_pending_fetchers_(0), | |
31 url_request_context_(url_request_context) { | |
32 DCHECK(url_request_context_); | |
33 } | |
34 | |
35 WindowsDhcpProxyScriptFetcher::~WindowsDhcpProxyScriptFetcher() { | |
36 Cancel(); | |
37 } | |
38 | |
39 int WindowsDhcpProxyScriptFetcher::Fetch(string16* utf16_text, | |
40 CompletionCallback* callback) { | |
41 DCHECK(CalledOnValidThread()); | |
42 if (state_ != STATE_START && state_ != STATE_DONE) { | |
43 NOTREACHED(); | |
44 return ERR_UNEXPECTED; | |
45 } | |
46 | |
47 std::set<std::string> adapter_names; | |
48 if (!ImplGetCandidateAdapterNames(&adapter_names)) { | |
eroman
2011/04/21 05:22:48
Is this a fast operation? (in other words, is it O
Jói
2011/04/21 15:57:38
I believe so. It calls the GetAdaptersAddresses W
| |
49 return ERR_UNEXPECTED; | |
50 } | |
51 if (adapter_names.empty()) { | |
52 *utf16_text = L""; | |
eroman
2011/04/21 05:22:48
no need to set this.
Jói
2011/05/03 21:20:59
Done.
| |
53 return ERR_PAC_NOT_IN_DHCP; | |
54 } | |
55 | |
56 state_ = STATE_NO_RESULTS; | |
57 | |
58 client_callback_ = callback; | |
59 destination_string_ = utf16_text; | |
60 | |
61 for (std::set<std::string>::iterator it = adapter_names.begin(); | |
62 it != adapter_names.end(); | |
63 ++it) { | |
64 scoped_refptr<DhcpProxyScriptAdapterFetcher> fetcher( | |
65 ImplCreateAdapterFetcher()); | |
66 fetcher->Fetch(*it, &fetcher_callback_); | |
67 fetchers_.push_back(fetcher.get()); | |
68 } | |
69 num_pending_fetchers_ = fetchers_.size(); | |
70 | |
71 return ERR_IO_PENDING; | |
72 } | |
73 | |
74 void WindowsDhcpProxyScriptFetcher::Cancel() { | |
75 DCHECK(CalledOnValidThread()); | |
76 CancelImpl(true); | |
77 } | |
78 | |
79 URLRequestContext* WindowsDhcpProxyScriptFetcher::GetRequestContext() const { | |
80 DCHECK(CalledOnValidThread()); | |
81 return url_request_context_; | |
82 } | |
83 | |
84 std::string WindowsDhcpProxyScriptFetcher::GetFetcherName() const { | |
85 DCHECK(CalledOnValidThread()); | |
86 return "win"; | |
87 } | |
88 | |
89 GURL WindowsDhcpProxyScriptFetcher::GetPacURL() const { | |
90 DCHECK(CalledOnValidThread()); | |
91 DCHECK(state_ == STATE_DONE); | |
92 | |
93 return pac_url_; | |
94 } | |
95 | |
96 void WindowsDhcpProxyScriptFetcher::CancelImpl(bool clear_fetchers) { | |
97 if (state_ != STATE_DONE) { | |
98 wait_timer_.Stop(); | |
99 state_ = STATE_DONE; | |
100 | |
101 for (FetcherList::iterator it = fetchers_.begin(); | |
102 it != fetchers_.end(); | |
103 ++it) { | |
104 (*it)->Cancel(); | |
105 } | |
106 | |
107 if (clear_fetchers) { | |
108 fetchers_.clear(); | |
109 } | |
110 } | |
111 } | |
112 | |
113 void WindowsDhcpProxyScriptFetcher::OnFetcherDone(int result) { | |
114 DCHECK(state_ == STATE_NO_RESULTS || state_ == STATE_SOME_RESULTS); | |
115 | |
116 if (--num_pending_fetchers_ == 0) { | |
117 TransitionToDone(); | |
118 return; | |
119 } | |
120 | |
121 // If the only pending adapters are those less preferred than one | |
122 // with a valid PAC script, we do not need to wait any longer. | |
123 for (FetcherList::iterator it = fetchers_.begin(); | |
124 it != fetchers_.end(); | |
125 ++it) { | |
126 bool did_finish = (*it)->DidFinish(); | |
127 int result = (*it)->result(); | |
128 if (did_finish && result == OK) { | |
129 TransitionToDone(); | |
130 return; | |
131 } | |
132 if (!did_finish || result != ERR_PAC_NOT_IN_DHCP) { | |
133 break; | |
134 } | |
135 } | |
136 | |
137 // Once we have a single result, we set a maximum on how long to wait | |
138 // for the rest of the results. | |
139 if (state_ == STATE_NO_RESULTS) { | |
140 state_ = STATE_SOME_RESULTS; | |
141 wait_timer_.Start( | |
eroman
2011/04/21 05:22:48
isn't there another timer already?
Jói
2011/05/03 21:20:59
There is a separate timer in the AdapterFetcher.
| |
142 base::TimeDelta::FromMilliseconds(ImplGetMaxWaitMs()), | |
143 this, &WindowsDhcpProxyScriptFetcher::OnWaitTimer); | |
144 } | |
145 } | |
146 | |
147 void WindowsDhcpProxyScriptFetcher::OnWaitTimer() { | |
148 DCHECK(state_ == STATE_SOME_RESULTS); | |
149 TransitionToDone(); | |
150 } | |
151 | |
152 void WindowsDhcpProxyScriptFetcher::TransitionToDone() { | |
153 DCHECK(state_ == STATE_NO_RESULTS || state_ == STATE_SOME_RESULTS); | |
154 | |
155 // This does not alter state for fetchers that are already done. | |
156 CancelImpl(false); | |
157 | |
158 // Should have returned immediately at Fetch() if no adapters to check. | |
159 DCHECK(!fetchers_.empty()); | |
160 | |
161 // Scan twice for the result; once through the whole list for success, | |
162 // then if no success, return result for most preferred network adapter, | |
163 // preferring "real" network errors to the ERR_PAC_NOT_IN_DHCP error. | |
164 // Default to ERR_ABORTED if no fetcher completed. | |
165 int result = ERR_ABORTED; | |
166 for (FetcherList::iterator it = fetchers_.begin(); | |
167 it != fetchers_.end(); | |
168 ++it) { | |
169 if ((*it)->DidFinish() && (*it)->result() == OK) { | |
170 result = OK; | |
171 *destination_string_ = (*it)->pac_script(); | |
172 pac_url_ = (*it)->pac_url(); | |
173 break; | |
174 } | |
175 } | |
176 if (result != OK) { | |
177 destination_string_->clear(); | |
178 for (FetcherList::iterator it = fetchers_.begin(); | |
179 it != fetchers_.end(); | |
180 ++it) { | |
181 if ((*it)->DidFinish()) { | |
182 result = (*it)->result(); | |
183 if (result != ERR_PAC_NOT_IN_DHCP) { | |
184 break; | |
185 } | |
186 } | |
187 } | |
188 } | |
189 | |
190 client_callback_->Run(result); | |
191 fetchers_.clear(); | |
192 state_ = STATE_DONE; | |
193 } | |
194 | |
195 DhcpProxyScriptAdapterFetcher* | |
196 WindowsDhcpProxyScriptFetcher::ImplCreateAdapterFetcher() { | |
197 return new DhcpProxyScriptAdapterFetcher(url_request_context_); | |
198 } | |
199 | |
200 bool WindowsDhcpProxyScriptFetcher::ImplGetCandidateAdapterNames( | |
201 std::set<std::string>* adapter_names) { | |
202 return GetCandidateAdapterNames(adapter_names); | |
203 } | |
204 | |
205 int WindowsDhcpProxyScriptFetcher::ImplGetMaxWaitMs() { | |
206 return kMaxWaitAfterFirstResultMs; | |
207 } | |
208 | |
209 | |
210 bool WindowsDhcpProxyScriptFetcher::GetCandidateAdapterNames( | |
211 std::set<std::string>* adapter_names) { | |
212 DCHECK(adapter_names); | |
213 | |
214 // The GetAdaptersAddresses MSDN page recommends using a size of 15000 to | |
215 // avoid reallocation. | |
216 ULONG adapters_size = 15000; | |
217 scoped_ptr_malloc<IP_ADAPTER_ADDRESSES> adapters; | |
218 ULONG error = ERROR_SUCCESS; | |
219 int num_tries = 0; | |
220 do { | |
221 adapters.reset( | |
222 reinterpret_cast<IP_ADAPTER_ADDRESSES*>(malloc(adapters_size))); | |
223 // Return only unicast addresses, and skip information we do not need. | |
224 error = GetAdaptersAddresses(AF_UNSPEC, | |
225 GAA_FLAG_SKIP_ANYCAST | | |
226 GAA_FLAG_SKIP_MULTICAST | | |
227 GAA_FLAG_SKIP_DNS_SERVER | | |
228 GAA_FLAG_SKIP_FRIENDLY_NAME, | |
229 NULL, | |
230 adapters.get(), | |
231 &adapters_size); | |
232 ++num_tries; | |
233 } while (error == ERROR_BUFFER_OVERFLOW && num_tries <= 3); | |
234 | |
235 if (error == ERROR_NO_DATA) { | |
236 // There are no adapters that we care about. | |
237 return true; | |
238 } | |
239 | |
240 if (error != ERROR_SUCCESS) { | |
241 NOTREACHED(); | |
242 return false; | |
243 } | |
244 | |
245 IP_ADAPTER_ADDRESSES* adapter = NULL; | |
246 for (adapter = adapters.get(); adapter; adapter = adapter->Next) { | |
247 if (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK) | |
248 continue; | |
249 if ((adapter->Flags & IP_ADAPTER_DHCP_ENABLED) == 0) | |
250 continue; | |
251 | |
252 DCHECK(adapter->AdapterName); | |
253 adapter_names->insert(adapter->AdapterName); | |
254 } | |
255 | |
256 return true; | |
257 } | |
258 | |
259 } // namespace net | |
OLD | NEW |