Chromium Code Reviews| Index: net/proxy/dhcp_proxy_script_fetcher_win.cc |
| diff --git a/net/proxy/dhcp_proxy_script_fetcher_win.cc b/net/proxy/dhcp_proxy_script_fetcher_win.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..1282131b77027e7999a92574b140d0f7bd1cef5e |
| --- /dev/null |
| +++ b/net/proxy/dhcp_proxy_script_fetcher_win.cc |
| @@ -0,0 +1,259 @@ |
| +// Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "net/proxy/dhcp_proxy_script_fetcher_win.h" |
| + |
| +#include "net/base/net_errors.h" |
| +#include "net/proxy/dhcp_proxy_script_adapter_fetcher_win.h" |
| + |
| +#include <winsock2.h> |
| +#include <iphlpapi.h> |
| +#pragma comment(lib, "iphlpapi.lib") |
| + |
| +namespace { |
| + |
| +// How long to wait at maximum after we get results (a PAC file or |
| +// knowledge that no PAC file is configured) from whichever network |
| +// adapter finishes first. |
| +const int kMaxWaitAfterFirstResultMs = 400; |
| + |
| +} // namespace |
| + |
| +namespace net { |
| + |
| +WindowsDhcpProxyScriptFetcher::WindowsDhcpProxyScriptFetcher( |
| + URLRequestContext* url_request_context) |
| + : state_(STATE_START), |
| + ALLOW_THIS_IN_INITIALIZER_LIST(fetcher_callback_( |
| + this, &WindowsDhcpProxyScriptFetcher::OnFetcherDone)), |
| + num_pending_fetchers_(0), |
| + url_request_context_(url_request_context) { |
| + DCHECK(url_request_context_); |
| +} |
| + |
| +WindowsDhcpProxyScriptFetcher::~WindowsDhcpProxyScriptFetcher() { |
| + Cancel(); |
| +} |
| + |
| +int WindowsDhcpProxyScriptFetcher::Fetch(string16* utf16_text, |
| + CompletionCallback* callback) { |
| + DCHECK(CalledOnValidThread()); |
| + if (state_ != STATE_START && state_ != STATE_DONE) { |
| + NOTREACHED(); |
| + return ERR_UNEXPECTED; |
| + } |
| + |
| + std::set<std::string> adapter_names; |
| + if (!ImplGetCandidateAdapterNames(&adapter_names)) { |
| + return ERR_UNEXPECTED; |
| + } |
| + if (adapter_names.empty()) { |
| + *utf16_text = L""; |
| + return ERR_PAC_NOT_IN_DHCP; |
| + } |
| + |
| + state_ = STATE_NO_RESULTS; |
| + |
| + client_callback_ = callback; |
| + destination_string_ = utf16_text; |
| + |
| + for (std::set<std::string>::iterator it = adapter_names.begin(); |
| + it != adapter_names.end(); |
| + ++it) { |
| + scoped_refptr<DhcpProxyScriptAdapterFetcher> fetcher( |
| + ImplCreateAdapterFetcher()); |
| + fetcher->Fetch(*it, &fetcher_callback_); |
| + fetchers_.push_back(fetcher.get()); |
|
eroman
2011/04/21 05:22:48
nit: why the .get() ?
Jói
2011/05/03 21:20:59
Done.
|
| + } |
| + num_pending_fetchers_ = fetchers_.size(); |
| + |
| + return ERR_IO_PENDING; |
| +} |
| + |
| +void WindowsDhcpProxyScriptFetcher::Cancel() { |
| + DCHECK(CalledOnValidThread()); |
| + CancelImpl(true); |
| +} |
| + |
| +URLRequestContext* WindowsDhcpProxyScriptFetcher::GetRequestContext() const { |
| + DCHECK(CalledOnValidThread()); |
| + return url_request_context_; |
| +} |
| + |
| +std::string WindowsDhcpProxyScriptFetcher::GetFetcherName() const { |
| + DCHECK(CalledOnValidThread()); |
| + return "win"; |
| +} |
| + |
| +GURL WindowsDhcpProxyScriptFetcher::GetPacURL() const { |
| + DCHECK(CalledOnValidThread()); |
| + DCHECK(state_ == STATE_DONE); |
| + |
| + return pac_url_; |
| +} |
| + |
| +void WindowsDhcpProxyScriptFetcher::CancelImpl(bool clear_fetchers) { |
| + if (state_ != STATE_DONE) { |
| + wait_timer_.Stop(); |
| + state_ = STATE_DONE; |
| + |
| + for (FetcherList::iterator it = fetchers_.begin(); |
| + it != fetchers_.end(); |
| + ++it) { |
| + (*it)->Cancel(); |
| + } |
| + |
| + if (clear_fetchers) { |
| + fetchers_.clear(); |
| + } |
| + } |
| +} |
| + |
| +void WindowsDhcpProxyScriptFetcher::OnFetcherDone(int result) { |
| + DCHECK(state_ == STATE_NO_RESULTS || state_ == STATE_SOME_RESULTS); |
| + |
| + if (--num_pending_fetchers_ == 0) { |
| + TransitionToDone(); |
| + return; |
| + } |
| + |
| + // If the only pending adapters are those less preferred than one |
| + // with a valid PAC script, we do not need to wait any longer. |
| + for (FetcherList::iterator it = fetchers_.begin(); |
| + it != fetchers_.end(); |
| + ++it) { |
| + bool did_finish = (*it)->DidFinish(); |
| + int result = (*it)->result(); |
| + if (did_finish && result == OK) { |
| + TransitionToDone(); |
| + return; |
| + } |
| + if (!did_finish || result != ERR_PAC_NOT_IN_DHCP) { |
| + break; |
| + } |
| + } |
| + |
| + // Once we have a single result, we set a maximum on how long to wait |
| + // for the rest of the results. |
| + if (state_ == STATE_NO_RESULTS) { |
| + state_ = STATE_SOME_RESULTS; |
| + wait_timer_.Start( |
| + base::TimeDelta::FromMilliseconds(ImplGetMaxWaitMs()), |
| + this, &WindowsDhcpProxyScriptFetcher::OnWaitTimer); |
| + } |
| +} |
| + |
| +void WindowsDhcpProxyScriptFetcher::OnWaitTimer() { |
| + DCHECK(state_ == STATE_SOME_RESULTS); |
| + TransitionToDone(); |
| +} |
| + |
| +void WindowsDhcpProxyScriptFetcher::TransitionToDone() { |
| + DCHECK(state_ == STATE_NO_RESULTS || state_ == STATE_SOME_RESULTS); |
| + |
| + // This does not alter state for fetchers that are already done. |
| + CancelImpl(false); |
| + |
| + // Should have returned immediately at Fetch() if no adapters to check. |
| + DCHECK(!fetchers_.empty()); |
| + |
| + // Scan twice for the result; once through the whole list for success, |
| + // then if no success, return result for most preferred network adapter, |
| + // preferring "real" network errors to the ERR_PAC_NOT_IN_DHCP error. |
| + // Default to ERR_ABORTED if no fetcher completed. |
| + int result = ERR_ABORTED; |
| + for (FetcherList::iterator it = fetchers_.begin(); |
| + it != fetchers_.end(); |
| + ++it) { |
| + if ((*it)->DidFinish() && (*it)->result() == OK) { |
| + result = OK; |
| + *destination_string_ = (*it)->pac_script(); |
| + pac_url_ = (*it)->pac_url(); |
| + break; |
| + } |
| + } |
| + if (result != OK) { |
| + destination_string_->clear(); |
| + for (FetcherList::iterator it = fetchers_.begin(); |
| + it != fetchers_.end(); |
| + ++it) { |
| + if ((*it)->DidFinish()) { |
| + result = (*it)->result(); |
| + if (result != ERR_PAC_NOT_IN_DHCP) { |
| + break; |
| + } |
| + } |
| + } |
| + } |
| + |
| + client_callback_->Run(result); |
| + fetchers_.clear(); |
| + state_ = STATE_DONE; |
| +} |
| + |
| +DhcpProxyScriptAdapterFetcher* |
| + WindowsDhcpProxyScriptFetcher::ImplCreateAdapterFetcher() { |
| + return new DhcpProxyScriptAdapterFetcher(url_request_context_); |
| +} |
| + |
| +bool WindowsDhcpProxyScriptFetcher::ImplGetCandidateAdapterNames( |
| + std::set<std::string>* adapter_names) { |
| + return GetCandidateAdapterNames(adapter_names); |
| +} |
| + |
| +int WindowsDhcpProxyScriptFetcher::ImplGetMaxWaitMs() { |
| + return kMaxWaitAfterFirstResultMs; |
| +} |
| + |
| + |
| +bool WindowsDhcpProxyScriptFetcher::GetCandidateAdapterNames( |
| + std::set<std::string>* adapter_names) { |
| + DCHECK(adapter_names); |
| + |
| + // The GetAdaptersAddresses MSDN page recommends using a size of 15000 to |
| + // avoid reallocation. |
| + ULONG adapters_size = 15000; |
| + scoped_ptr_malloc<IP_ADAPTER_ADDRESSES> adapters; |
| + ULONG error = ERROR_SUCCESS; |
| + int num_tries = 0; |
| + do { |
| + adapters.reset( |
| + reinterpret_cast<IP_ADAPTER_ADDRESSES*>(malloc(adapters_size))); |
| + // Return only unicast addresses, and skip information we do not need. |
| + error = GetAdaptersAddresses(AF_UNSPEC, |
| + GAA_FLAG_SKIP_ANYCAST | |
| + GAA_FLAG_SKIP_MULTICAST | |
| + GAA_FLAG_SKIP_DNS_SERVER | |
| + GAA_FLAG_SKIP_FRIENDLY_NAME, |
| + NULL, |
| + adapters.get(), |
| + &adapters_size); |
| + ++num_tries; |
| + } while (error == ERROR_BUFFER_OVERFLOW && num_tries <= 3); |
| + |
| + if (error == ERROR_NO_DATA) { |
| + // There are no adapters that we care about. |
| + return true; |
| + } |
| + |
| + if (error != ERROR_SUCCESS) { |
| + NOTREACHED(); |
| + return false; |
| + } |
| + |
| + IP_ADAPTER_ADDRESSES* adapter = NULL; |
| + for (adapter = adapters.get(); adapter; adapter = adapter->Next) { |
| + if (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK) |
| + continue; |
| + if ((adapter->Flags & IP_ADAPTER_DHCP_ENABLED) == 0) |
| + continue; |
| + |
| + DCHECK(adapter->AdapterName); |
| + adapter_names->insert(adapter->AdapterName); |
| + } |
| + |
| + return true; |
| +} |
| + |
| +} // namespace net |