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