30. 乾貨系列從零用Rust編寫正反向代理,HTTP的組裝之旅(中間件)

来源:https://www.cnblogs.com/wmproxy/archive/2023/11/24/wmproxy30.html
-Advertisement-
Play Games

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

點擊 [關註][在看][點贊] 是對作者最大的支持


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 公眾號「架構成長指南」,專註於生產實踐、雲原生、分散式系統、大數據技術分享。 資料庫和Redis如何保持強一致性,這篇文章告訴你 目的 Redis和Msql來保持數據同步,並且強一致,以此來提高對應介面的響應速度,剛開始考慮是用mybatis的二級緩存,發現坑不少,於是決定自己搞 要關註的問題點 操 ...
  • 十六、C++字元串(一) 1、原生字元串實現將兩個字元串拼接 //原生字元串實現將兩個字元串拼接 #include <iostream> #include <locale> int main() { char strA[0x10] = "123"; //定義字元串 char strB[0x10] = ...
  • ReentrantReadWriteLock讀寫鎖 樂觀鎖和悲觀鎖 樂觀鎖 樂觀鎖,就是給需要共用的數據,添加一個版本號version,例如1,每次有線程更新共用數據後,version+1,每次線程進行數據更新時,要比較當前線程持有的數據的版本號,相等則修改,不相等則不修改,支持併發訪問。 悲觀鎖 ...
  • 哈嘍大家好,我是鹹魚 接觸過 Python 的小伙伴應該對【字典】這一數據類型都瞭解吧 雖然 Python 沒有顯式名稱為“哈希表”的內置數據結構,但是字典是哈希表實現的數據結構 在 Python 中,字典的鍵(key)被哈希,哈希值決定了鍵對應的值(value)在字典底層數據存儲中的位置 那麼今天 ...
  • 0 大綱 [Apache Flink]2017年12月發佈的1.4.0版本開始,為流計算引入里程碑特性:TwoPhaseCommitSinkFunction。它提取了兩階段提交協議的通用邏輯,使得通過Flink來構建端到端的Exactly-Once程式成為可能。同時支持: 數據源(source) 和 ...
  • 一眨眼明天就周末了,一周過的真快! 今天咱們用Python來實現一下動態網頁數據的抓取 最近不是有消息說世界首富馬上要變成中國人了嗎,這要真成了,可就是歷史上首位中國世界首富了! 那我們就以富豪排行榜為例,爬取一下2023年國內富豪五百強,最後實現一下可視化分析。 準備工作 環境使用 Python ...
  • 來源:levelup.gitconnected.com/how-to-design-a-system-to-scale-to-your-first-100-million-users-4450a2f9703d 1 從頭開始 2 可擴展性的藝術 3 使用負載均衡器來均衡所有節點上的流量 4 擴展關係數 ...
  • 因PDF文檔具有較好的穩定性和相容性,現在越來越多的合同、研究論文、報告等都採用PDF格式。為了進一步保護這些重要文檔內容免受未經授權的複製或使用,我們可以添加水印以表明其狀態、所有權或用途。針對工作中可能出現的在 C++ 應用程式中給 PDF 文檔添加文字水印的需求,本文將詳細展示如何通過第三方國 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...