ToB企服应用市场:ToB评测及商务社交产业平台

标题: 39. 干货系列从零用Rust编写负载均衡及代理,正则及格式替换 [打印本页]

作者: 雁过留声    时间: 2024-3-26 03:25
标题: 39. 干货系列从零用Rust编写负载均衡及代理,正则及格式替换
wmproxy

wmproxy已用Rust实现http/https代理, socks5代理, 反向代理, 静态文件服务器,四层TCP/UDP转发,七层负载均衡,内网穿透,后续将实现websocket代理等,会将实现过程分享出来,感兴趣的可以一起造个轮子
项目地址

国内: https://gitee.com/tickbh/wmproxy
github: https://github.com/tickbh/wmproxy
项目设计目标

利用正则替换的能力,能把指定的字符串替换成想要的字符串。
正则库

因为rust官方团队并未将正则正式的加入到std标准库里面,目前我们引用的是regex也是rust-lang官方出品的正则库。
匹配的规则






<ul>(?=...):正向肯定预查,表示要匹配的字符串后面必须紧跟着指定的模式。
(?!...):正向否定预查,表示要匹配的字符串后面不能紧跟着指定的模式。
(?:反向否定预查,表示要匹配的字符串前面不能紧跟着指定的模式。</li/ulh3 id="需求功能"需求功能/h3ullip需要从codeRequest/code中获取codeUrl/code或者codePath/code并将其中间的某一部分替换成另一部分,且需兼容部分不需要替换,两种模式需均能正常的。/p/lilip需要配置中正确的读取分割的信息,如code"{path}/ '/ro(\\w+)/(.*) {path} /ro$1/Cargo.toml' /root/README.md"/code需要正确的分割成code{path}//code, code/ro(\\w+)/(.*) {path} /ro$1/Cargo.toml/code, code/root/README.md/code三个部分。/p/li/ulh3 id="需求实现"需求实现/h3p以下是一段try_paths的配置/p
  1. [[http.server.location]]
  2. rate_limit = "4m/s"
  3. rule = "/root"
  4. file_server = { browse = true }
  5. proxy_pass = ""
  6. try_paths = "{path}/ '/ro(\\w+)/(.*) {path} /ro$1/Cargo.toml' /root/README.md"
复制代码
p我们需要将try_paths做正确的拆分,我们需要将一个字符串按空格做分割,且如果有单引号code'/code或者双引号code"/code需要找到其对应的结尾,防止将其中的字符串做切割。/pp我们将利用以下正则,其中小括号括起来是我们将匹配的内容,用code|/code则表示并行的匹配规则,当第一个没有匹配到将匹配第二个选项。/p
  1. r#"([^\s'"]+)|"([^"]*)"|'([^']*)'"#
复制代码
p其中code([^\s'"]+)/code表示非空白字符开头也非单号双引号开头,直到碰到空格或者单引号双引号停止,那我们将获取第一个匹配项code{path}//code/pp其中code"([^"]*)"/code则表示以双引号开头,中间不能添加任何双引号的其它任意字符,直到匹配到另一个双引号停止,取中间的数据,不取双引号。code'([^']*)'/code则类似双引号,那么code'/ro(\\w+)/(.*) {path} /ro$1/Cargo.toml'/code将是单引号开头的匹配项直到另一个单引号截止,那么匹配的结果为code/ro(\\w+)/(.*) {path} /ro$1/Cargo.toml/code/pp另一个匹配第一个规则code/root/README.md/code,此时我们已经正确将数据进行切割,以下是源码实现,用lazy_static是为了只初始化一次,无需重复耗性能。/p
  1. pub fn split_by_whitespace<'a>(key: &'a str) -> Vec<&'a str> {
  2.     lazy_static! {
  3.         static ref RE: Regex = Regex::new(r#"([^\s'"]+)|"([^"]*)"|'([^']*)'"#).unwrap();
  4.     };
  5.    
  6.     let mut vals = vec![];
  7.     for (_, [value]) in RE.captures_iter(key).map(|c| c.extract()) {
  8.         vals.push(value);
  9.     }
  10.     vals
  11. }
复制代码
测试用例

根据Request生成我们任意想要的内容
  1. pub fn format_req(req: &Request<Body>, formats: &str) -> String {
  2.     let pw = FORMAT_PATTERN_CACHE.with(|m| {
  3.         if !m.borrow().contains_key(&formats) {
  4.             let p = PatternEncoder::new(formats);
  5.             m.borrow_mut().insert(
  6.                 Box::leak(formats.to_string().clone().into_boxed_str()),
  7.                 Arc::new(p),
  8.             );
  9.         }
  10.         m.borrow()[&formats].clone()
  11.     });
  12.     // 将其转化成Record然后进行encode
  13.     let record = ProxyRecord::new_req(Record::builder().level(Level::Info).build(), req);
  14.     let mut buf = vec![];
  15.     pw.encode(&mut SimpleWriter(&mut buf), &record).unwrap();
  16.     String::from_utf8_lossy(&buf[..]).to_string()
  17. }
  18. fn inner_oper_regex(req: &Request<Body>, re: &Regex, vals: &[&str]) -> String {
  19.     let mut ret = String::new();
  20.     let key = Self::format_req(req, vals[0]);
  21.     for idx in 1..vals.len() {
  22.         if idx != 1 {
  23.             ret += " ";
  24.         }
  25.         let val = re.replace_all(&key, vals[idx]);
  26.         ret += &val;
  27.     }
  28.     ret
  29. }
  30. pub fn format_req_may_regex(req: &Request<Body>, formats: &str) -> String {
  31.     let formats = formats.trim();
  32.     if formats.contains(char::is_whitespace) {
  33.         // 因为均是从配置中读取的数据, 在这里缓存正则表达示会在总量上受到配置的限制
  34.         lazy_static! {
  35.             static ref RE_CACHES: Mutex<HashMap<&'static str, Regex>> =
  36.                 Mutex::new(HashMap::new());
  37.         };
  38.         if formats.len() == 0 {
  39.             return String::new();
  40.         }
  41.         let vals = Self::split_by_whitespace(formats);
  42.         if vals.len() < 2 {
  43.             return String::new();
  44.         }
  45.         if let Ok(mut guard) = RE_CACHES.lock() {
  46.             if let Some(re) = guard.get(&vals[1]) {
  47.                 return Self::inner_oper_regex(req, re, &vals[1..]);
  48.             } else {
  49.                 if let Ok(re) = Regex::new(vals[0]) {
  50.                     let ret = Self::inner_oper_regex(req, &re, &vals[1..]);
  51.                     guard.insert(Box::leak(vals[0].to_string().into_boxed_str()), re);
  52.                     return ret;
  53.                 }
  54.             }
  55.         }
  56.     }
  57.     Self::format_req(req, formats)
  58. }
复制代码
小结

正则在计算机的处理中是非常的常用的一种技术,具有许多优点,使得它在文本处理和模式匹配方面非常强大和灵活,有强大的文本匹配和搜索功能,跨平台性跨语言,每种语言都有相应的实现,既简洁又高效便捷,是受欢迎的一种又相处较难的字符串处理技术。
点击 [关注][在看][点赞] 是对作者最大的支持

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4