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