1// Copyright (c) 2012 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/proxy_list.h" 6 7#include "base/callback.h" 8#include "base/logging.h" 9#include "base/rand_util.h" 10#include "base/strings/string_tokenizer.h" 11#include "base/time/time.h" 12#include "base/values.h" 13#include "net/proxy/proxy_server.h" 14 15using base::TimeDelta; 16using base::TimeTicks; 17 18namespace net { 19 20ProxyList::ProxyList() { 21} 22 23ProxyList::~ProxyList() { 24} 25 26void ProxyList::Set(const std::string& proxy_uri_list) { 27 proxies_.clear(); 28 base::StringTokenizer str_tok(proxy_uri_list, ";"); 29 while (str_tok.GetNext()) { 30 ProxyServer uri = ProxyServer::FromURI( 31 str_tok.token_begin(), str_tok.token_end(), ProxyServer::SCHEME_HTTP); 32 // Silently discard malformed inputs. 33 if (uri.is_valid()) 34 proxies_.push_back(uri); 35 } 36} 37 38void ProxyList::SetSingleProxyServer(const ProxyServer& proxy_server) { 39 proxies_.clear(); 40 AddProxyServer(proxy_server); 41} 42 43void ProxyList::AddProxyServer(const ProxyServer& proxy_server) { 44 if (proxy_server.is_valid()) 45 proxies_.push_back(proxy_server); 46} 47 48void ProxyList::DeprioritizeBadProxies( 49 const ProxyRetryInfoMap& proxy_retry_info) { 50 // Partition the proxy list in two: 51 // (1) the known bad proxies 52 // (2) everything else 53 std::vector<ProxyServer> good_proxies; 54 std::vector<ProxyServer> bad_proxies_to_try; 55 56 std::vector<ProxyServer>::const_iterator iter = proxies_.begin(); 57 for (; iter != proxies_.end(); ++iter) { 58 ProxyRetryInfoMap::const_iterator bad_proxy = 59 proxy_retry_info.find(iter->ToURI()); 60 if (bad_proxy != proxy_retry_info.end()) { 61 // This proxy is bad. Check if it's time to retry. 62 if (bad_proxy->second.bad_until >= TimeTicks::Now()) { 63 // still invalid. 64 if (bad_proxy->second.try_while_bad) 65 bad_proxies_to_try.push_back(*iter); 66 continue; 67 } 68 } 69 good_proxies.push_back(*iter); 70 } 71 72 // "proxies_ = good_proxies + bad_proxies" 73 proxies_.swap(good_proxies); 74 proxies_.insert(proxies_.end(), bad_proxies_to_try.begin(), 75 bad_proxies_to_try.end()); 76} 77 78void ProxyList::RemoveProxiesWithoutScheme(int scheme_bit_field) { 79 for (std::vector<ProxyServer>::iterator it = proxies_.begin(); 80 it != proxies_.end(); ) { 81 if (!(scheme_bit_field & it->scheme())) { 82 it = proxies_.erase(it); 83 continue; 84 } 85 ++it; 86 } 87} 88 89void ProxyList::Clear() { 90 proxies_.clear(); 91} 92 93bool ProxyList::IsEmpty() const { 94 return proxies_.empty(); 95} 96 97size_t ProxyList::size() const { 98 return proxies_.size(); 99} 100 101// Returns true if |*this| lists the same proxies as |other|. 102bool ProxyList::Equals(const ProxyList& other) const { 103 if (size() != other.size()) 104 return false; 105 return proxies_ == other.proxies_; 106} 107 108const ProxyServer& ProxyList::Get() const { 109 DCHECK(!proxies_.empty()); 110 return proxies_[0]; 111} 112 113void ProxyList::SetFromPacString(const std::string& pac_string) { 114 base::StringTokenizer entry_tok(pac_string, ";"); 115 proxies_.clear(); 116 while (entry_tok.GetNext()) { 117 ProxyServer uri = ProxyServer::FromPacString( 118 entry_tok.token_begin(), entry_tok.token_end()); 119 // Silently discard malformed inputs. 120 if (uri.is_valid()) 121 proxies_.push_back(uri); 122 } 123 124 // If we failed to parse anything from the PAC results list, fallback to 125 // DIRECT (this basically means an error in the PAC script). 126 if (proxies_.empty()) { 127 proxies_.push_back(ProxyServer::Direct()); 128 } 129} 130 131std::string ProxyList::ToPacString() const { 132 std::string proxy_list; 133 std::vector<ProxyServer>::const_iterator iter = proxies_.begin(); 134 for (; iter != proxies_.end(); ++iter) { 135 if (!proxy_list.empty()) 136 proxy_list += ";"; 137 proxy_list += iter->ToPacString(); 138 } 139 return proxy_list.empty() ? std::string() : proxy_list; 140} 141 142base::ListValue* ProxyList::ToValue() const { 143 base::ListValue* list = new base::ListValue(); 144 for (size_t i = 0; i < proxies_.size(); ++i) 145 list->AppendString(proxies_[i].ToURI()); 146 return list; 147} 148 149bool ProxyList::Fallback(ProxyRetryInfoMap* proxy_retry_info, 150 const BoundNetLog& net_log) { 151 152 // TODO(eroman): It would be good if instead of removing failed proxies 153 // from the list, we simply annotated them with the error code they failed 154 // with. Of course, ProxyService::ReconsiderProxyAfterError() would need to 155 // be given this information by the network transaction. 156 // 157 // The advantage of this approach is when the network transaction 158 // fails, we could output the full list of proxies that were attempted, and 159 // why each one of those failed (as opposed to just the last failure). 160 // 161 // And also, before failing the transaction wholesale, we could go back and 162 // retry the "bad proxies" which we never tried to begin with. 163 // (RemoveBadProxies would annotate them as 'expected bad' rather then delete 164 // them from the list, so we would know what they were). 165 166 if (proxies_.empty()) { 167 NOTREACHED(); 168 return false; 169 } 170 UpdateRetryInfoOnFallback(proxy_retry_info, base::TimeDelta(), true, 171 ProxyServer(), net_log); 172 173 // Remove this proxy from our list. 174 proxies_.erase(proxies_.begin()); 175 return !proxies_.empty(); 176} 177 178void ProxyList::AddProxyToRetryList(ProxyRetryInfoMap* proxy_retry_info, 179 base::TimeDelta retry_delay, 180 bool try_while_bad, 181 const ProxyServer& proxy_to_retry, 182 const BoundNetLog& net_log) const { 183 // Mark this proxy as bad. 184 std::string proxy_key = proxy_to_retry.ToURI(); 185 ProxyRetryInfoMap::iterator iter = proxy_retry_info->find(proxy_key); 186 if (iter != proxy_retry_info->end()) { 187 // TODO(nsylvain): This is not the first time we get this. We should 188 // double the retry time. Bug 997660. 189 iter->second.bad_until = TimeTicks::Now() + iter->second.current_delay; 190 } else { 191 ProxyRetryInfo retry_info; 192 retry_info.current_delay = retry_delay; 193 retry_info.bad_until = TimeTicks().Now() + retry_info.current_delay; 194 retry_info.try_while_bad = try_while_bad; 195 (*proxy_retry_info)[proxy_key] = retry_info; 196 } 197 net_log.AddEvent(NetLog::TYPE_PROXY_LIST_FALLBACK, 198 NetLog::StringCallback("bad_proxy", &proxy_key)); 199} 200 201void ProxyList::UpdateRetryInfoOnFallback( 202 ProxyRetryInfoMap* proxy_retry_info, 203 base::TimeDelta retry_delay, 204 bool reconsider, 205 const ProxyServer& another_proxy_to_bypass, 206 const BoundNetLog& net_log) const { 207 // Time to wait before retrying a bad proxy server. 208 if (retry_delay == base::TimeDelta()) { 209#if defined(SPDY_PROXY_AUTH_ORIGIN) 210 // Randomize the timeout over a range from one to five minutes. 211 retry_delay = 212 TimeDelta::FromMilliseconds( 213 base::RandInt(1 * 60 * 1000, 5 * 60 * 1000)); 214#else 215 retry_delay = TimeDelta::FromMinutes(5); 216#endif 217 } 218 219 if (proxies_.empty()) { 220 NOTREACHED(); 221 return; 222 } 223 224 if (!proxies_[0].is_direct()) { 225 AddProxyToRetryList(proxy_retry_info, retry_delay, reconsider, proxies_[0], 226 net_log); 227 228 // If an additional proxy to bypass is specified, add it to the retry map 229 // as well. 230 if (another_proxy_to_bypass.is_valid()) { 231 AddProxyToRetryList(proxy_retry_info, retry_delay, reconsider, 232 another_proxy_to_bypass, net_log); 233 } 234 } 235} 236 237} // namespace net 238