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_resolution/proxy_list.h"
6
7 #include "base/callback.h"
8 #include "base/logging.h"
9 #include "base/strings/string_tokenizer.h"
10 #include "base/time/time.h"
11 #include "base/values.h"
12 #include "net/base/proxy_server.h"
13 #include "net/log/net_log.h"
14 #include "net/log/net_log_event_type.h"
15 #include "net/log/net_log_with_source.h"
16
17 using base::TimeDelta;
18 using base::TimeTicks;
19
20 namespace net {
21
22 ProxyList::ProxyList() = default;
23
24 ProxyList::ProxyList(const ProxyList& other) = default;
25
26 ProxyList::ProxyList(ProxyList&& other) = default;
27
28 ProxyList& ProxyList::operator=(const ProxyList& other) = default;
29
30 ProxyList& ProxyList::operator=(ProxyList&& other) = default;
31
32 ProxyList::~ProxyList() = default;
33
Set(const std::string & proxy_uri_list)34 void ProxyList::Set(const std::string& proxy_uri_list) {
35 proxies_.clear();
36 base::StringTokenizer str_tok(proxy_uri_list, ";");
37 while (str_tok.GetNext()) {
38 ProxyServer uri =
39 ProxyServer::FromURI(str_tok.token_piece(), ProxyServer::SCHEME_HTTP);
40 // Silently discard malformed inputs.
41 if (uri.is_valid())
42 proxies_.push_back(uri);
43 }
44 }
45
SetSingleProxyServer(const ProxyServer & proxy_server)46 void ProxyList::SetSingleProxyServer(const ProxyServer& proxy_server) {
47 proxies_.clear();
48 AddProxyServer(proxy_server);
49 }
50
AddProxyServer(const ProxyServer & proxy_server)51 void ProxyList::AddProxyServer(const ProxyServer& proxy_server) {
52 if (proxy_server.is_valid())
53 proxies_.push_back(proxy_server);
54 }
55
DeprioritizeBadProxies(const ProxyRetryInfoMap & proxy_retry_info)56 void ProxyList::DeprioritizeBadProxies(
57 const ProxyRetryInfoMap& proxy_retry_info) {
58 // Partition the proxy list in two:
59 // (1) the known bad proxies
60 // (2) everything else
61 std::vector<ProxyServer> good_proxies;
62 std::vector<ProxyServer> bad_proxies_to_try;
63
64 std::vector<ProxyServer>::const_iterator iter = proxies_.begin();
65 for (; iter != proxies_.end(); ++iter) {
66 auto bad_proxy = proxy_retry_info.find(iter->ToURI());
67 if (bad_proxy != proxy_retry_info.end()) {
68 // This proxy is bad. Check if it's time to retry.
69 if (bad_proxy->second.bad_until >= TimeTicks::Now()) {
70 // still invalid.
71 if (bad_proxy->second.try_while_bad)
72 bad_proxies_to_try.push_back(*iter);
73 continue;
74 }
75 }
76 good_proxies.push_back(*iter);
77 }
78
79 // "proxies_ = good_proxies + bad_proxies"
80 proxies_.swap(good_proxies);
81 proxies_.insert(proxies_.end(), bad_proxies_to_try.begin(),
82 bad_proxies_to_try.end());
83 }
84
RemoveProxiesWithoutScheme(int scheme_bit_field)85 void ProxyList::RemoveProxiesWithoutScheme(int scheme_bit_field) {
86 for (auto it = proxies_.begin(); it != proxies_.end();) {
87 if (!(scheme_bit_field & it->scheme())) {
88 it = proxies_.erase(it);
89 continue;
90 }
91 ++it;
92 }
93 }
94
Clear()95 void ProxyList::Clear() {
96 proxies_.clear();
97 }
98
IsEmpty() const99 bool ProxyList::IsEmpty() const {
100 return proxies_.empty();
101 }
102
size() const103 size_t ProxyList::size() const {
104 return proxies_.size();
105 }
106
107 // Returns true if |*this| lists the same proxies as |other|.
Equals(const ProxyList & other) const108 bool ProxyList::Equals(const ProxyList& other) const {
109 if (size() != other.size())
110 return false;
111 return proxies_ == other.proxies_;
112 }
113
Get() const114 const ProxyServer& ProxyList::Get() const {
115 CHECK(!proxies_.empty());
116 return proxies_[0];
117 }
118
GetAll() const119 const std::vector<ProxyServer>& ProxyList::GetAll() const {
120 return proxies_;
121 }
122
SetFromPacString(const std::string & pac_string)123 void ProxyList::SetFromPacString(const std::string& pac_string) {
124 base::StringTokenizer entry_tok(pac_string, ";");
125 proxies_.clear();
126 while (entry_tok.GetNext()) {
127 ProxyServer uri = ProxyServer::FromPacString(entry_tok.token_piece());
128 // Silently discard malformed inputs.
129 if (uri.is_valid())
130 proxies_.push_back(uri);
131 }
132
133 // If we failed to parse anything from the PAC results list, fallback to
134 // DIRECT (this basically means an error in the PAC script).
135 if (proxies_.empty()) {
136 proxies_.push_back(ProxyServer::Direct());
137 }
138 }
139
ToPacString() const140 std::string ProxyList::ToPacString() const {
141 std::string proxy_list;
142 auto iter = proxies_.begin();
143 for (; iter != proxies_.end(); ++iter) {
144 if (!proxy_list.empty())
145 proxy_list += ";";
146 proxy_list += iter->ToPacString();
147 }
148 return proxy_list.empty() ? std::string() : proxy_list;
149 }
150
ToValue() const151 base::Value ProxyList::ToValue() const {
152 base::Value list(base::Value::Type::LIST);
153 for (const auto& proxy : proxies_)
154 list.Append(proxy.ToURI());
155 return list;
156 }
157
Fallback(ProxyRetryInfoMap * proxy_retry_info,int net_error,const NetLogWithSource & net_log)158 bool ProxyList::Fallback(ProxyRetryInfoMap* proxy_retry_info,
159 int net_error,
160 const NetLogWithSource& net_log) {
161 if (proxies_.empty()) {
162 NOTREACHED();
163 return false;
164 }
165 // By default, proxies are not retried for 5 minutes.
166 UpdateRetryInfoOnFallback(proxy_retry_info, TimeDelta::FromMinutes(5), true,
167 std::vector<ProxyServer>(), net_error, net_log);
168
169 // Remove this proxy from our list.
170 proxies_.erase(proxies_.begin());
171 return !proxies_.empty();
172 }
173
AddProxyToRetryList(ProxyRetryInfoMap * proxy_retry_info,base::TimeDelta retry_delay,bool try_while_bad,const ProxyServer & proxy_to_retry,int net_error,const NetLogWithSource & net_log) const174 void ProxyList::AddProxyToRetryList(ProxyRetryInfoMap* proxy_retry_info,
175 base::TimeDelta retry_delay,
176 bool try_while_bad,
177 const ProxyServer& proxy_to_retry,
178 int net_error,
179 const NetLogWithSource& net_log) const {
180 // Mark this proxy as bad.
181 TimeTicks bad_until = TimeTicks::Now() + retry_delay;
182 std::string proxy_key = proxy_to_retry.ToURI();
183 auto iter = proxy_retry_info->find(proxy_key);
184 if (iter == proxy_retry_info->end() || bad_until > iter->second.bad_until) {
185 ProxyRetryInfo retry_info;
186 retry_info.current_delay = retry_delay;
187 retry_info.bad_until = bad_until;
188 retry_info.try_while_bad = try_while_bad;
189 retry_info.net_error = net_error;
190 (*proxy_retry_info)[proxy_key] = retry_info;
191 }
192 net_log.AddEventWithStringParams(NetLogEventType::PROXY_LIST_FALLBACK,
193 "bad_proxy", proxy_key);
194 }
195
UpdateRetryInfoOnFallback(ProxyRetryInfoMap * proxy_retry_info,base::TimeDelta retry_delay,bool reconsider,const std::vector<ProxyServer> & additional_proxies_to_bypass,int net_error,const NetLogWithSource & net_log) const196 void ProxyList::UpdateRetryInfoOnFallback(
197 ProxyRetryInfoMap* proxy_retry_info,
198 base::TimeDelta retry_delay,
199 bool reconsider,
200 const std::vector<ProxyServer>& additional_proxies_to_bypass,
201 int net_error,
202 const NetLogWithSource& net_log) const {
203 DCHECK(!retry_delay.is_zero());
204
205 if (proxies_.empty()) {
206 NOTREACHED();
207 return;
208 }
209
210 if (!proxies_[0].is_direct()) {
211 AddProxyToRetryList(proxy_retry_info,
212 retry_delay,
213 reconsider,
214 proxies_[0],
215 net_error,
216 net_log);
217 // If any additional proxies to bypass are specified, add to the retry map
218 // as well.
219 for (const ProxyServer& additional_proxy : additional_proxies_to_bypass) {
220 AddProxyToRetryList(proxy_retry_info, retry_delay, reconsider,
221 additional_proxy, net_error, net_log);
222 }
223 }
224 }
225
226 } // namespace net
227