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