<p id="m2nkj"><option id="m2nkj"><big id="m2nkj"></big></option></p>
    <strong id="m2nkj"></strong>
    <ruby id="m2nkj"></ruby>

    <var id="m2nkj"></var>
  • 基于 Node.js 實現(xiàn)的跨平臺 Web 抓包調(diào)試工具 Whistle 原理分析

    共 5977字,需瀏覽 12分鐘

     ·

    2022-01-10 05:47

    大廠技術(shù)??高級前端??Node進階

    點擊上方?程序員成長指北,關注公眾號

    回復1,加入高級Node交流群

    導語?通過這篇文章可以大致了解 Whistle 的實現(xiàn)原理,并學習如何實現(xiàn)一個簡單的抓包調(diào)試工具。

    項目 Github 地址:https://github.com/avwo/whistle?

    Whistle?是基于 Node.js 實現(xiàn)的跨平臺 Web 抓包調(diào)試(HTTP)代理,主要功能:

    1. 實時抓包:支持 HTTP、HTTPS、HTTP2、WebSocket、TCP 等常見 Web 請求的抓包;

    2. 修改請求響應:與一般抓包調(diào)試工具采用斷點的方式不同,Whistle 采用類似系統(tǒng) host 的配置規(guī)則方式;

    3. 擴展功能:支持通過 Node 編寫插件,或作為獨立 NPM 包引入項目兩種擴展方式。

    本文將從最基本的概念開始逐步講解 Whistle 功能,包含以下內(nèi)容:

    1. 什么是 HTTP 代理

    2. 實現(xiàn)簡單 HTTP 代理

    3. 完整 HTTP 代理架構(gòu)(Whistle)

    4. 具體實現(xiàn)原理

    5. 參考資料

    1. 什么是 HTTP 代理

    代理是客戶端到服務端的中轉(zhuǎn)服務,其中:

    1. 不經(jīng)過代理的請求:客戶端和服務端直接建立連接后,即可開始交換數(shù)據(jù)。

    2. 經(jīng)過代理的請求:客戶端不與服務端直接建立連接,而是先跟代理建立連接后,將目標服務器的地址發(fā)送給代理,通過代理再跟服務端建立連接,這里如果代理服務為 HTTP Server,則稱為 HTTP 代理。

    接下來看下客戶端如何將目標服務器地址傳給 HTTP 代理,以及 HTTP 代理如何跟目標服務器建立連接。

    2. 實現(xiàn)簡單 HTTP 代理

    先看一個用 Node.js 實現(xiàn)的最簡單 HTTP 代理:

    const http = require('http');
    const { connect } = require('net');

    /****************** 工具方法 ******************/
    const getHostPort = (host, defaultPort) => {
    let port = defaultPort || 80;
    const index = host.indexOf(':');
    if (index !== -1) {
    port = host.substring(index + 1);
    host = host.substring(0, index);
    }
    return {host, port};
    };

    const getOptions = (req, defaultPort) => {
    // 這里假定 host 一定存在,完整實現(xiàn)參見 Whistle
    const { host, port } = getHostPort(req.headers.host, defaultPort);
    return {
    hostname: host, // 指定請求域名,用于通過 DNS 獲取服務器 IP 及設置請求頭 host 字段
    port, // 指定服務器端口
    path: req.url || '/',
    method: req.method,
    headers: req.headers,
    rejectUnauthorized: false, // 給 HTTPS 請求用的,HTTP 請求會自動忽略
    };
    };

    // 簡單處理,出錯直接斷開,完整實現(xiàn)邏輯參考 Whistle
    const handleClose = (req, res) => {
    const destroy = (err) => { // 及時關閉無用的連接,防止內(nèi)存泄露
    req.destroy();
    res && res.destroy();
    };
    res && res.on('error', destroy);
    req.on('error', destroy);
    req.once('close', destroy);
    };


    /****************** 服務代碼 ******************/
    const server = http.createServer();
    // 處理 HTTP 請求
    server.on('request', (req, res) => {
    // 與服務端建立連接,透傳客戶端請求及服務端響應內(nèi)容
    const client = http.request(getOptions(req), (svrRes) => {
    res.writeHead(svrRes.statusCode, svrRes.headers);
    svrRes.pipe(res);
    });
    req.pipe(client);
    handleClose(res, client);
    });

    // 隧道代理:處理 HTTPS、HTTP2、WebSocket、TCP 等請求
    server.on('connect', (req, socket) => {
    // 與服務端建立連接,透傳客戶端請求及服務端響應內(nèi)容
    const client = connect(getHostPort(req.url), () => {
    socket.write('HTTP/1.1 200 Connection Established\r\n\r\n');
    socket.pipe(client).pipe(socket);
    });
    handleClose(socket, client);
    });

    server.listen(8080);

    上述代碼實現(xiàn)了一個具有轉(zhuǎn)發(fā)請求功能的 HTTP 代理,從代碼可知 HTTP 代理就是一個普通的 HTTP Server,并監(jiān)聽?request?和?connect?這兩個事件,客戶端會通過這兩個事件將目標服務器地址傳過來,其中:

    1. request:一般普通 HTTP 會通過該事件將目標服務器地址傳過來。

    2. connect:一般非 HTTP 請求,如 HTTPS、HTTP/2、WebSocket、TCP 等會通過該事件將目標服務器地址傳過來,觸發(fā)該事件的代理請求也叫隧道代理。

    可以在事件里面的?req.url?或?req.headers.host?獲取目標服務器的地址(host:port),再跟該服務器地址建立連接并將結(jié)果通過 HTTP 響應的方式返回給客戶端,這里只是實現(xiàn)代理的最基本功能,完整的 HTTP 除了請求轉(zhuǎn)發(fā),至少應該還有:

    1. 查看實時抓包;

    2. 解析 HTTPS 請求;

    3. 修改請求響應內(nèi)容;

    4. 擴展功能。

    下面以 Whistle 為例看下如何用 Node.js 實現(xiàn)一個完整的 HTTP 代理。

    3. 完整 HTTP 代理架構(gòu)(Whistle)

    主要分五個模塊:

    1. 請求接入模塊

    2. 隧道代理模塊

    3. 處理 HTTP 請求模塊

    4. 規(guī)則管理模塊

    5. 插件管理模塊

    4. 具體實現(xiàn)原理

    下面分別看下這五個模塊具體是怎么實現(xiàn)的。

    4.1 請求接入模塊

    所有請求先會經(jīng)過請求接入模塊,Whistle 支持四種請求接入方式:

    1. HTTP & HTTPS 直接請求:相當于配 hosts 或 DNS 的方式,將請求轉(zhuǎn)發(fā)到 Whistle;

    2. HTTP 代理:Whistle 默認接入方式,即配系統(tǒng)代理或通過瀏覽器插件配 HTTP 代理的方式;

    3. HTTPS 代理:在 HTTP 代理之上對代理請求進行了加密,即 HTTPS Server,可以通過指定證書轉(zhuǎn)成 HTTP 代理請求;

    4. Socks5 代理:利用 npm 包?socksv5?轉(zhuǎn)成普通的 TCP 請求,并將 TCP 請求轉(zhuǎn)成隧道代理請求。

    基實現(xiàn)原理是:將所有請求都轉(zhuǎn)成 HTTP 代理的?隧道代理請求?或?HTTP 請求,再解析?隧道代理請求?轉(zhuǎn)成 HTTP 請求。

    如何將普通 tcp 請求轉(zhuǎn)成隧道代理請求參見:?lack-proxy

    下面看下如何從?隧道代理請求?解析出 HTTP 請求。

    4.2 隧道代理模塊

    關鍵點(HTTP 請求也可以走隧道代理):

    1. 通過匹配的全局規(guī)則判斷是否要解析隧道代理請求,如果不解析,則當成普通 TCP 請求處理;

    2. 如果需要,則通過?socket.once('data', handler)?讀取請求點第一幀數(shù)據(jù);

    3. 將第一幀數(shù)據(jù)轉(zhuǎn)成字符串,通過正則?/^(\w+)\s+(\S+)\s+HTTP\/1.\d$/mi?是否是 HTTP 請求?如果是 HTTP 請求,再判斷下是否是?CONNECT?請求,即隧道代理請求(隧道代理請求也可以代理隧道代理請求),如果是,則轉(zhuǎn)回隧道代理方法處理,如果不是,則轉(zhuǎn)到?HTTP 請求模塊處理;

    4. 如果不是 HTTP 請求,則當成 HTTPS 請求處理,這里需要用到中間人的方式將 HTTPS 請求轉(zhuǎn)成 HTTP 請求;

    5. Whistle 會先按以下順序獲取請求證書:

      • 通過匹配的插件獲?。梢酝ㄟ^規(guī)則?sniCallback://plugin?指定加載證書的插件);

      • 通過啟動參數(shù)?-z certDir?指定目錄或?~/.WhistleAppData/custom_certs?加載的自定義證書;

      • 如果沒有上述兩種自動證書,Whistle 會自動生成一個默認的證書。

    6. 獲取到證書后,再利用該證書啟動一個 HTTPS Server,將 HTTPS 請求轉(zhuǎn)成 HTTP 請求交給?HTTP 請求模塊處理。

    4.3 HTTP 請求處理模塊

    HTTP 請求處理可以分兩個階段:

    1. 請求階段:

      • 匹配全局規(guī)則;

      • 如果規(guī)則里類似 whistle.xxx 的規(guī)則,執(zhí)行對應插件鉤子,獲取插件規(guī)則并跟匹配的全局規(guī)則合并;

      • 執(zhí)行規(guī)則、記錄狀態(tài)并請求到指定服務。

    2. 響應階段:

      • 執(zhí)行匹配插件的鉤子,獲取插件規(guī)則并跟匹配的全局規(guī)則合并;

      • 執(zhí)行規(guī)則、記錄狀態(tài)并請求返回客戶端。

    4.4 規(guī)則管理

    與傳統(tǒng)抓包調(diào)試代理 采用斷點修改請求響應數(shù)據(jù)不同,Whistle 采用配置規(guī)則的方式修改請求響應,采用配置方式的好處是操作簡單,且可以將操作持久化存儲及共享給他人,先看幾個例子:

    Whistle 的規(guī)則管理主要兩個功能:

    1. 解析規(guī)則

    2. 匹配規(guī)則

    解析規(guī)則

    Whistle 有兩類規(guī)則:

    1. 全局規(guī)則(公共規(guī)則),所有請求都會嘗試匹配的規(guī)則,由以下規(guī)則組成:

    2. 插件規(guī)則(私有規(guī)則),即進入插件的請求(匹配的全局規(guī)則里有 whistle.xxx 協(xié)議)才會匹配到的規(guī)則,由以下規(guī)則組成:

      文檔:https://wproxy.org/whistle/plugins.html

      • 插件 reqRulesServer 等 hooks 動態(tài)返回;

      • 插件根目錄 _rules.txt 等文件配置的靜態(tài)規(guī)則;

    匹配規(guī)則

    Whistle 規(guī)則的完整結(jié)構(gòu)為:

    文檔:https://wproxy.org/whistle/mode.html

    4.5 插件管理

    Whistle 插件的功能很多,不僅具備 Node 的所有能力,且可以操作 Whistle 的所有規(guī)則(理論上可以基于插件實現(xiàn)一個 Whistle),主要用來做以下事情:

    1. 鑒權(quán)功能

    2. 提供 UI 交互界面

    3. 作為請求 Server(直接響應或轉(zhuǎn)發(fā)并修改請求響應)

    4. 統(tǒng)計請求信息(查看上報 / 打點數(shù)據(jù)等)

    5. 設置規(guī)則(動態(tài),靜態(tài),全局及私有規(guī)則)

    6. 獲取抓包數(shù)據(jù)

    7. 編解碼請求響應數(shù)據(jù)流(pipe stream 功能)

    8. 擴展界面右鍵菜單(如:分享抓包數(shù)據(jù))

    9. 保存并同步 Rules & Values 數(shù)據(jù)

    10. 自定義 HTTPS 請求的證書

    比如:

    1. whistle.script:實現(xiàn)通過自定義腳本動態(tài)設置規(guī)則

    2. whistle.vase:提供靈活強大的 mock 能力

    3. whistle.inspect:方便快速注入 vConsole、eruda 等頁面調(diào)試工具

    4. whistle.sni-callback:自定義證書插件

    其它插件例子參見:https://github.com/whistle-plugins

    Whistle 是如何實現(xiàn)插件功能?主要遵循以下三個設計原則:

    1. 完備性:

      確保所有功能點都可擴展,如:請求鑒權(quán)、生成證書、獲取抓包、設置規(guī)則、請求處理等。

    2. 穩(wěn)定性:

      插件內(nèi)部異常不影響其它功能,Whistle 的每個插件獨立進程,插件與 Whistle 之間通過 HTTP 協(xié)議交互。

      Whistle 是使用 npm 包?pfork?來啟動插件進程,進程間的交換是直接通過 Node 的 http 模塊實現(xiàn)的),方便開發(fā)者利用 http 的生態(tài)開發(fā)插件。

    3. 易用性:

      方便用戶開發(fā)及使用。

    4. 開發(fā):結(jié)構(gòu)簡單 (npm 包) + 腳手架?lack

      使用:安裝 npm 包即可,用法跟內(nèi)置協(xié)議一樣,且可內(nèi)置交互界面。

    有關插件的更多細節(jié)參見:https://wproxy.org/whistle/plugins.html

    事實上,Whistle 除了支持插件擴展,還可以同時作為獨立模塊引入項目使用;除了本地開發(fā)使用,也可以基于 Whistle 開發(fā)出支持多人使用的開發(fā)聯(lián)調(diào)協(xié)作工具,比如后面會給大家介紹其實現(xiàn)原理的:

    1. 基于 Whistle 實現(xiàn)的多人多環(huán)境遠程抓包調(diào)試工具。

      Nohost:https://github.com/Tencent/nohost

    2. 基于 Whistle 和 Nohost 實現(xiàn)的分布式遠程抓包調(diào)試工具 TDE 等等。

      TDE 目前只在騰訊內(nèi)部使用,后續(xù)后逐步對外開源。

    5. 參考資料

    1. Github 倉庫:https://github.com/avwo/whistle

    2. 官方插件倉庫:https://github.com/whistle-plugins

    3. 詳細文檔:https://wproxy.org/whistle/

    Node 社群


    我組建了一個氛圍特別好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你對Node.js學習感興趣的話(后續(xù)有計劃也可以),我們可以一起進行Node.js相關的交流、學習、共建。下方加 考拉 好友回復「Node」即可。


    ???“分享、點贊、在看” 支持一波??


    瀏覽 83
    點贊
    評論
    收藏
    分享

    手機掃一掃分享

    分享
    舉報
    評論
    圖片
    表情
    推薦
    點贊
    評論
    收藏
    分享

    手機掃一掃分享

    分享
    舉報
    <p id="m2nkj"><option id="m2nkj"><big id="m2nkj"></big></option></p>
    <strong id="m2nkj"></strong>
    <ruby id="m2nkj"></ruby>

    <var id="m2nkj"></var>
  • 三级三级久久三级 | 精品在线视频播放 | 激情乱伦五月天 | 俺去也在线视频 | 亚洲黄色在线看 | 无套内射学生妹去看片 | 欧美日本一级二级三级 | 久久久久久日产精品 | 丁香婷婷色视频 | 天堂资源最新在线 |