iT邦幫忙

2024 iThome 鐵人賽

DAY 16
2
Software Development

微服務奇兵:30天Quarkus特訓營系列 第 16

後端協定基礎知識建置-HTTPS 版本差異

  • 分享至 

  • xImage
  •  

在前面的介紹中,相信你已經對HTTPS有了一定的了解,並掌握了它在資料加密和傳輸安全上的重要性。然而,除了安全性之外,還有一個經常被忽略的關鍵領域——HTTP版本的演進與其差異

很多人可能認為HTTP只是一個簡單的通訊協定,不需要特別關注它的細節。但事實上,從HTTP/1.0HTTP/3的演進,這些技術革新提升了網頁的效能、穩定性以及用戶體驗。尤其是對於開發者來說,理解這些版本之間的差異,能夠幫助我們更好地優化網站效能。

版本基本上我們跳過HTTP0.9,因為它為 HTTP 協定的最早版本,於 1991 年推出。與後來的版本相比,功能非常有限,僅支持GET請求,且僅能傳輸純文字(沒有圖片、CSS 或 JavaScript 等檔案)。

TCP/IP簡述

在進入版本差異介紹之前,稍微簡單提一下TCP/IP,縮寫為Transmission Control Protocol(TCP) 及Internet Protocol (IP) 。它為電腦之間溝通的「通訊語言」

你可以把TCP/IP想像成郵件系統中的「郵差」和「信封」:

  • IP(Internet Protocol,網際協定) 就像是「信封」,它決定信件要送到哪裡,也就是把每個數據包(像是我們傳送的資料)打包並指定一個目的地(IP位址)。
  • TCP(Transmission Control Protocol,傳輸控制協定) 則像是「郵差」,確保信件完整地送到收件人手中,並且按順序將信件拆開給對方。如果中途信件丟了,TCP還會負責補寄,確保資料不會遺失。

IP負責「路線」,讓資料知道要從哪裡送到哪裡;而TCP則確保這些資料完整無誤地到達。因此,TCP/IP的合作就像郵差根據信封上的地址,負責把一封封信件送到目的地一樣。

所以說,TCP 屬於 傳輸層(第四層)。傳輸層負責數據的可靠傳輸,確保資料到達目標後是完整的,並且處理數據包的順序和錯誤檢查。而IP 屬於「網路層」(第三層)。在這一層,IP負責將數據包從一台設備傳送到另一台設備,並確保數據能夠跨越不同的網路進行傳遞。

TCP 建立連線時,會基於基於三次握手來確定建立連線,這裡一樣引用 Cloudflare的圖,三次握手的明確步驟為

  1. SYN(Synchronize):客戶端首先發送一個同步請求(SYN)訊息給伺服器,表達它希望建立連線。
  2. SYN/ACK(Synchronize/Acknowledge):伺服器收到 SYN 訊息後,回傳一個 SYN/ACK 訊息,表示它已收到同步請求,並同意進行連線。
  3. ACK(Acknowledge):最後,客戶端發送一個確認(ACK)訊息,告訴伺服器它也準備好進行通訊。此時,連線正式建立,客戶端和伺服器可以開始資料傳輸。

三次握手的設計是為了確保連線的可靠性,確保雙方都能接收到並準備好進行資料通訊。這也是 TCP 相對於 UDP 更加穩定的原因之一,因為每次連線建立都必須經過確認,避免資料遺失或重傳。

https://ithelp.ithome.com.tw/upload/images/20240917/20115895H17uhNIFMX.png

所有的 HTTP 傳輸協定(從 HTTP/1.0 到 HTTP/3)都是基於 TCP/IP 架構來進行變形和改進的。不同的版本在 TCP/IP 基礎上,針對其效率和使用場景進行了不同的優化與調整。

HTTP版本差異詳述

接著我們可以進入版本差異的探討了,這裡一樣借用bytebytego的圖如下。這張圖主要展示了從 HTTP/1.0HTTP/3.0 協議的演進,並且詳細解釋了不同HTTP版本在資料傳輸層的差異。

https://ithelp.ithome.com.tw/upload/images/20240917/20115895lc9pkde4wb.png

1. HTTP/1.0 和 HTTP/1.1:基於TCP的HTTP連接

HTTP/1.0HTTP/1.1 都是基於 TCP 連接進行傳輸的。可以看到左側方框中,顯示了三次握手過程,但HTTP1.0每次請求都需要建立一個新的TCP連接,這會在多次請求時,需要反覆進行握手,導致效能降低。

所以HTTP1.1引入 Keep-Alive,允許重複使用同一個 TCP連接來發送多個HTTP請求,,減少了每次請求都要重新建立連接的負擔,這就是圖中 Persistent Connection(持久連接)的概念。這樣一來,開啟連接後可以保持多個請求和回應,直到連接關閉。

2. HTTP/2:多工處理、Frame 與標頭壓縮

HTTP/2 使用相同的 TCP 連接,但帶來了許多效能上的提升,特別是引入了「多工處理」和「Frame」機制。

  • Frame 的引入

    HTTP/2 將所有的資料打包成獨立的「Frame」(幀)。每個請求和回應的資料都被切分成不同的 Frame,這些 Frame 可以在同一個 TCP 連接上進行傳輸,並且可以並行處理。每個 Frame 帶有流ID,這樣伺服器和客戶端能夠識別屬於同一個請求或回應的 Frame,並按需重組成完整的數據流(stream)。這使得 HTTP/2 能夠在同一條連接中同時處理多個請求,避免了 HTTP/1.1 中「封包頭阻塞」的問題(Head-of-Line Blocking)。

    圖中展示了多個請求和回應(streams)在同一個 TCP 連接上傳輸。每個請求會有自己的標頭和資料,這些資料會被切分成不同的 Frame 同時傳輸,而不需要等候其他請求完成。Frame 的引入使得 HTTP/2 能夠在一個連線上同時傳輸多個請求與回應,實現更高效的多工處理。另外 ,HTTP/2 引入了標頭壓縮機制,進一步減少重複的請求和回應標頭資訊,降低了資料傳輸量。

  • 標頭壓縮

    這邊嘴巴癢,在稍微提一下關於標頭壓縮,標頭壓縮在 HTTP/2 中具體是透過一種稱為 HPACK 的壓縮格式進行,主要是使用靜態字典和動態字典來減少重複標頭的傳輸。

    如下圖( 引用點我 )

https://ithelp.ithome.com.tw/upload/images/20240917/20115895DBRCzY3taR.png

最左邊的方框顯示了傳統的 HTTP 請求標頭,包括常見的 :method:scheme:host:pathuser-agent 和一個自定義標頭 custom-hdr。這些標頭會隨著每次請求一起傳輸,但在重複請求中,這些資料其實很多都是重複的。

中間的方框展示了兩種字典:靜態字典(Static Table)動態字典(Dynamic Table)

靜態字典包含了常用的標頭和標頭值,例如 :method GET:host example.com,每一個常用項目都有對應的索引值,這些索引可以用來替代實際的文字傳輸,以節省資料量。在這裡,可以看到靜態字典中的 :method GET 對應到索引2。

動態字典動態字典是隨著請求過程中逐步更新的。當某個標頭在請求中首次出現,它會被加入到動態字典中,並分配一個索引值。例如,user-agent:host example.com 都被加入到了動態字典中,並分別分配了索引值62和63。在後續的請求中,這些標頭可以直接使用動態字典中的索引,而不需要重複傳送整個字串

右邊的方框展示了最終的編碼結果。這個過程中,Header 被轉換為數字和霍夫曼編碼的組合:

  • 數字索引:如果標頭或其值已經存在於靜態或動態字典中,就會使用對應的索引來表示。例如,2 代表 :method GET63 代表 :host example.com62 代表 user-agent
  • 霍夫曼編碼:對於那些不在字典中的值,會進行 霍夫曼編碼(Huffman Encoding),這是一種壓縮技術,用更短的二進位碼來表示頻繁使用的字元,進一步壓縮數據量。圖中的 /resourcesome-value 就是用霍夫曼編碼進行壓縮的。

具體的過程總結,HTTP/2 請求中,標頭資料首先會檢查是否已存在於靜態或動態字典中。如果存在,直接使用對應的索引來替代;如果不存在,則將該標頭或值加入動態字典並使用霍夫曼編碼壓縮數據。後續的請求可以利用動態字典中的索引來減少重複標頭的傳輸,進一步優化效能。

舉個例子,如果第一次請求標頭是

GET /index.html HTTP/2
Host: www.example.com
User-Agent: Mozilla/5.0
Accept: text/html

在後續請求中,如果 HostUser-Agent 沒有變化,HPACK 可以直接用動態字典的索引來表示這些標頭,例如將 Host 設為 1User-Agent 設為 2。這樣接下來的請求不會再傳完整的字串,而是使用編號來代表標頭,減少傳輸量。

  • Frame 與多工處理的運作方式
    • 請求與回應的資料切分:每個 HTTP/2 請求與回應都會被分成多個 Frame,這些 Frame 會附帶一個「流ID」(Stream ID),來標示它們屬於哪個請求或回應。
    • 多個 Frame 的並行傳輸:不同請求的 Frame 可以同時被傳輸,這意味著多個請求可以在同一條 TCP 連線上並行進行,而不會發生像 HTTP/1.1 那樣的頭阻塞問題。
    • 重新組合 Frame:在伺服器或客戶端接收到 Frame 後,根據流ID 將這些 Frame 重新組合成完整的請求或回應。

3.HTTP/3:基於UDP的QUIC協議

HTTP/3 則完全跳脫了TCP的限制,改用基於 UDPQUIC 協議來進行資料傳輸。在圖的右下角展示了QUIC的傳輸過程。QUIC不再依賴三次握手,而是使用UDP進行更快的連接建立與資料傳輸。

QUIC 的多工處理功能比 HTTP/2 的 TCP 方式更有效率,因為在 TCP 中,如果一個資料包丟失,整個連線的所有請求都必須等待重傳。但在 QUIC 中,資料包的丟失只會影響單一請求(單一資料流),而不會阻塞其他請求的傳輸,這進一步提升了傳輸效能,特別是針對高延遲或不穩定的網路環境。 圖中展示的 QUIC 連線允許多個資料包(以編號 1 到 7 表示)同時傳輸,而不會互相阻塞。

此外,QUIC 也提供了零 RTT 連接建立(0-RTT connection establishment)和內建的 TLS 加密,進一步改善了效率和安全性。

以下為表格總結

HTTP版本 傳輸方式 效能特點 主要應用場景
HTTP/1.0 基於TCP,單一請求 每次請求都要建立新連線,延遲高效能低 早期靜態網頁傳輸
HTTP/1.1 基於TCP,持久連線 支援持久連線和管道化,但仍有隊頭阻塞問題 現代網頁應用,但效能有限
HTTP/2.0 基於TCP,多工處理 支援多工處理、標頭壓縮和伺服器推送,效能顯著提升 現代高效能Web應用,API服務等
HTTP/3.0 基於UDP,使用QUIC協定 基於UDP的低延遲、高效能傳輸,資料包丟失只影響單一請求,適合不穩定網路環境 影音串流、即時通訊、互動遊戲、低延遲應用場景

觀察格式

以下是我用Telnet去觀察格式差異紀錄。因為是紀錄,所以就不多加解釋了,不過也建議你可以跟著玩看看去看HTTP的細部格式與差異。

測試: 使用Telnet 觀察格式

使用 telnet 指令來手動發送一個 HTTP/1.0 請求,請求的目的是連線到 Google 的伺服器

https://ithelp.ithome.com.tw/upload/images/20240917/20115895SiUoaYmHWg.png

  • Status Line

    HTTP/1.0 301 Moved Permanently: Status Line,HTTP/1.0 表示協議版本。301 表示 HTTP 狀態碼,301 意思是所請求的資源已被永久移動到新的位置。Moved Permanently 是與狀態碼相關的人類可讀的狀態描述。

  • Headers

    • Location: https://about.google/
    • Content-Type: text/html; charset=UTF-8
    • X-Content-Type-Options: nosniff
    • Date: Sat, 15 Jul 2023 01:29:28 GMT
    • Expires: Sat, 15 Jul 2023 01:59:28 GMT
    • Cache-Control: public, max-age=1800
    • Server: sffe
    • Content-Length: 218
    • X-XSS-Protection: 0
  • Blank Line

    Headers 和 Body 之間的空白行就是這裡的 Blank Line,這表示 Headers 的結束。

  • Body

    **<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">...**回應的 Body 是一個 HTML 文件,它告訴用戶資源已經被移動,並提供了一個連結到新位置。

Connection closed by foreign host. 是在 HTTP Response 外的資訊,這是 telnet 客戶端告訴我們遠端伺服器已經關閉了連線。(HTTP1.0拿到資料後就會關閉連線但HTTP1.1不會)。

測試: 使用curl觀察HTTP2格式

https://ithelp.ithome.com.tw/upload/images/20240917/201158952QWhsksP4p.png

HTTP/2 的請求和回應:

  • Request Headers

    • GET / HTTP/2 - 這是請求行,包含了 HTTP 方法 (GET)、路徑 (/)、和協議版本 (HTTP/2)。
    • Host: www.google.com
    • user-agent: curl/7.68.0
    • accept: */*
  • Response Headers

    • HTTP/2 200 - 這是狀態行,包含了協議版本 (HTTP/2) 和狀態碼 (200,表示 OK)。
    • date: Sat, 15 Jul 2023 01:48:22 GMT
    • expires: -1
    • cache-control: private, max-age=0
    • 等等,直到 vary: Accept-Encoding 這一行。
  • Body:這是 HTML 文檔的開始,從 <!doctype html><html itemscope=""... 開始,實際的 HTML 內容在這段輸出中被省略了。

Connection state changed (MAX_CONCURRENT_STREAMS == 100)!。在 HTTP/2 中,一個連接可以有多個串流(stream),每個串流都是一個獨立的請求/回應交換。這個訊息表示該連接的最大並行串流數已經被設定為 100。

最後,TLSv1.3 (IN), TLS handshake, Newsession Ticket (4): 以及後續的內容是關於 TLS 連接的細節,並不屬於 HTTP/2 請求/回應的一部分。

HTTP/2 與 HTTP/3,由於它們是二進位協議,並使用較複雜的機制,如Frames來組織資料, telnet 這種基於文本的工具來無法模擬或解析這些協議。(目前curl要使用HTTP3測試會比較麻煩,建議去看curl source readme操作看看)

直接用瀏覽器開發者模式觀察

一般Chrome開發者模式即可看HTTP發送與回應詳細格式,你可你看到目前大多是2.0與3.0混用狀況。

https://ithelp.ithome.com.tw/upload/images/20240917/20115895ZPIcwjbI6J.png

點擊後訊息如下

https://ithelp.ithome.com.tw/upload/images/20240917/20115895vPqqbMY5ZO.png

Headers

  • General:這部分包含一般性資訊,如請求的 URL、請求方法(例如 GET 或 POST)、回應的 HTTP 狀態碼(例如 200, 404等)、遠端地址(即服務器的 IP 和端口)和參照策略。

  • Response Headers:顯示從服務器返回的 HTTP 標頭。這些標頭可能包括內容類型、設置的 cookies、是否允許跨域請求等等。

  • Request Headers:顯示出發給服務器的 HTTP 標頭,可能包含用戶代理、接受的內容類型、引薦來源等資訊。

Payload:顯示了請求的主體數據,只有當請求方法為 POST 或 PUT 時才有。

Preview:顯示 HTTP 回應的內容。如果回應是 JSON,它會以更易於閱讀的方式格式化。

Response:顯示 HTTP 回應的原始內容。

Initiator:顯示發起該 HTTP 請求的檔案或函式的資訊。

Timing:顯示 HTTP 請求相關的各種時間點,例如,發送請求、接收回應等等的時間。


上一篇
後端協定基礎知識建置-HTTPS傳輸格式
下一篇
後端協定基礎知識建置-認識RESTful API設計原理
系列文
微服務奇兵:30天Quarkus特訓營30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言