wmproxy wmproxy已用Rust實現http/https代理, socks5代理, 反向代理, 靜態文件伺服器,四層TCP/UDP轉發,七層負載均衡,內網穿透,後續將實現websocket代理等,會將實現過程分享出來,感興趣的可以一起造個輪子 項目地址 國內: https://gitee. ...
wmproxy
wmproxy
已用Rust
實現http/https
代理, socks5
代理, 反向代理, 靜態文件伺服器,四層TCP/UDP轉發,七層負載均衡,內網穿透,後續將實現websocket
代理等,會將實現過程分享出來,感興趣的可以一起造個輪子
項目地址
國內: https://gitee.com/tickbh/wmproxy
github: https://github.com/tickbh/wmproxy
旅程路線
大家好,我是這趟旅程的導游,可以叫我導游
,我為大家來介紹HTTP的組裝之旅。
大家好,我是這趟旅程的乘客,可以叫我小H
,出來玩實在太開心了
旅行開端
首先導游帶我來了碼頭,說是我接一位神秘的來客
我問:“這個神秘的來客有什麼來頭?”
導游答:“這個神秘的來客,名字叫socket
,這碼頭能正常的運營有一半的功勞是他的存在,等下他來了我們要趕緊麻溜的接待。”
只見遠處來了一艘輪船正在緩緩的駛來,他看起來好壯觀。
導游這時候急忙的說:“他馬上就停靠進來了,這邊碼頭的工人馬上就會將他的貨卸下了,去晚了就看不到全過程了。”
我們就立即來到了停靠點,只見有一套完善的流程正在對接著這一切,我想這是這碼頭能繁榮的原因吧,高效!
socket說:“這些東西是http
大神要的東西,你們把它們按照指定的順序轉交給他,他會安排如何處理的!”
旅行中轉
接下來運送集裝箱的過程又會碰到什麼有趣的事呢?
我們跟隨著運送車隊前行,只見http
大神安排了查驗數據,但奇怪的是他只檢查前面的一輛車,後面的就數一數就放行了。
於是我就好奇的問:“為什麼前面一輛車要檢查,後面的車就數一數都不用拿出來檢查?”
http大神就笑著說:“我檢查前面的車,因為我不知道這些車裝了什麼東西,第一輛車帶有車隊的信息,我就通過他來知道這趟車的內容是什麼了。下麵我跟你說下這輛車帶的信息。”
POST /report HTTP/1.1\r\n
Content-Type: application/x-www-form-urlencoded; charset=UTF-8\r\n
Accept: */*\r\n
Accept-Language: zh-CN,zh-Hans;q=0.9\r\n
Accept-Encoding: gzip, deflate, br\r\n
Host: www.wm-proxy.com\r\n
Origin: https://www.wm-proxy.com\r\n
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.1 Safari/605.1.15\r\n
Referer: https://www.wm-proxy.com/\r\n
Content-Length: 218\r\n
Connection: keep-alive\r\n
\r\n
通過這第一輛車的信息,我就知道了他要找的是叫/report
的,他用POST
的方法去敲他家的門,他可以接受Accept-Language: zh-CN,zh-Hans;q=0.9
這種格式的語言,接受Accept-Encoding: gzip, deflate, br
這三種格式的壓縮,及後面還帶著218
的數據,在他訪問結束的時候不要立刻關閉Connection: keep-alive
他可能還有話沒說完,叫我再等等,然後通過\r\n\r\n
表示他沒有其它信息了。
我驚嘆的答到:“原來這車上隱藏了這麼多的信息吖,那接下來他們會遇到什麼?”
http大神說:“我現在知道了他們的全部信息了,我現在會把這些車隊組裝成Request<RecvStream>
的結構體,等下你看到那邊的一扇扇門了沒有,那個叫中間件
。”
我來到這門前:“這怎麼有兩個門呢?他們等下走哪個門呢?”
http大神說:“他們這些門,都是有統一的規格的,他們需要按照我的規格來建這些門,左邊是進的process_request
,右邊是出的process_response
,要嚴格的按照這流程來,要不然會出錯哦。下麵我給你說下他這個門有哪些註意的”
#[async_trait]
pub trait Middleware: Send + Sync {
async fn process_request(&mut self, request: &mut RecvRequest) -> ProtResult<()>;
async fn process_response(&mut self, request: &mut RecvRequest, response: &mut RecvResponse) -> ProtResult<()>;
}
他們按照這規格建起各種各樣的門,他們可以規定進門後要怎麼處理我組裝的這個車隊RecvRequest
,比如要把車隊變成紅色之類等操作或者記錄下有多少個車隊經過等,這樣我就可以不會顯得臃腫,但是我的能力卻是大大的提升了起來。
我表示羡慕到:“原來這種方式強大可以變得這麼優雅。那通過這些門後會有什麼好玩的?”
旅程終點
過了
中間件
的門,我們就來到了這旅程的終點。我們來到了一個大廣場,廣場上有個神奇的裝置,看起來像傳送門。
我就很好奇的問:“這傳送門看起來的有點高級,這邊角好像和剛纔那門有點像,難道也是要遵循你的規則?”
http大神回答:“觀察力不錯哦,這傳送門也是同樣的道理哦,因為我也不知道他們要把這個數據拿來乾什麼,所以我就弄了個傳送門,讓他們把數據加工好後再給我咯,我的要求就是建傳送門必須要按以下格式”
#[async_trait]
pub trait OperateTrait {
async fn operate(&mut self, req: &mut RecvRequest) -> ProtResult<RecvResponse>;
}
“他們必須返回給我一個RecvResponse
或者一個錯誤,我等下好告訴socket
要運送哪些東西回去。你看,從傳送門裡出來了一個對象Ok(RecvResponse)
,那我們接下來把這個對象帶回去。這對象類似這樣子的內容:”
HTTP/1.1 200 OK\r\n
Content-Encoding: deflate\r\n
Date: Wed, 22 Nov 2023 11:30:23 GMT\r\n
Content-Type: application/json;charset=UTF-8\r\n
Content-Length: 31\r\n
Connection: keep-alive\r\n
Access-Control-Allow-Origin: *\r\n
\r\n
旅程返程
得到了正在的返回結果,我們就馬不停蹄的往回走了
我們又回到了中間件
的門,但是此時是從另一側的門回來,上面標註著process_response
,我想這應該是處理我們剛剛從傳送門回來的對象吧,這也是邏輯嚴密的結構。
async fn process_response(&mut self, request: &mut RecvRequest, response: &mut RecvResponse) -> ProtResult<()>;
經過了中間件
的門,對象Ok(RecvResponse)
被重新粉刷了藍色,也做了標記,我們就把他帶到了socket
的面前。
socket說:“你們回來的真快,這才過去了不到10ms,把我要的東西帶過來了嗎?”
http大神說:“都在這了,順序不要亂了哦,他們可是被暗中標記過了,亂了的話,等下就還原不回來了。”
於是socket就把這些數據帶回去了,看著這船來船往的碼頭,我就感慨到,原來他們都已經預處理好各種情況的應對了,難怪看起來那麼的井井有條。只見HTTP大神給了例子:
use std::{env, error::Error, time::Duration};
use async_trait::async_trait;
use tokio::{net::TcpListener};
use webparse::{Response};
use wenmeng::{self, ProtResult, Server, RecvRequest, RecvResponse, OperateTrait, Middleware};
struct Operate;
#[async_trait]
impl OperateTrait for Operate {
async fn operate(&mut self, req: &mut RecvRequest) -> ProtResult<RecvResponse> {
tokio::time::sleep(Duration::new(1, 1)).await;
let response = Response::builder()
.version(req.version().clone())
.body("Hello World\r\n".to_string())?;
Ok(response.into_type())
}
}
struct HelloMiddleware;
#[async_trait]
impl Middleware for HelloMiddleware {
async fn process_request(&mut self, request: &mut RecvRequest) -> ProtResult<()> {
println!("hello request {}", request.url());
Ok(())
}
async fn process_response(&mut self, _request: &mut RecvRequest, response: &mut RecvResponse) -> ProtResult<()> {
println!("hello response {}", response.status());
Ok(())
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
env_logger::init();
let addr = env::args()
.nth(1)
.unwrap_or_else(|| "0.0.0.0:8080".to_string());
let server = TcpListener::bind(&addr).await?;
println!("Listening on: {}", addr);
loop {
let (stream, addr) = server.accept().await?;
tokio::spawn(async move {
let mut server = Server::new(stream, Some(addr));
server.middle(HelloMiddleware);
let operate = Operate;
let e = server.incoming(operate).await;
println!("close server ==== addr = {:?} e = {:?}", addr, e);
});
}
}
小結
在http的處理過程中,還有其它的好玩的東西,需要註意的東西,這次我們介紹了中間件及回調處理的小小見聞。以下是一個小demo
點擊 [關註],[在看],[點贊] 是對作者最大的支持