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