wmproxy

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

项目地址

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

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

设计目标

通过简单配置方便用户快速使用tcp转websocket及websocket转tcp,也可支持http升级到websocket协议。

改造http升级websocket

因为负载均衡的不确定性,在未读取数据前,未能确定当前的处理逻辑

  • /root/proxy.png 访问当前的件服务器
  • /api/up 通过负载均衡访问后端服务器
  • /ws 将连接升级成websocket
  • 其它情况

    所以我们得预备能支持websocket的可能,那我们将同时设置回调HTTP及websocket,源码在reverse/http.rs
let timeout = oper.servers[0].comm.build_client_timeout();
let mut server = Server::builder()
.addr(addr)
.timeout_layer(timeout)
.stream(inbound); // 设置HTTP回调
server.set_callback_http(Box::new(Operate { inner: oper }));
// 设置websocket回调,客户端有可能升级到websocket协议
server.set_callback_ws(Box::new(ServerWsOperate::new(servers)));
if let Err(e) = server.incoming().await {
if server.get_req_num() == 0 {
log::info!("反向代理:未处理任何请求时发生错误:{:?}", e);
} else {
if !e.is_io() {
log::info!("反向代理:处理信息时发生错误:{:?}", e);
}
}
}

ServerWsOperate中定义了服务的内部信息,及向远程websocket发送的sender,以做绑定

pub struct ServerWsOperate {
inner: InnerWsOper,
sender: Option<Sender<OwnedMessage>>,
}

在on_open的时候建立和远程websocket的双向绑定:


#[async_trait]
impl WsTrait for ServerWsOperate {
/// 握手完成后之后的回调,服务端返回了Response之后就认为握手成功
async fn on_open(&mut self, shake: WsHandshake) -> ProtResult<Option<WsOption>> {
if shake.request.is_none() {
return Err(ProtError::Extension("miss request"));
}
let mut option = WsOption::new();
if let Some(location) =
ReverseHelper::get_location_by_req(&self.inner.servers, shake.request.as_ref().unwrap())
{
if !location.is_ws {
return Err(ProtError::Extension("Not Support Ws"));
}
if let Ok((url, domain)) = location.get_reverse_url() {
println!("connect url = {}, domain = {:?}", url, domain);
let mut client = Client::builder()
.url(url)?
.connect_with_domain(&domain)
.await?; let (serv_sender, serv_receiver) = channel::<OwnedMessage>(10);
let (cli_sender, cli_receiver) = channel::<OwnedMessage>(10);
option.set_receiver(serv_receiver);
self.sender = Some(cli_sender); client.set_callback_ws(Box::new(ClientWsOperate {
sender: Some(serv_sender),
receiver: Some(cli_receiver),
})); tokio::spawn(async move {
if let Err(e) = client
.wait_ws_operate_with_req(shake.request.unwrap())
.await
{
println!("error = {:?}", e);
};
println!("client close!!!!!!!!!!");
});
}
return Ok(Some(option));
}
return Err(ProtError::Extension("miss match"));
}
}

在此地方,我们是用负载均衡来做配置location.get_reverse_url(),远程端的域名和ip要映射成本地的ip,所以这边可能要读取负载的ip而不是从dns中解析ip。

获取正确的连接域名和ip地址。

pub fn get_reverse_url(&self) -> ProtResult<(Url, String)> {
if let Some(addr) = self.get_upstream_addr() {
if let Some(r) = &self.comm.proxy_url {
let mut url = r.clone();
let domain = url.domain.clone().unwrap_or(String::new());
url.domain = Some(format!("{}", addr.ip()));
url.port = Some(addr.port());
Ok((url, domain))
} else {
let url = Url::parse(format!("http://{}/", addr).into_bytes())?;
let domain = format!("{}", addr.ip());
Ok((url, domain))
}
} else {
Err(ProtError::Extension("error"))
}
}

此处的处理方式与nginx不同,nginx是将所有升级请求的头信息全部删除,再根据配置的过行补充

Upgrade: websocket
Connection: Upgrade

所以在nginx中配置支持websocket通常如下配置,也就是通常配置的时候需要查找资料进行copy

proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

在wmproxy中并不会对客户端的请求做特殊的处理,也就是发了升级远程的websocket服务器接受了升级,我们当前协议就会升级。所以我们在配置中加入了一个字段is_ws,如果升级成websocket但是并不支持websocket的时候直接进行报错,告知不支持协议

[[http.server.location]]
rule = "/ws"
is_ws = true
reverse_proxy = "http://ws"

如此在该问该url的时候就可以转websocket了,比如websocat ws://127.0.0.1/ws

tcp转websocket

利用上章讲述的StreamToWs,且利用stream流的转发,将转发类型配置tcp2ws非安全的ws,或者tcp2wss带tls的wss,实现源码在reverse/stream.rs

[[stream.server]]
bind_addr = "0.0.0.0:85"
proxy_url = "ws://127.0.0.1:8081/"
bind_mode = "tcp2ws"

这样子,我们就可以将本地监听的85端口的地址,流量转发成8081的websocket远程地址。如果远程端验证域名可以配置上相应的domain = "wmproxy.com"

if s.bind_mode == "tcp2ws" {
let mut stream_to_ws = StreamToWs::new(inbound, format!("ws://{}", addr))?;
if domain.is_some() {
stream_to_ws.set_domain(domain.unwrap());
}
let _ = stream_to_ws.copy_bidirectional().await;
}

如此我们就可以轻松的获取tcp流量转websocket的能力。

websocket转tcp

利用上章讲述的WsToStream,且利用stream流的转发,将转发类型配置ws2tcp转发为tcp,实现源码在reverse/stream.rs

[[stream.server]]
bind_addr = "0.0.0.0:86"
up_name = "ws1"
proxy_url = "tcp://127.0.0.1:8082"
bind_mode = "ws2tcp"

这样子,我们就可以将本地监听的86端口websocket的地址,流量转发成8082的tcp远程地址。如果远程端验证域名可以配置上相应的domain = "wmproxy.com"

if s.bind_mode == "ws2tcp" {
let mut ws_to_stream = WsToStream::new(inbound, addr)?;
if domain.is_some() {
ws_to_stream.set_domain(domain.unwrap());
}
let _ = ws_to_stream.copy_bidirectional().await;
}

如此我们就可以轻松的获取websocket流量转tcp的能力。

小结

利用wmproxy可以轻松的转化tcp到websocket的流量互转,配置简单。可以利用现成的websocket高速通道辅助我们的tcp程序获取更稳定的流量通道。

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

42 干货系列从零用Rust编写负载均衡及代理,wmproxy中配置tcp转websocket的更多相关文章

  1. Docker系列-(3) Docker-compose使用与负载均衡

    上一篇文章介绍了docker镜像的制作与发布,本文主要介绍实际docker工程部署中经常用到的docker-compose工具,以及docker的网络配置和负载均衡. Docker-compose介绍 ...

  2. Apache负载均衡与Tomcat集群配置学习(Windows环境)

    本文主要参考自http://www.iteye.com/topic/985404?dhcc,经由实际操作配置操并记录而成. 由于最近的一个Java开发项目用到了Tomcat中间件作为web服务器,刚开 ...

  3. 负载均衡服务之HAProxy https配置、四层负载均衡以及访问控制

    前文我们聊了下haproxy的访问控制ACL的配置,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/12817773.html:今天我们来聊一聊haproxy的h ...

  4. 负载均衡技术在CDN中发挥着重要作用

    转载地址:http://www.qicaispace.com/gonggao/server/page01/info07.asp CDN是一个经策略性部署的整体系统,能够帮助用户解决分布式存储.负载均衡 ...

  5. Nginx服务器部署 负载均衡 反向代理

    Nginx服务器部署负载均衡反向代理 LVS Nginx HAProxy的优缺点 三种负载均衡器的优缺点说明如下: LVS的优点: 1.抗负载能力强.工作在第4层仅作分发之用,没有流量的产生,这个特点 ...

  6. [架构]辨析: 高可用 | 集群 | 主从 | 负载均衡 | 反向代理 | 中间件 | 微服务 | 容器 | 云原生 | DevOps | ...

    词汇集 灾备 冷备份 双机热备份 异地容灾备份 云备份 灾难演练 磁盘阵列(RAID) 故障切换 心跳监测 高可用 集群 主从复制(Master-Slave) 多集群横向扩容(master-clust ...

  7. 死磕nginx系列--使用upsync模块实现负载均衡

    问题描述 nginx reload是有一定损耗的,如果你使用的是长连接的话,那么当reload nginx时长连接所有的worker进程会进行优雅退出,并当该worker进程上的所有连接都释放时,进程 ...

  8. hbase源码系列(一)Balancer 负载均衡

    看源码很久了,终于开始动手写博客了,为什么是先写负载均衡呢,因为一个室友入职新公司了,然后他们遇到这方面的问题,某些机器的硬盘使用明显比别的机器要多,每次用hadoop做完负载均衡,很快又变回来了. ...

  9. 架构之Nginx(负载均衡/反向代理)

    Nginx ("engine x") 是一个高性能的 HTTP 和 反向代理 服务器 ,也是一个 IMAP/POP3/SMTP 代理 服务器 . Nginx 是由 Igor Sys ...

  10. nginx+php负载均衡集群环境中的session共享方案梳理

    在网站使用nginx+php做负载均衡情况下,同一个IP访问同一个页面会被分配到不同的服务器上,如果session不同步的话,就会出现很多问题,比如说最常见的登录状态. 下面罗列几种nginx负载均衡 ...

随机推荐

  1. CPF C#跨平台UI框架开源了

    介绍 C#跨平台UI框架 提供NETStandard2.0和net4的库,通过Netcore可以跨平台,支持Windows.Mac.Linux,Net4的可以支持XP. 各个平台运行效果一致,不依赖系 ...

  2. 🔥🔥Java开发者的Python快速进修指南:面向对象进阶

    在上一期中,我们对Python中的对象声明进行了初步介绍.这一期,我们将深入探讨对象继承.组合以及多态这三个核心概念.不过,这里不打算赘述太多理论,因为我们都知道,Python与Java在这些方面的主 ...

  3. Netty源码学习6——netty编码解码器&粘包半包问题的解决

    系列文章目录和关于我 零丶引入 经过<Netty源码学习4--服务端是处理新连接的&netty的reactor模式和<Netty源码学习5--服务端是如何读取数据的>的学习, ...

  4. 必知必会Java

    你好,我是阿光. 最近想着把工作中使用过的java命令都梳理一下,方便日后查阅.虽然这类文章很多,但自己梳理总结后,还是会有一些新的收获.这也是这篇笔记的由来. 今天先聊聊 jps 命令. 命令概述 ...

  5. idea常用快捷键使用

    idea常用快捷键使用:1.shift+u 大小写2.alt+shift+u 驼峰命名(插件:CamelCase)3.ctrl+alt 点击跳转实现类4.ctrl 点击跳转接口类5.Alt+F7 查看 ...

  6. 关于C#反射概念,附带案例!

    反射 C#中的反射是一种使程序在运行时能够动态地获取类型信息并调用其成员的技术.通过反射,程序可以在运行时进行类型的动态加载.创建对象.调用方法和属性,以及访问和修改字段等.反射可以使程序更加灵活,但 ...

  7. cocos2d-Js 各类碰撞检测

    这里总结一下点.圆.矩形之间的简单碰撞检测算法(矩形不包括旋转状态) 点和圆的碰撞检测: 1.计算点和圆心的距离 2.判断点与圆心的距离是否小于圆的半径 isCollision: function(p ...

  8. AI助力软件工程师高效工作:8款神器助你优化工作流程

    随着人工智能技术的不断发展,AI工具在软件工程领域展现出强大的应用潜力.善用 AI 工具可以消除繁琐事务带来的倦怠,帮助软件工程师更好地传达想法,完成更高质量的工作.我们可以将 AI 以各种方式应用于 ...

  9. 解密数据可视化软件、BI软件和数字孪生软件的不同

    在现代企业和科技领域,数据起着至关重要的作用.为了更好地管理和理解数据,不同类型的软件工具应运而生,其中包括数据可视化软件.BI(Business Intelligence)软件和数字孪生软件.虽然它 ...

  10. pytest框架学习-pytest_addoption钩子函数

    适用场景:一套自动化代码,多套环境. pytest_addoption 允许用户自定义注册一个命令行参数,方便用户通过命令行参数的形式给 pytest 传递不同的参数进行不同测试场景的切换. pyte ...