1 // Copyright 2018 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 "services/network/network_service_proxy_delegate.h"
6 #include "net/base/url_util.h"
7 #include "net/http/http_request_headers.h"
8 #include "net/http/http_util.h"
9 #include "net/proxy_resolution/proxy_info.h"
10 #include "net/proxy_resolution/proxy_resolution_service.h"
11 #include "services/network/url_loader.h"
12 #include "url/url_constants.h"
13
14 namespace network {
15 namespace {
16
ApplyProxyConfigToProxyInfo(const net::ProxyConfig::ProxyRules & rules,const net::ProxyRetryInfoMap & proxy_retry_info,const GURL & url,net::ProxyInfo * proxy_info)17 bool ApplyProxyConfigToProxyInfo(const net::ProxyConfig::ProxyRules& rules,
18 const net::ProxyRetryInfoMap& proxy_retry_info,
19 const GURL& url,
20 net::ProxyInfo* proxy_info) {
21 DCHECK(proxy_info);
22 if (rules.empty())
23 return false;
24
25 rules.Apply(url, proxy_info);
26 proxy_info->DeprioritizeBadProxies(proxy_retry_info);
27 return !proxy_info->is_empty() && !proxy_info->proxy_server().is_direct();
28 }
29
30 // Checks if |target_proxy| is in |proxy_list|.
CheckProxyList(const net::ProxyList & proxy_list,const net::ProxyServer & target_proxy)31 bool CheckProxyList(const net::ProxyList& proxy_list,
32 const net::ProxyServer& target_proxy) {
33 for (const auto& proxy : proxy_list.GetAll()) {
34 if (!proxy.is_direct() &&
35 proxy.host_port_pair().Equals(target_proxy.host_port_pair())) {
36 return true;
37 }
38 }
39 return false;
40 }
41
42 // Returns true if there is a possibility that |proxy_rules->Apply()| can
43 // choose |target_proxy|. This does not consider the bypass rules; it only
44 // scans the possible set of proxy server.
RulesContainsProxy(const net::ProxyConfig::ProxyRules & proxy_rules,const net::ProxyServer & target_proxy)45 bool RulesContainsProxy(const net::ProxyConfig::ProxyRules& proxy_rules,
46 const net::ProxyServer& target_proxy) {
47 switch (proxy_rules.type) {
48 case net::ProxyConfig::ProxyRules::Type::EMPTY:
49 return false;
50
51 case net::ProxyConfig::ProxyRules::Type::PROXY_LIST:
52 return CheckProxyList(proxy_rules.single_proxies, target_proxy);
53
54 case net::ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME:
55 return CheckProxyList(proxy_rules.proxies_for_http, target_proxy) ||
56 CheckProxyList(proxy_rules.proxies_for_https, target_proxy);
57 }
58
59 NOTREACHED();
60 return false;
61 }
62
IsValidCustomProxyConfig(const mojom::CustomProxyConfig & config)63 bool IsValidCustomProxyConfig(const mojom::CustomProxyConfig& config) {
64 switch (config.rules.type) {
65 case net::ProxyConfig::ProxyRules::Type::EMPTY:
66 return true;
67
68 case net::ProxyConfig::ProxyRules::Type::PROXY_LIST:
69 return !config.rules.single_proxies.IsEmpty();
70
71 case net::ProxyConfig::ProxyRules::Type::PROXY_LIST_PER_SCHEME:
72 return !config.rules.proxies_for_http.IsEmpty() ||
73 !config.rules.proxies_for_https.IsEmpty();
74 }
75
76 NOTREACHED();
77 return false;
78 }
79
80 // Merges headers from |in| to |out|. If the header already exists in |out| they
81 // are combined.
MergeRequestHeaders(net::HttpRequestHeaders * out,const net::HttpRequestHeaders & in)82 void MergeRequestHeaders(net::HttpRequestHeaders* out,
83 const net::HttpRequestHeaders& in) {
84 for (net::HttpRequestHeaders::Iterator it(in); it.GetNext();) {
85 std::string old_value;
86 if (out->GetHeader(it.name(), &old_value)) {
87 out->SetHeader(it.name(), old_value + ", " + it.value());
88 } else {
89 out->SetHeader(it.name(), it.value());
90 }
91 }
92 }
93
94 } // namespace
95
NetworkServiceProxyDelegate(mojom::CustomProxyConfigPtr initial_config,mojo::PendingReceiver<mojom::CustomProxyConfigClient> config_client_receiver)96 NetworkServiceProxyDelegate::NetworkServiceProxyDelegate(
97 mojom::CustomProxyConfigPtr initial_config,
98 mojo::PendingReceiver<mojom::CustomProxyConfigClient>
99 config_client_receiver)
100 : proxy_config_(std::move(initial_config)),
101 receiver_(this, std::move(config_client_receiver)) {
102 // Make sure there is always a valid proxy config so we don't need to null
103 // check it.
104 if (!proxy_config_)
105 proxy_config_ = mojom::CustomProxyConfig::New();
106 }
107
~NetworkServiceProxyDelegate()108 NetworkServiceProxyDelegate::~NetworkServiceProxyDelegate() {}
109
OnResolveProxy(const GURL & url,const std::string & method,const net::ProxyRetryInfoMap & proxy_retry_info,net::ProxyInfo * result)110 void NetworkServiceProxyDelegate::OnResolveProxy(
111 const GURL& url,
112 const std::string& method,
113 const net::ProxyRetryInfoMap& proxy_retry_info,
114 net::ProxyInfo* result) {
115 if (!EligibleForProxy(*result, method))
116 return;
117
118 net::ProxyInfo proxy_info;
119 if (ApplyProxyConfigToProxyInfo(proxy_config_->rules, proxy_retry_info, url,
120 &proxy_info)) {
121 DCHECK(!proxy_info.is_empty() && !proxy_info.is_direct());
122 result->OverrideProxyList(proxy_info.proxy_list());
123 }
124 }
125
OnFallback(const net::ProxyServer & bad_proxy,int net_error)126 void NetworkServiceProxyDelegate::OnFallback(const net::ProxyServer& bad_proxy,
127 int net_error) {}
128
OnBeforeTunnelRequest(const net::ProxyServer & proxy_server,net::HttpRequestHeaders * extra_headers)129 void NetworkServiceProxyDelegate::OnBeforeTunnelRequest(
130 const net::ProxyServer& proxy_server,
131 net::HttpRequestHeaders* extra_headers) {
132 if (IsInProxyConfig(proxy_server))
133 MergeRequestHeaders(extra_headers, proxy_config_->connect_tunnel_headers);
134 }
135
OnTunnelHeadersReceived(const net::ProxyServer & proxy_server,const net::HttpResponseHeaders & response_headers)136 net::Error NetworkServiceProxyDelegate::OnTunnelHeadersReceived(
137 const net::ProxyServer& proxy_server,
138 const net::HttpResponseHeaders& response_headers) {
139 return net::OK;
140 }
141
OnCustomProxyConfigUpdated(mojom::CustomProxyConfigPtr proxy_config)142 void NetworkServiceProxyDelegate::OnCustomProxyConfigUpdated(
143 mojom::CustomProxyConfigPtr proxy_config) {
144 DCHECK(IsValidCustomProxyConfig(*proxy_config));
145 proxy_config_ = std::move(proxy_config);
146 }
147
MarkProxiesAsBad(base::TimeDelta bypass_duration,const net::ProxyList & bad_proxies_list,MarkProxiesAsBadCallback callback)148 void NetworkServiceProxyDelegate::MarkProxiesAsBad(
149 base::TimeDelta bypass_duration,
150 const net::ProxyList& bad_proxies_list,
151 MarkProxiesAsBadCallback callback) {
152 std::vector<net::ProxyServer> bad_proxies = bad_proxies_list.GetAll();
153
154 // Synthesize a suitable |ProxyInfo| to add the proxies to the
155 // |ProxyRetryInfoMap| of the proxy service.
156 //
157 // TODO(eroman): Support this more directly on ProxyResolutionService.
158 net::ProxyList proxy_list;
159 for (const auto& bad_proxy : bad_proxies)
160 proxy_list.AddProxyServer(bad_proxy);
161 proxy_list.AddProxyServer(net::ProxyServer::Direct());
162
163 net::ProxyInfo proxy_info;
164 proxy_info.UseProxyList(proxy_list);
165
166 proxy_resolution_service_->MarkProxiesAsBadUntil(
167 proxy_info, bypass_duration, bad_proxies, net::NetLogWithSource());
168
169 std::move(callback).Run();
170 }
171
ClearBadProxiesCache()172 void NetworkServiceProxyDelegate::ClearBadProxiesCache() {
173 proxy_resolution_service_->ClearBadProxiesCache();
174 }
175
IsInProxyConfig(const net::ProxyServer & proxy_server) const176 bool NetworkServiceProxyDelegate::IsInProxyConfig(
177 const net::ProxyServer& proxy_server) const {
178 if (!proxy_server.is_valid() || proxy_server.is_direct())
179 return false;
180
181 if (RulesContainsProxy(proxy_config_->rules, proxy_server))
182 return true;
183
184 return false;
185 }
186
MayProxyURL(const GURL & url) const187 bool NetworkServiceProxyDelegate::MayProxyURL(const GURL& url) const {
188 return !proxy_config_->rules.empty();
189 }
190
EligibleForProxy(const net::ProxyInfo & proxy_info,const std::string & method) const191 bool NetworkServiceProxyDelegate::EligibleForProxy(
192 const net::ProxyInfo& proxy_info,
193 const std::string& method) const {
194 bool has_existing_config =
195 !proxy_info.is_direct() || proxy_info.proxy_list().size() > 1u;
196 if (!proxy_config_->should_override_existing_config && has_existing_config)
197 return false;
198
199 if (!proxy_config_->allow_non_idempotent_methods &&
200 !net::HttpUtil::IsMethodIdempotent(method)) {
201 return false;
202 }
203
204 return true;
205 }
206
207 } // namespace network
208