16dbb5987Sopenharmony_ci// Copyright (c) 2023 Huawei Device Co., Ltd.
26dbb5987Sopenharmony_ci// Licensed under the Apache License, Version 2.0 (the "License");
36dbb5987Sopenharmony_ci// you may not use this file except in compliance with the License.
46dbb5987Sopenharmony_ci// You may obtain a copy of the License at
56dbb5987Sopenharmony_ci//
66dbb5987Sopenharmony_ci//     http://www.apache.org/licenses/LICENSE-2.0
76dbb5987Sopenharmony_ci//
86dbb5987Sopenharmony_ci// Unless required by applicable law or agreed to in writing, software
96dbb5987Sopenharmony_ci// distributed under the License is distributed on an "AS IS" BASIS,
106dbb5987Sopenharmony_ci// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
116dbb5987Sopenharmony_ci// See the License for the specific language governing permissions and
126dbb5987Sopenharmony_ci// limitations under the License.
136dbb5987Sopenharmony_ci
146dbb5987Sopenharmony_ci//! Proxy implementation.
156dbb5987Sopenharmony_ci
166dbb5987Sopenharmony_ciuse core::convert::TryFrom;
176dbb5987Sopenharmony_ciuse std::net::IpAddr;
186dbb5987Sopenharmony_ci
196dbb5987Sopenharmony_ciuse ylong_http::headers::HeaderValue;
206dbb5987Sopenharmony_ciuse ylong_http::request::uri::{Authority, Scheme, Uri};
216dbb5987Sopenharmony_ci
226dbb5987Sopenharmony_ciuse crate::error::HttpClientError;
236dbb5987Sopenharmony_ciuse crate::util::base64::encode;
246dbb5987Sopenharmony_ciuse crate::util::normalizer::UriFormatter;
256dbb5987Sopenharmony_ci
266dbb5987Sopenharmony_ci/// `Proxies` is responsible for managing a list of proxies.
276dbb5987Sopenharmony_ci#[derive(Clone, Default)]
286dbb5987Sopenharmony_cipub(crate) struct Proxies {
296dbb5987Sopenharmony_ci    list: Vec<Proxy>,
306dbb5987Sopenharmony_ci}
316dbb5987Sopenharmony_ci
326dbb5987Sopenharmony_ciimpl Proxies {
336dbb5987Sopenharmony_ci    pub(crate) fn add_proxy(&mut self, proxy: Proxy) {
346dbb5987Sopenharmony_ci        self.list.push(proxy)
356dbb5987Sopenharmony_ci    }
366dbb5987Sopenharmony_ci
376dbb5987Sopenharmony_ci    pub(crate) fn match_proxy(&self, uri: &Uri) -> Option<&Proxy> {
386dbb5987Sopenharmony_ci        self.list.iter().find(|proxy| proxy.is_intercepted(uri))
396dbb5987Sopenharmony_ci    }
406dbb5987Sopenharmony_ci}
416dbb5987Sopenharmony_ci
426dbb5987Sopenharmony_ci/// Proxy is a configuration of client which should manage the destination
436dbb5987Sopenharmony_ci/// address of request.
446dbb5987Sopenharmony_ci///
456dbb5987Sopenharmony_ci/// A `Proxy` has below rules:
466dbb5987Sopenharmony_ci///
476dbb5987Sopenharmony_ci/// - Manage the uri of destination address.
486dbb5987Sopenharmony_ci/// - Manage the request content such as headers.
496dbb5987Sopenharmony_ci/// - Provide no proxy function which the request will not affected by proxy.
506dbb5987Sopenharmony_ci#[derive(Clone)]
516dbb5987Sopenharmony_cipub(crate) struct Proxy {
526dbb5987Sopenharmony_ci    pub(crate) intercept: Intercept,
536dbb5987Sopenharmony_ci    pub(crate) no_proxy: Option<NoProxy>,
546dbb5987Sopenharmony_ci}
556dbb5987Sopenharmony_ci
566dbb5987Sopenharmony_ciimpl Proxy {
576dbb5987Sopenharmony_ci    pub(crate) fn new(intercept: Intercept) -> Self {
586dbb5987Sopenharmony_ci        Self {
596dbb5987Sopenharmony_ci            intercept,
606dbb5987Sopenharmony_ci            no_proxy: None,
616dbb5987Sopenharmony_ci        }
626dbb5987Sopenharmony_ci    }
636dbb5987Sopenharmony_ci
646dbb5987Sopenharmony_ci    pub(crate) fn http(uri: &str) -> Result<Self, HttpClientError> {
656dbb5987Sopenharmony_ci        Ok(Proxy::new(Intercept::Http(ProxyInfo::new(uri)?)))
666dbb5987Sopenharmony_ci    }
676dbb5987Sopenharmony_ci
686dbb5987Sopenharmony_ci    pub(crate) fn https(uri: &str) -> Result<Self, HttpClientError> {
696dbb5987Sopenharmony_ci        Ok(Proxy::new(Intercept::Https(ProxyInfo::new(uri)?)))
706dbb5987Sopenharmony_ci    }
716dbb5987Sopenharmony_ci
726dbb5987Sopenharmony_ci    pub(crate) fn all(uri: &str) -> Result<Self, HttpClientError> {
736dbb5987Sopenharmony_ci        Ok(Proxy::new(Intercept::All(ProxyInfo::new(uri)?)))
746dbb5987Sopenharmony_ci    }
756dbb5987Sopenharmony_ci
766dbb5987Sopenharmony_ci    pub(crate) fn basic_auth(&mut self, username: &str, password: &str) {
776dbb5987Sopenharmony_ci        let auth = encode(format!("{username}:{password}").as_bytes());
786dbb5987Sopenharmony_ci
796dbb5987Sopenharmony_ci        // All characters in base64 format are valid characters, so we ignore the error.
806dbb5987Sopenharmony_ci        let mut auth = HeaderValue::from_bytes(auth.as_slice()).unwrap();
816dbb5987Sopenharmony_ci        auth.set_sensitive(true);
826dbb5987Sopenharmony_ci
836dbb5987Sopenharmony_ci        match &mut self.intercept {
846dbb5987Sopenharmony_ci            Intercept::All(info) => info.basic_auth = Some(auth),
856dbb5987Sopenharmony_ci            Intercept::Http(info) => info.basic_auth = Some(auth),
866dbb5987Sopenharmony_ci            Intercept::Https(info) => info.basic_auth = Some(auth),
876dbb5987Sopenharmony_ci        }
886dbb5987Sopenharmony_ci    }
896dbb5987Sopenharmony_ci
906dbb5987Sopenharmony_ci    pub(crate) fn no_proxy(&mut self, no_proxy: &str) {
916dbb5987Sopenharmony_ci        self.no_proxy = NoProxy::from_str(no_proxy);
926dbb5987Sopenharmony_ci    }
936dbb5987Sopenharmony_ci
946dbb5987Sopenharmony_ci    pub(crate) fn via_proxy(&self, uri: &Uri) -> Uri {
956dbb5987Sopenharmony_ci        let info = self.intercept.proxy_info();
966dbb5987Sopenharmony_ci        let mut builder = Uri::builder();
976dbb5987Sopenharmony_ci        builder = builder
986dbb5987Sopenharmony_ci            .scheme(info.scheme().clone())
996dbb5987Sopenharmony_ci            .authority(info.authority().clone());
1006dbb5987Sopenharmony_ci
1016dbb5987Sopenharmony_ci        if let Some(path) = uri.path() {
1026dbb5987Sopenharmony_ci            builder = builder.path(path.clone());
1036dbb5987Sopenharmony_ci        }
1046dbb5987Sopenharmony_ci
1056dbb5987Sopenharmony_ci        if let Some(query) = uri.query() {
1066dbb5987Sopenharmony_ci            builder = builder.query(query.clone());
1076dbb5987Sopenharmony_ci        }
1086dbb5987Sopenharmony_ci
1096dbb5987Sopenharmony_ci        // Here all parts of builder is accurate.
1106dbb5987Sopenharmony_ci        builder.build().unwrap()
1116dbb5987Sopenharmony_ci    }
1126dbb5987Sopenharmony_ci
1136dbb5987Sopenharmony_ci    pub(crate) fn is_intercepted(&self, uri: &Uri) -> bool {
1146dbb5987Sopenharmony_ci        // uri is formatted uri, use unwrap directly
1156dbb5987Sopenharmony_ci        let no_proxy = self
1166dbb5987Sopenharmony_ci            .no_proxy
1176dbb5987Sopenharmony_ci            .as_ref()
1186dbb5987Sopenharmony_ci            .map(|no_proxy| no_proxy.contain(uri.host().unwrap().as_str()))
1196dbb5987Sopenharmony_ci            .unwrap_or(false);
1206dbb5987Sopenharmony_ci
1216dbb5987Sopenharmony_ci        match self.intercept {
1226dbb5987Sopenharmony_ci            Intercept::All(_) => !no_proxy,
1236dbb5987Sopenharmony_ci            Intercept::Http(_) => !no_proxy && *uri.scheme().unwrap() == Scheme::HTTP,
1246dbb5987Sopenharmony_ci            Intercept::Https(_) => !no_proxy && *uri.scheme().unwrap() == Scheme::HTTPS,
1256dbb5987Sopenharmony_ci        }
1266dbb5987Sopenharmony_ci    }
1276dbb5987Sopenharmony_ci}
1286dbb5987Sopenharmony_ci
1296dbb5987Sopenharmony_ci#[derive(Clone)]
1306dbb5987Sopenharmony_cipub(crate) enum Intercept {
1316dbb5987Sopenharmony_ci    All(ProxyInfo),
1326dbb5987Sopenharmony_ci    Http(ProxyInfo),
1336dbb5987Sopenharmony_ci    Https(ProxyInfo),
1346dbb5987Sopenharmony_ci}
1356dbb5987Sopenharmony_ci
1366dbb5987Sopenharmony_ciimpl Intercept {
1376dbb5987Sopenharmony_ci    pub(crate) fn proxy_info(&self) -> &ProxyInfo {
1386dbb5987Sopenharmony_ci        match self {
1396dbb5987Sopenharmony_ci            Self::All(info) => info,
1406dbb5987Sopenharmony_ci            Self::Http(info) => info,
1416dbb5987Sopenharmony_ci            Self::Https(info) => info,
1426dbb5987Sopenharmony_ci        }
1436dbb5987Sopenharmony_ci    }
1446dbb5987Sopenharmony_ci}
1456dbb5987Sopenharmony_ci
1466dbb5987Sopenharmony_ci/// ProxyInfo which contains authentication, scheme and host.
1476dbb5987Sopenharmony_ci#[derive(Clone)]
1486dbb5987Sopenharmony_cipub(crate) struct ProxyInfo {
1496dbb5987Sopenharmony_ci    pub(crate) scheme: Scheme,
1506dbb5987Sopenharmony_ci    pub(crate) authority: Authority,
1516dbb5987Sopenharmony_ci    pub(crate) basic_auth: Option<HeaderValue>,
1526dbb5987Sopenharmony_ci}
1536dbb5987Sopenharmony_ci
1546dbb5987Sopenharmony_ciimpl ProxyInfo {
1556dbb5987Sopenharmony_ci    pub(crate) fn new(uri: &str) -> Result<Self, HttpClientError> {
1566dbb5987Sopenharmony_ci        let mut uri = match Uri::try_from(uri) {
1576dbb5987Sopenharmony_ci            Ok(u) => u,
1586dbb5987Sopenharmony_ci            Err(e) => {
1596dbb5987Sopenharmony_ci                return err_from_other!(Build, e);
1606dbb5987Sopenharmony_ci            }
1616dbb5987Sopenharmony_ci        };
1626dbb5987Sopenharmony_ci        // Makes sure that all parts of uri exist.
1636dbb5987Sopenharmony_ci        UriFormatter::new().format(&mut uri)?;
1646dbb5987Sopenharmony_ci        let (scheme, authority, _, _) = uri.into_parts();
1656dbb5987Sopenharmony_ci        // `scheme` and `authority` must have values after formatting.
1666dbb5987Sopenharmony_ci        Ok(Self {
1676dbb5987Sopenharmony_ci            basic_auth: None,
1686dbb5987Sopenharmony_ci            scheme: scheme.unwrap(),
1696dbb5987Sopenharmony_ci            authority: authority.unwrap(),
1706dbb5987Sopenharmony_ci        })
1716dbb5987Sopenharmony_ci    }
1726dbb5987Sopenharmony_ci
1736dbb5987Sopenharmony_ci    pub(crate) fn authority(&self) -> &Authority {
1746dbb5987Sopenharmony_ci        &self.authority
1756dbb5987Sopenharmony_ci    }
1766dbb5987Sopenharmony_ci
1776dbb5987Sopenharmony_ci    pub(crate) fn scheme(&self) -> &Scheme {
1786dbb5987Sopenharmony_ci        &self.scheme
1796dbb5987Sopenharmony_ci    }
1806dbb5987Sopenharmony_ci}
1816dbb5987Sopenharmony_ci
1826dbb5987Sopenharmony_ci#[derive(Clone)]
1836dbb5987Sopenharmony_cienum Ip {
1846dbb5987Sopenharmony_ci    Address(IpAddr),
1856dbb5987Sopenharmony_ci}
1866dbb5987Sopenharmony_ci
1876dbb5987Sopenharmony_ci#[derive(Clone, Default)]
1886dbb5987Sopenharmony_cipub(crate) struct NoProxy {
1896dbb5987Sopenharmony_ci    ips: Vec<Ip>,
1906dbb5987Sopenharmony_ci    domains: Vec<String>,
1916dbb5987Sopenharmony_ci}
1926dbb5987Sopenharmony_ci
1936dbb5987Sopenharmony_ciimpl NoProxy {
1946dbb5987Sopenharmony_ci    pub(crate) fn from_str(no_proxy: &str) -> Option<Self> {
1956dbb5987Sopenharmony_ci        if no_proxy.is_empty() {
1966dbb5987Sopenharmony_ci            return None;
1976dbb5987Sopenharmony_ci        }
1986dbb5987Sopenharmony_ci
1996dbb5987Sopenharmony_ci        let no_proxy_vec = no_proxy.split(',').map(|c| c.trim()).collect::<Vec<&str>>();
2006dbb5987Sopenharmony_ci        let mut ip_list = Vec::new();
2016dbb5987Sopenharmony_ci        let mut domains_list = Vec::new();
2026dbb5987Sopenharmony_ci
2036dbb5987Sopenharmony_ci        for host in no_proxy_vec {
2046dbb5987Sopenharmony_ci            let address = match Uri::from_bytes(host.as_bytes()) {
2056dbb5987Sopenharmony_ci                Ok(uri) => uri,
2066dbb5987Sopenharmony_ci                Err(_) => {
2076dbb5987Sopenharmony_ci                    continue;
2086dbb5987Sopenharmony_ci                }
2096dbb5987Sopenharmony_ci            };
2106dbb5987Sopenharmony_ci            // use unwrap directly, host has been checked before
2116dbb5987Sopenharmony_ci            match address.host().unwrap().as_str().parse::<IpAddr>() {
2126dbb5987Sopenharmony_ci                Ok(ip) => ip_list.push(Ip::Address(ip)),
2136dbb5987Sopenharmony_ci                Err(_) => domains_list.push(host.to_string()),
2146dbb5987Sopenharmony_ci            }
2156dbb5987Sopenharmony_ci        }
2166dbb5987Sopenharmony_ci        Some(NoProxy {
2176dbb5987Sopenharmony_ci            ips: ip_list,
2186dbb5987Sopenharmony_ci            domains: domains_list,
2196dbb5987Sopenharmony_ci        })
2206dbb5987Sopenharmony_ci    }
2216dbb5987Sopenharmony_ci
2226dbb5987Sopenharmony_ci    pub(crate) fn contain(&self, proxy_host: &str) -> bool {
2236dbb5987Sopenharmony_ci        match proxy_host.parse::<IpAddr>() {
2246dbb5987Sopenharmony_ci            Ok(ip) => self.contains_ip(ip),
2256dbb5987Sopenharmony_ci            Err(_) => self.contains_domain(proxy_host),
2266dbb5987Sopenharmony_ci        }
2276dbb5987Sopenharmony_ci    }
2286dbb5987Sopenharmony_ci
2296dbb5987Sopenharmony_ci    fn contains_ip(&self, ip: IpAddr) -> bool {
2306dbb5987Sopenharmony_ci        for Ip::Address(i) in self.ips.iter() {
2316dbb5987Sopenharmony_ci            if &ip == i {
2326dbb5987Sopenharmony_ci                return true;
2336dbb5987Sopenharmony_ci            }
2346dbb5987Sopenharmony_ci        }
2356dbb5987Sopenharmony_ci        false
2366dbb5987Sopenharmony_ci    }
2376dbb5987Sopenharmony_ci
2386dbb5987Sopenharmony_ci    fn contains_domain(&self, domain: &str) -> bool {
2396dbb5987Sopenharmony_ci        for block_domain in self.domains.iter() {
2406dbb5987Sopenharmony_ci            let mut block_domain = block_domain.clone();
2416dbb5987Sopenharmony_ci            // Changes *.example.com to .example.com
2426dbb5987Sopenharmony_ci            if (block_domain.starts_with('*')) && (block_domain.len() > 1) {
2436dbb5987Sopenharmony_ci                block_domain = block_domain.trim_matches('*').to_string();
2446dbb5987Sopenharmony_ci            }
2456dbb5987Sopenharmony_ci
2466dbb5987Sopenharmony_ci            if block_domain == "*"
2476dbb5987Sopenharmony_ci                || block_domain.ends_with(domain)
2486dbb5987Sopenharmony_ci                || block_domain == domain
2496dbb5987Sopenharmony_ci                || block_domain.trim_matches('.') == domain
2506dbb5987Sopenharmony_ci            {
2516dbb5987Sopenharmony_ci                return true;
2526dbb5987Sopenharmony_ci            } else if domain.ends_with(&block_domain) {
2536dbb5987Sopenharmony_ci                // .example.com and www.
2546dbb5987Sopenharmony_ci                if block_domain.starts_with('.')
2556dbb5987Sopenharmony_ci                    || domain.as_bytes().get(domain.len() - block_domain.len() - 1) == Some(&b'.')
2566dbb5987Sopenharmony_ci                {
2576dbb5987Sopenharmony_ci                    return true;
2586dbb5987Sopenharmony_ci                }
2596dbb5987Sopenharmony_ci            }
2606dbb5987Sopenharmony_ci        }
2616dbb5987Sopenharmony_ci        false
2626dbb5987Sopenharmony_ci    }
2636dbb5987Sopenharmony_ci}
2646dbb5987Sopenharmony_ci
2656dbb5987Sopenharmony_ci#[cfg(test)]
2666dbb5987Sopenharmony_cimod ut_proxy {
2676dbb5987Sopenharmony_ci    use ylong_http::request::uri::{Scheme, Uri};
2686dbb5987Sopenharmony_ci
2696dbb5987Sopenharmony_ci    use crate::util::proxy::{Proxies, Proxy};
2706dbb5987Sopenharmony_ci
2716dbb5987Sopenharmony_ci    /// UT test cases for `Proxy::via_proxy`.
2726dbb5987Sopenharmony_ci    ///
2736dbb5987Sopenharmony_ci    /// # Brief
2746dbb5987Sopenharmony_ci    /// 1. Creates a `Proxy`.
2756dbb5987Sopenharmony_ci    /// 2. Calls `Proxy::via_proxy` with some `Uri`to get the results.
2766dbb5987Sopenharmony_ci    /// 4. Checks if the test result is correct.
2776dbb5987Sopenharmony_ci    #[test]
2786dbb5987Sopenharmony_ci    fn ut_via_proxy() {
2796dbb5987Sopenharmony_ci        let proxy = Proxy::http("http://www.example.com").unwrap();
2806dbb5987Sopenharmony_ci        let uri = Uri::from_bytes(b"http://www.example2.com").unwrap();
2816dbb5987Sopenharmony_ci        let res = proxy.via_proxy(&uri);
2826dbb5987Sopenharmony_ci        assert_eq!(res.to_string(), "http://www.example.com:80");
2836dbb5987Sopenharmony_ci    }
2846dbb5987Sopenharmony_ci
2856dbb5987Sopenharmony_ci    /// UT test cases for `Proxies`.
2866dbb5987Sopenharmony_ci    ///
2876dbb5987Sopenharmony_ci    /// # Brief
2886dbb5987Sopenharmony_ci    /// 1. Creates a `Proxies`.
2896dbb5987Sopenharmony_ci    /// 2. Adds some `Proxy` to `Proxies`
2906dbb5987Sopenharmony_ci    /// 3. Calls `Proxies::match_proxy` with some `Uri`s and get the results.
2916dbb5987Sopenharmony_ci    /// 4. Checks if the test result is correct.
2926dbb5987Sopenharmony_ci    #[test]
2936dbb5987Sopenharmony_ci    fn ut_proxies() {
2946dbb5987Sopenharmony_ci        let mut proxies = Proxies::default();
2956dbb5987Sopenharmony_ci        proxies.add_proxy(Proxy::http("http://www.aaa.com").unwrap());
2966dbb5987Sopenharmony_ci        proxies.add_proxy(Proxy::https("http://www.bbb.com").unwrap());
2976dbb5987Sopenharmony_ci
2986dbb5987Sopenharmony_ci        let uri = Uri::from_bytes(b"http://www.example.com").unwrap();
2996dbb5987Sopenharmony_ci        let proxy = proxies.match_proxy(&uri).unwrap();
3006dbb5987Sopenharmony_ci        assert!(proxy.no_proxy.is_none());
3016dbb5987Sopenharmony_ci        let info = proxy.intercept.proxy_info();
3026dbb5987Sopenharmony_ci        assert_eq!(info.scheme, Scheme::HTTP);
3036dbb5987Sopenharmony_ci        assert_eq!(info.authority.to_string(), "www.aaa.com:80");
3046dbb5987Sopenharmony_ci
3056dbb5987Sopenharmony_ci        let uri = Uri::from_bytes(b"https://www.example.com").unwrap();
3066dbb5987Sopenharmony_ci        let matched = proxies.match_proxy(&uri).unwrap();
3076dbb5987Sopenharmony_ci        assert!(matched.no_proxy.is_none());
3086dbb5987Sopenharmony_ci        let info = matched.intercept.proxy_info();
3096dbb5987Sopenharmony_ci        assert_eq!(info.scheme, Scheme::HTTP);
3106dbb5987Sopenharmony_ci        assert_eq!(info.authority.to_string(), "www.bbb.com:80");
3116dbb5987Sopenharmony_ci
3126dbb5987Sopenharmony_ci        // with no_proxy
3136dbb5987Sopenharmony_ci        let mut proxies = Proxies::default();
3146dbb5987Sopenharmony_ci        let mut proxy = Proxy::http("http://www.aaa.com").unwrap();
3156dbb5987Sopenharmony_ci        proxy.no_proxy("http://no_proxy.aaa.com");
3166dbb5987Sopenharmony_ci        proxies.add_proxy(proxy);
3176dbb5987Sopenharmony_ci
3186dbb5987Sopenharmony_ci        let uri = Uri::from_bytes(b"http://www.bbb.com").unwrap();
3196dbb5987Sopenharmony_ci        let matched = proxies.match_proxy(&uri).unwrap();
3206dbb5987Sopenharmony_ci        let info = matched.intercept.proxy_info();
3216dbb5987Sopenharmony_ci        assert_eq!(info.scheme, Scheme::HTTP);
3226dbb5987Sopenharmony_ci        assert_eq!(info.authority.to_string(), "www.aaa.com:80");
3236dbb5987Sopenharmony_ci
3246dbb5987Sopenharmony_ci        let uri = Uri::from_bytes(b"http://no_proxy.aaa.com").unwrap();
3256dbb5987Sopenharmony_ci        assert!(proxies.match_proxy(&uri).is_none());
3266dbb5987Sopenharmony_ci
3276dbb5987Sopenharmony_ci        let mut proxies = Proxies::default();
3286dbb5987Sopenharmony_ci        let mut proxy = Proxy::http("http://www.aaa.com").unwrap();
3296dbb5987Sopenharmony_ci        proxy.no_proxy(".aaa.com");
3306dbb5987Sopenharmony_ci        proxies.add_proxy(proxy);
3316dbb5987Sopenharmony_ci
3326dbb5987Sopenharmony_ci        let uri = Uri::from_bytes(b"http://no_proxy.aaa.com").unwrap();
3336dbb5987Sopenharmony_ci        assert!(proxies.match_proxy(&uri).is_none());
3346dbb5987Sopenharmony_ci
3356dbb5987Sopenharmony_ci        let mut proxies = Proxies::default();
3366dbb5987Sopenharmony_ci        let mut proxy = Proxy::http("http://127.0.0.1:3000").unwrap();
3376dbb5987Sopenharmony_ci        proxy.no_proxy("http://127.0.0.1:80");
3386dbb5987Sopenharmony_ci        proxies.add_proxy(proxy);
3396dbb5987Sopenharmony_ci
3406dbb5987Sopenharmony_ci        let uri = Uri::from_bytes(b"http://127.0.0.1:80").unwrap();
3416dbb5987Sopenharmony_ci        assert!(proxies.match_proxy(&uri).is_none());
3426dbb5987Sopenharmony_ci    }
3436dbb5987Sopenharmony_ci}
344