wmproxy

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

项目地址

国内: https://gitee.com/tickbh/wmproxy

github: https://github.com/tickbh/wmproxy

设计目标

快速的设置多IP绑定,及IP端口段的支持,方便快速的自定义能力。

IP解析示例

以下是常见的IP解析示例情况,本地ip为192.168.0.100示例:

  • 正常IP解析

    • 127.0.0.1:8869 解析成 ipv4 127.0.0.1 端口 8869,只接受本地来的连接信息
    • 0.0.0.0:8869 解析成 ipv4 0.0.0.0 端口 8869,可接受所有来自ipv4的连接信息
  • :开头的地址,且不包含-

    • :8869 解析成 ipv4 127.0.0.1 端口 8869 及 ipv4 192.168.0.100 端口 8869
  • 包含-的地址

    • :8869-:8871 解析成 ipv4 127.0.0.1 端口 8869 - 8871 三个端口地址 及 ipv4 192.168.0.100 端口 8869 - 8871 三个端口地址,总共6个端口地址
    • 127.0.0.1:8869-:8871 解析成 ipv4 127.0.0.1 端口 8869 - 8871 三个端口地址 总共3个端口地址
    • 127.0.0.1:8869-192.168.0.100:8871 解析成 ipv4 127.0.0.1 端口 8869 - 8871 三个端口地址 总共3个端口地址,忽略后面的地址,只接受端口号
  • 手动多个地址,可以空格或者,做间隔

    • 127.0.0.1:8869 127.0.0.1:8899 192.168.0.100:8899 就相应的解析成三个端口地址

定义类

由于解析出来的地址可能是多个或者个单个,这里用数组来进行表示

#[derive(Debug, Clone)]
pub struct WrapVecAddr(pub Vec<SocketAddr>);

通常序列化会用到FromStr将字符串转化成类

反序列化都会用到Display将类转化成字符串

所以在这里,我们将实现FromStrDisplay


impl FromStr for WrapVecAddr {
type Err = AddrParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
// 范围的如:8080-:8090, 表示11端口
if s.contains("-") {
let vals = s
.split(&['-'])
.filter(|s| !s.is_empty())
.collect::<Vec<&str>>();
let start = parse_socker_addr(vals[0])?;
if vals.len() != 2 {
return Ok(WrapVecAddr(start));
} else {
let end = parse_socker_addr(vals[1])?;
let mut results = vec![];
for port in start[0].port()..=end[1].port() {
for idx in &start {
let mut addr = idx.clone();
addr.set_port(port);
results.push(addr);
}
}
return Ok(WrapVecAddr(results));
}
} else {
let vals = s
.split(&[',', ' '])
.filter(|s| !s.is_empty())
.collect::<Vec<&str>>();
let mut results = vec![];
for s in vals {
results.extend(parse_socker_addr(s)?);
}
Ok(WrapVecAddr(results))
}
}
} impl Display for WrapVecAddr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut values = vec![];
for a in &self.0 {
values.push(format!("{}", a));
}
f.write_str(&values.join(","))
}
}

这样子后我们将配置加上就可以自动实现序列化及反序列化了

#[serde_as]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerConfig {
#[serde_as(as = "DisplayFromStr")]
pub bind_addr: WrapVecAddr,
// ...
}

获取本机地址

通过库local-ip-address获取本地IP地址,再根据缺省IP时添加本地IP地址访问,因为缺省时有可能是需要本地内网进行访问。所以需要补上本地网卡地址。所以我们在解析以:时做了特殊处理:


fn parse_socker_addr(s: &str) -> Result<Vec<SocketAddr>, AddrParseError> {
if s.starts_with(":") {
let port = s.trim_start_matches(':');
let mut results = vec![];
if let Ok(port) = port.parse::<u16>() {
if let Ok(v) = local_ip() {
results.push(SocketAddr::new(v, port));
}
if let Ok(v) = local_ipv6() {
results.push(SocketAddr::new(v, port));
}
results.push(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), port));
} else {
results.push(format!("127.0.0.1{s}").parse::<SocketAddr>()?);
}
Ok(results)
} else {
let addr = s.parse::<SocketAddr>()?;
Ok(vec![addr])
}
}

参数获取

以下举例file-server的参数

#[derive(Debug, Clone, Bpaf)]
#[allow(dead_code)]
struct FileServerConfig {
/// 静态文件根目录路径
#[bpaf(short, long, fallback(String::new()))]
pub(crate) root: String,
#[bpaf(
short,
long,
fallback(WrapVecAddr(vec![SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8869)])),
display_fallback
)]
/// 监听地址
pub(crate) listen: WrapVecAddr,
#[bpaf(long)]
/// 监听地址
pub(crate) listen_ssl: Option<WrapVecAddr>,
/// ...
}

如此我们就可以轻松的用SSL监听及普通的监听添加多个端口的支持。

wmproxy file-server --listen :8869-8871

此时我们可以同时监听3个端口均支持文件服务器。此时我们就可以轻松控制多个端口地址。

绑定多个地址

以下是负载均衡中的绑定示例

for v in &value.bind_addr.0 {
if bind_addr_set.contains(&v) {
continue;
}
bind_addr_set.insert(v);
let url = format!("http://{}", v);
log::info!("HTTP服务:{},提供http处理及转发功能。", Style::new().blink().green().apply_to(url));
let listener = Helper::bind(v).await?;
listeners.push(listener);
tlss.push(false);
} for v in &value.bind_ssl.0 {
if bind_addr_set.contains(&v) {
continue;
}
bind_addr_set.insert(v);
if !is_ssl {
return Err(crate::ProxyError::Extension("配置SSL端口但未配置证书"));
}
let url = format!("https://{}", v);
log::info!("HTTPs服务:{},提供https处理及转发功能。", Style::new().blink().green().apply_to(url));
let listener = Helper::bind(v).await?;
listeners.push(listener);
tlss.push(is_ssl);
}

支持ssl绑定及非ssl绑定同一个location。

其中链接信息使用了console,输出了绿色的,可以点击的url链接。可以方便在启动的时候进行点击。

图中圈圈位置是可以点击跳转成url,方便本地开发环境的时候测试使用。

总结

通过FromStrDisplay的重定义,我们可以支持更强大的自定义的序列化操作,系统绑定端口既认端口号也认绑定IP,所以我们可以对同个端口进行多次绑定。

点击 [关注][在看][点赞] 是对作者最大的支持

49从零开始用Rust编写nginx,我竟然在同一个端口上绑定了多少IP的更多相关文章

  1. bloom-server 基于 rust 编写的 rest api cache 中间件

    bloom-server 基于 rust 编写的 rest api cache 中间件,他位于lb 与api worker 之间,使用redis 作为缓存内容存储, 我们需要做的就是配置proxy,同 ...

  2. centos LNMP第一部分环境搭建 LAMP LNMP安装先后顺序 php安装 安装nginx 编写nginx启动脚本 懒汉模式 mv /usr/php/{p.conf.default,p.conf} php运行方式SAPI介绍 第二十三节课

    centos  LNMP第一部分环境搭建 LAMP安装先后顺序  LNMP安装先后顺序 php安装 安装nginx  编写nginx启动脚本   懒汉模式  mv   /usr/local/php/{ ...

  3. Linux实战教学笔记37:企业级Nginx Web服务优化实战(上)

    一,Nginx基本安全优化 1.1 调整参数隐藏Nginx软件版本号信息 一般来说,软件的漏洞都和版本有关,这个很像汽车的缺陷,同一批次的要有问题就都有问题,别的批次可能就都是好的.因此,我们应尽量隐 ...

  4. 基于Nginx dyups模块的站点动态上下线并实现简单服务治理

    简介 今天主要讨论一下,对于分布式服务,站点如何平滑的上下线问题. 分布式服务 在分布式服务下,我们会用nginx做负载均衡, 业务站点访问某服务站点的时候, 统一走nginx, 然后nginx根据一 ...

  5. Nginx 中 fastcgi_pass 监听端口 unix socket和tcp socket差

    Nginx 中 fastcgi_pass 监听端口 unix socket和tcp socket差别   Nginx连接fastcgi的方式有2种:unix domain socket和TCP,Uni ...

  6. 使用nginx反向代理实现多端口映射(未解决)

    问题: 想实现访问在同一个主机上实现多个域名访问, 如用 blog.xxx.com访问博客(使用8000端口), app.xxx.com访问其他应用(使用8080端口): 不同的服务用URL区分,不用 ...

  7. 使用python找出nginx访问日志中访问次数最多的10个ip排序生成网页

    使用python找出nginx访问日志中访问次数最多的10个ip排序生成网页 方法1:linux下使用awk命令 # cat access1.log | awk '{print $1" &q ...

  8. nginx 反向代理时丢失端口的解决方案

        今天,配置nginx反向代理时遇到一个问题,当设置nginx监听80端口时转发请求没有问题.但一旦设置为监听其他端口,就一直跳转不正常:如,访问欢迎页面时应该是重定向到登录页面,在这个重定向的 ...

  9. nginx配置基于域名、端口、IP的虚拟主机

    1.基于域名的虚拟主机: 绝大多数企业对外提供服务的网站使用的都是基于域名的主机,通过不同的域名区分不同的虚拟主机. 首先我们进入安装nginxd的目录下:/application/nginx-1.6 ...

  10. nginx——绑定 Nginx 进程到不同的 CPU 上

    为什么要绑定 Nginx 进程到不同的 CPU 上 :默认情况下,Nginx 的多个进程有可能跑在某一个 CPU 或 CPU 的某一核上,导致 Nginx 进程使用硬件的资源不均,因此绑定 Nginx ...

随机推荐

  1. 基于python+django的求职招聘网站-网上招聘管理系统设计与实现

    该系统是基于python+django的求职招聘网站.网上招聘管理系统.网上人才招聘系统.毕业生求职招聘系统.大学生求职招聘系统.校园招聘系统.企业招聘系统.系统适合场景:大学生.课程作业.毕业设计. ...

  2. Prime Time - 介绍

    Prime Time是对timing进行分析 Prime Time使用的是STA方法进行分析 工具会有更新,但是核心内容是不变的 Prime Time(intro to STA) 没有PT工具的时候, ...

  3. Oracle数据库如何解决创建用户名开头必须要C##问题?

    1.问题 我们在创建用户,概要文件等时,由于使用的是容器数据库,其文件名必须以C##开头. 我们在学习过程中暂时不需要对齐进行区分,所以如何修改这个设定呢? 2.解决 参考链接如何解决创建用户名开头必 ...

  4. JVM内存用量的再学习

    JVM内存用量的再学习 背景 最近解决一个SQLServer的问题耗时很久. 最终找到了一个看似合理的问题解释. 但是感觉不能只是总结于数据库方面 因为为了解决这个问题增加了很多监控措施. 所以想就这 ...

  5. 银河麒麟安装多版本gcc的方式方法

    银河麒麟安装多版本gcc的方式方法 背景 最近想升级一下gcc 但是发现自己编译的话非常麻烦 记得之前CentOS7的时候有一个scl的处理 发现CentOS8 已经没有scl的仓库了 简单验证了一下 ...

  6. [转帖]金仓数据库KingbaseES数据目录结构

    KingbaseES数据库结构 [kingbase@postgresV8]$ tree -LP2data/ . ├── data │ ├── base # 存储用户创建的数据库文件及隶属于用户数据库的 ...

  7. [转帖]《Linux性能优化实战》笔记(23)—— 内核线程 CPU 利用率过高,perf 与 火焰图

    在排查网络问题时,我们还经常碰到的一个问题,就是内核线程的 CPU 使用率很高.比如,在高并发的场景中,内核线程 ksoftirqd 的 CPU 使用率通常就会比较高.回顾一下前面学过的 CPU 和网 ...

  8. [转帖]py_innodb_page_info.py工具使用

    目录 一.Linux安装Python3 1. 解压包 2. 安装环境 3. 生成编译脚本 4. 检查python3.10的编译器 5. 建立Python3和pip3的软链 6. 添加到PATH 7.  ...

  9. 【构造,树】【Loj】Loj6669 Nauuo and Binary Tree

    2023.7.3 Problem Link 交互库有一棵 \(n\) 个点的二叉树,你每次可以询问两个点之间的距离,猜出这棵二叉树.\(n\le 3000\),询问次数上限 \(30000\). 首先 ...

  10. 01uni-app的创建运行在不同端上的配置 以及tarBar的配置

    uni-app的创建### 01==>创建uni-app的项目非常简单.不需要注意什么注意点哈!! 创建项目的时候 可以参考官网 https://uniapp.dcloud.io/quickst ...