基于 HTTP/2 的 WEB 內網穿透實現

jopen 9年前發布 | 14K 次閱讀 HTTP

HTTP/2 引入了二進制分幀層,將 HTTP/1.1 中的請求和響應拆成顆粒度更細的幀(frame),從而實現了優先級、流量控制和 Server Push 等功能;HTTP/2 在單條 TCP 連接上可以打開多個流,從而實現了多路復用;HTTP/2 使用靜態字典、動態字典以及哈夫曼編碼,對請求 / 響應頭部進行壓縮。總之,HTTP/2 從協議層面解決了 HTTP/1.1 的諸多問題。

在我之前寫的《搭建 ngrok 服務實現內網穿透》這篇文章里,我介紹了如何通過 ngrok 讓內網 WEB 在其它網絡環境中能夠被訪問。本文要實現的服務與 ngrok 類似,我把它稱之為 Pangolin,中文是穿山甲的意思(名字來自于同事的類似項目,在此表示感謝)。Pangolin 客戶端和服務端之間的報文轉發,是用 node-http2 這個 Node.js 模塊提供的 HTTP/2 服務來實現的。

Pangolin 的需求來自于本博客用戶評論(via)。 實際上,能實現類似功能的軟件很多,有使用私有協議進行轉發的,有使用 WebSocket 進行轉發的。而我認為 HTTP/2 應該是個不錯的選擇,打算試一下。最終我花了一個小時實現了一個初步能用的版本,除開 node-http2,全部代碼不超過 200 行。代碼我放在了 github 上,有興趣的同學可以玩一下。

下面簡單介紹它的原理,我畫了一張草圖:

最左側是最終用來訪問服務的瀏覽器,它可能位于公網,也可能位于其它內網;最右側是實際提供 WEB 服務的 HTTP Server,它位于內網。顯然,左側瀏覽器沒辦法直接訪問右側 WEB 服務,只能借助公網節點作為橋梁。中間的 pangolin 服務端運行在公網節點上;Pangolin 客戶端運行在與 WEB 服務同臺機器或者同一網段內。

瀏覽器發起請求后,請求報文沿著綠色箭頭從左到右流動,每個節點都相當于左側相鄰節點的 HTTP Server。唯一的問題出現在 Pangolin 服務端和客戶端之間:客戶端位于內網,正常情況下 Pangolin 服務端連不上客戶端提供的 HTTP Server。

這個問題我用了一個取巧的辦法解決:由于 Pangolin 服務端有公網 IP,可以開啟 TCP Server,客戶端可以通過 IP 和約定的端口與服務端建立 TCP 連接。那么只要稍微改造一下 node-http2 的代碼,使它可以基于指定 socket 創建 HTTP/2 Server、發送 HTTP/2 Request,就可以打通所有節點了。這個問題解決后,左側的請求可以順利到達右側,響應數據也可以沿著之前的連接逐級返回。

Pangolin 服務端和客戶端內部之間使用 HTTP/2,可以大幅提高性能,降低程序復雜性;對外使用 HTTP/1.1,保證了與已有系統的兼容性。

為了實現內網穿透,Pangolin 需要做以下準備工作:

  • Pangolin 服務端開啟 TCP Server;
  • Pangolin 客戶端啟動 TCP Client,與 Pangolin 服務端連接,得到 socket 長連接;
  • Pangolin 客戶端基于這個 socket 連接,開啟 HTTP/2 Server;
  • Pangolin 服務端開啟 HTTP/1.1 Server,等待瀏覽器來訪問;

實際的數據傳輸流程如下:

  • 瀏覽器向 Pangolin 服務端發起請求(HTTP/1.1);
  • Pangolin 服務端基于已有 socket,向 Pangolin 客戶端發起請求(HTTP/2);
  • Pangolin 客戶端向內網 WEB 服務發起請求,得到響應(HTTP/1.1);
  • Pangolin 客戶端基于已有 socket,將響應返回給 Pangolin 服務端(HTTP/2);
  • Pangolin 服務端將響應返回給瀏覽器(HTTP/1.1);

由于 Pangolin 客戶端采用了 HTTP 轉發,而不是 TCP 隧道,所以可以輕松實現 ngrok 那樣的管理界面,用來查看完整的 Request/Response 信息。目前我還只是簡單地打印了請求日志。

HTTP/2 協議本身并沒有規定它必須基于 TLS 部署,沒有安全層的 HTTP/2 被稱之為 h2c(HTTP/2 Cleartext)。目前來看,所有瀏覽器都不打算支持 h2c。但如果一個系統的某些環節對安全沒有那么高的要求,或者已經通過了其它方案確保了安全,部署 h2c 也是一個非常好的選擇。現在很多 HTTP/2 工具和類庫同時支持 h2 和 h2c,node-http2 也是如此。

實際上,我為了測試方便,在實現 pangolin 時也選擇了 h2c。通過 Wireshark 抓包可以看出,HTTP/2 層下面直接就是 TCP 層:

好了,本文就討論這么多內容,大家有什么問題或想法歡迎給我留言。

本文鏈接:https://imququ.com/post/tunnel-to-localhost-base-on-http2.html 

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!