1. 程式人生 > >基於 HTTP/2 的 WEB 內網穿透實現

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

提醒:本文最後更新於 1094 天前,文中所描述的資訊可能已發生改變,請謹慎使用。

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 上,有興趣的同學可以玩一下。

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

pangolin-dataflow

最左側是最終用來訪問服務的瀏覽器,它可能位於公網,也可能位於其它內網;最右側是實際提供 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 層:

wireshark-h2c

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

--EOF--

提醒:本文最後更新於 1094 天前,文中所描述的資訊可能已發生改變,請謹慎使用。