1 // Copyright (c) 2023 Huawei Device Co., Ltd.
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 //     http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 //! Proxy implementation.
15 
16 use core::convert::TryFrom;
17 use std::net::IpAddr;
18 
19 use ylong_http::headers::HeaderValue;
20 use ylong_http::request::uri::{Authority, Scheme, Uri};
21 
22 use crate::error::HttpClientError;
23 use crate::util::base64::encode;
24 use crate::util::normalizer::UriFormatter;
25 
26 /// `Proxies` is responsible for managing a list of proxies.
27 #[derive(Clone, Default)]
28 pub(crate) struct Proxies {
29     list: Vec<Proxy>,
30 }
31 
32 impl Proxies {
add_proxy(&mut self, proxy: Proxy)33     pub(crate) fn add_proxy(&mut self, proxy: Proxy) {
34         self.list.push(proxy)
35     }
36 
match_proxy(&self, uri: &Uri) -> Option<&Proxy>37     pub(crate) fn match_proxy(&self, uri: &Uri) -> Option<&Proxy> {
38         self.list.iter().find(|proxy| proxy.is_intercepted(uri))
39     }
40 }
41 
42 /// Proxy is a configuration of client which should manage the destination
43 /// address of request.
44 ///
45 /// A `Proxy` has below rules:
46 ///
47 /// - Manage the uri of destination address.
48 /// - Manage the request content such as headers.
49 /// - Provide no proxy function which the request will not affected by proxy.
50 #[derive(Clone)]
51 pub(crate) struct Proxy {
52     pub(crate) intercept: Intercept,
53     pub(crate) no_proxy: Option<NoProxy>,
54 }
55 
56 impl Proxy {
new(intercept: Intercept) -> Self57     pub(crate) fn new(intercept: Intercept) -> Self {
58         Self {
59             intercept,
60             no_proxy: None,
61         }
62     }
63 
http(uri: &str) -> Result<Self, HttpClientError>64     pub(crate) fn http(uri: &str) -> Result<Self, HttpClientError> {
65         Ok(Proxy::new(Intercept::Http(ProxyInfo::new(uri)?)))
66     }
67 
https(uri: &str) -> Result<Self, HttpClientError>68     pub(crate) fn https(uri: &str) -> Result<Self, HttpClientError> {
69         Ok(Proxy::new(Intercept::Https(ProxyInfo::new(uri)?)))
70     }
71 
all(uri: &str) -> Result<Self, HttpClientError>72     pub(crate) fn all(uri: &str) -> Result<Self, HttpClientError> {
73         Ok(Proxy::new(Intercept::All(ProxyInfo::new(uri)?)))
74     }
75 
basic_auth(&mut self, username: &str, password: &str)76     pub(crate) fn basic_auth(&mut self, username: &str, password: &str) {
77         let auth = encode(format!("{username}:{password}").as_bytes());
78 
79         // All characters in base64 format are valid characters, so we ignore the error.
80         let mut auth = HeaderValue::from_bytes(auth.as_slice()).unwrap();
81         auth.set_sensitive(true);
82 
83         match &mut self.intercept {
84             Intercept::All(info) => info.basic_auth = Some(auth),
85             Intercept::Http(info) => info.basic_auth = Some(auth),
86             Intercept::Https(info) => info.basic_auth = Some(auth),
87         }
88     }
89 
no_proxy(&mut self, no_proxy: &str)90     pub(crate) fn no_proxy(&mut self, no_proxy: &str) {
91         self.no_proxy = NoProxy::from_str(no_proxy);
92     }
93 
via_proxy(&self, uri: &Uri) -> Uri94     pub(crate) fn via_proxy(&self, uri: &Uri) -> Uri {
95         let info = self.intercept.proxy_info();
96         let mut builder = Uri::builder();
97         builder = builder
98             .scheme(info.scheme().clone())
99             .authority(info.authority().clone());
100 
101         if let Some(path) = uri.path() {
102             builder = builder.path(path.clone());
103         }
104 
105         if let Some(query) = uri.query() {
106             builder = builder.query(query.clone());
107         }
108 
109         // Here all parts of builder is accurate.
110         builder.build().unwrap()
111     }
112 
is_intercepted(&self, uri: &Uri) -> bool113     pub(crate) fn is_intercepted(&self, uri: &Uri) -> bool {
114         // uri is formatted uri, use unwrap directly
115         let no_proxy = self
116             .no_proxy
117             .as_ref()
118             .map(|no_proxy| no_proxy.contain(uri.host().unwrap().as_str()))
119             .unwrap_or(false);
120 
121         match self.intercept {
122             Intercept::All(_) => !no_proxy,
123             Intercept::Http(_) => !no_proxy && *uri.scheme().unwrap() == Scheme::HTTP,
124             Intercept::Https(_) => !no_proxy && *uri.scheme().unwrap() == Scheme::HTTPS,
125         }
126     }
127 }
128 
129 #[derive(Clone)]
130 pub(crate) enum Intercept {
131     All(ProxyInfo),
132     Http(ProxyInfo),
133     Https(ProxyInfo),
134 }
135 
136 impl Intercept {
proxy_info(&self) -> &ProxyInfo137     pub(crate) fn proxy_info(&self) -> &ProxyInfo {
138         match self {
139             Self::All(info) => info,
140             Self::Http(info) => info,
141             Self::Https(info) => info,
142         }
143     }
144 }
145 
146 /// ProxyInfo which contains authentication, scheme and host.
147 #[derive(Clone)]
148 pub(crate) struct ProxyInfo {
149     pub(crate) scheme: Scheme,
150     pub(crate) authority: Authority,
151     pub(crate) basic_auth: Option<HeaderValue>,
152 }
153 
154 impl ProxyInfo {
new(uri: &str) -> Result<Self, HttpClientError>155     pub(crate) fn new(uri: &str) -> Result<Self, HttpClientError> {
156         let mut uri = match Uri::try_from(uri) {
157             Ok(u) => u,
158             Err(e) => {
159                 return err_from_other!(Build, e);
160             }
161         };
162         // Makes sure that all parts of uri exist.
163         UriFormatter::new().format(&mut uri)?;
164         let (scheme, authority, _, _) = uri.into_parts();
165         // `scheme` and `authority` must have values after formatting.
166         Ok(Self {
167             basic_auth: None,
168             scheme: scheme.unwrap(),
169             authority: authority.unwrap(),
170         })
171     }
172 
authority(&self) -> &Authority173     pub(crate) fn authority(&self) -> &Authority {
174         &self.authority
175     }
176 
scheme(&self) -> &Scheme177     pub(crate) fn scheme(&self) -> &Scheme {
178         &self.scheme
179     }
180 }
181 
182 #[derive(Clone)]
183 enum Ip {
184     Address(IpAddr),
185 }
186 
187 #[derive(Clone, Default)]
188 pub(crate) struct NoProxy {
189     ips: Vec<Ip>,
190     domains: Vec<String>,
191 }
192 
193 impl NoProxy {
from_str(no_proxy: &str) -> Option<Self>194     pub(crate) fn from_str(no_proxy: &str) -> Option<Self> {
195         if no_proxy.is_empty() {
196             return None;
197         }
198 
199         let no_proxy_vec = no_proxy.split(',').map(|c| c.trim()).collect::<Vec<&str>>();
200         let mut ip_list = Vec::new();
201         let mut domains_list = Vec::new();
202 
203         for host in no_proxy_vec {
204             let address = match Uri::from_bytes(host.as_bytes()) {
205                 Ok(uri) => uri,
206                 Err(_) => {
207                     continue;
208                 }
209             };
210             // use unwrap directly, host has been checked before
211             match address.host().unwrap().as_str().parse::<IpAddr>() {
212                 Ok(ip) => ip_list.push(Ip::Address(ip)),
213                 Err(_) => domains_list.push(host.to_string()),
214             }
215         }
216         Some(NoProxy {
217             ips: ip_list,
218             domains: domains_list,
219         })
220     }
221 
contain(&self, proxy_host: &str) -> bool222     pub(crate) fn contain(&self, proxy_host: &str) -> bool {
223         match proxy_host.parse::<IpAddr>() {
224             Ok(ip) => self.contains_ip(ip),
225             Err(_) => self.contains_domain(proxy_host),
226         }
227     }
228 
contains_ip(&self, ip: IpAddr) -> bool229     fn contains_ip(&self, ip: IpAddr) -> bool {
230         for Ip::Address(i) in self.ips.iter() {
231             if &ip == i {
232                 return true;
233             }
234         }
235         false
236     }
237 
contains_domain(&self, domain: &str) -> bool238     fn contains_domain(&self, domain: &str) -> bool {
239         for block_domain in self.domains.iter() {
240             let mut block_domain = block_domain.clone();
241             // Changes *.example.com to .example.com
242             if (block_domain.starts_with('*')) && (block_domain.len() > 1) {
243                 block_domain = block_domain.trim_matches('*').to_string();
244             }
245 
246             if block_domain == "*"
247                 || block_domain.ends_with(domain)
248                 || block_domain == domain
249                 || block_domain.trim_matches('.') == domain
250             {
251                 return true;
252             } else if domain.ends_with(&block_domain) {
253                 // .example.com and www.
254                 if block_domain.starts_with('.')
255                     || domain.as_bytes().get(domain.len() - block_domain.len() - 1) == Some(&b'.')
256                 {
257                     return true;
258                 }
259             }
260         }
261         false
262     }
263 }
264 
265 #[cfg(test)]
266 mod ut_proxy {
267     use ylong_http::request::uri::{Scheme, Uri};
268 
269     use crate::util::proxy::{Proxies, Proxy};
270 
271     /// UT test cases for `Proxy::via_proxy`.
272     ///
273     /// # Brief
274     /// 1. Creates a `Proxy`.
275     /// 2. Calls `Proxy::via_proxy` with some `Uri`to get the results.
276     /// 4. Checks if the test result is correct.
277     #[test]
ut_via_proxy()278     fn ut_via_proxy() {
279         let proxy = Proxy::http("http://www.example.com").unwrap();
280         let uri = Uri::from_bytes(b"http://www.example2.com").unwrap();
281         let res = proxy.via_proxy(&uri);
282         assert_eq!(res.to_string(), "http://www.example.com:80");
283     }
284 
285     /// UT test cases for `Proxies`.
286     ///
287     /// # Brief
288     /// 1. Creates a `Proxies`.
289     /// 2. Adds some `Proxy` to `Proxies`
290     /// 3. Calls `Proxies::match_proxy` with some `Uri`s and get the results.
291     /// 4. Checks if the test result is correct.
292     #[test]
ut_proxies()293     fn ut_proxies() {
294         let mut proxies = Proxies::default();
295         proxies.add_proxy(Proxy::http("http://www.aaa.com").unwrap());
296         proxies.add_proxy(Proxy::https("http://www.bbb.com").unwrap());
297 
298         let uri = Uri::from_bytes(b"http://www.example.com").unwrap();
299         let proxy = proxies.match_proxy(&uri).unwrap();
300         assert!(proxy.no_proxy.is_none());
301         let info = proxy.intercept.proxy_info();
302         assert_eq!(info.scheme, Scheme::HTTP);
303         assert_eq!(info.authority.to_string(), "www.aaa.com:80");
304 
305         let uri = Uri::from_bytes(b"https://www.example.com").unwrap();
306         let matched = proxies.match_proxy(&uri).unwrap();
307         assert!(matched.no_proxy.is_none());
308         let info = matched.intercept.proxy_info();
309         assert_eq!(info.scheme, Scheme::HTTP);
310         assert_eq!(info.authority.to_string(), "www.bbb.com:80");
311 
312         // with no_proxy
313         let mut proxies = Proxies::default();
314         let mut proxy = Proxy::http("http://www.aaa.com").unwrap();
315         proxy.no_proxy("http://no_proxy.aaa.com");
316         proxies.add_proxy(proxy);
317 
318         let uri = Uri::from_bytes(b"http://www.bbb.com").unwrap();
319         let matched = proxies.match_proxy(&uri).unwrap();
320         let info = matched.intercept.proxy_info();
321         assert_eq!(info.scheme, Scheme::HTTP);
322         assert_eq!(info.authority.to_string(), "www.aaa.com:80");
323 
324         let uri = Uri::from_bytes(b"http://no_proxy.aaa.com").unwrap();
325         assert!(proxies.match_proxy(&uri).is_none());
326 
327         let mut proxies = Proxies::default();
328         let mut proxy = Proxy::http("http://www.aaa.com").unwrap();
329         proxy.no_proxy(".aaa.com");
330         proxies.add_proxy(proxy);
331 
332         let uri = Uri::from_bytes(b"http://no_proxy.aaa.com").unwrap();
333         assert!(proxies.match_proxy(&uri).is_none());
334 
335         let mut proxies = Proxies::default();
336         let mut proxy = Proxy::http("http://127.0.0.1:3000").unwrap();
337         proxy.no_proxy("http://127.0.0.1:80");
338         proxies.add_proxy(proxy);
339 
340         let uri = Uri::from_bytes(b"http://127.0.0.1:80").unwrap();
341         assert!(proxies.match_proxy(&uri).is_none());
342     }
343 }
344