跳轉到

HTTP

HTTP(HyperText Transfer Protocol)超文本傳輸協定的說明和注意事項。

OSI 中扮演的角色

傳輸層(Transport Layer)之上,通常包辦會議層、表現層、應用層, 但在第三版(HTTP/3)之後,連傳輸層也一起包進去了, 詳見 QUIC 官網實際封包內容

HTTP 建立在 TCP 之上,雖然 TCP 可以確保連線的穩定, 但是我們需要更高層次的會話(Session)和設定機制,例如: 這次請求的人,是不是一個月前登入的那個使用者。 這種驗證邏輯,在任何傳輸層協定都無法辦到,因為這已經牽涉到「應用邏輯」了。

從上面也可以得知,HTTP 其實就是一種針對應用程式邏輯的協定, 所謂的超文本(Hypertext)就是不再像底層協定那樣, 透過位元(bit)去做一些參數設定,例如 TCP 選項, 而是透過純文字來控制參數, 例如 HTTP 用 HSTS 去協調請求方(通常是瀏覽器)應該用哪個版本的協定。

整個協定非常單純的分成三個區塊:協定資訊參數設定溝通內容。 並分別用 CRLF 這個換行符號, 來標示這三個區塊的位置。

協定資訊

這是請求或回應的第一行。

如果是請求方,內容就包括你用了什麼版本的 HTTP, 你針對應用程式的哪個地方(HTTP Path), 做什麼樣的請求(HTTP Method),例如:

GET /hello HTTP/2
...接下來是參數設定...

就是使用 GET 方法到應用程式的 /hello 這個位置去做 HTTP/2 的請求。

如果是回應, 則是會有版本和回應的狀態(HTTP Status),例如:

HTTP/2 200
...接下來是參數設定...

就是回應 200 這個編號, 在 RFC-9110 中, 200 這個編號代表 OK

參數設定

讓這個協定擁有非常多眉角的地方就是參數設定, 位置在請求(或回應)的第二行到下一個空行:

GET / HTTP/2
header1: value
header2: value

payload

上述範例可以看到標頭(Header)總共有兩個, 這是因為第二行到下一個空行之間總共有兩行。

標頭的格式很單純,行首到冒號之前為鍵(key),大小寫沒差,不可以有空格; 冒號後為值(value),大小寫有差,需要忽略前面的空格。

由此誕生極其複雜的應用設定環境。 身為使用者通常你不用太擔心這件事情,因為偉大的瀏覽器和相關規範,例如 W3CIANA 和很受公信的 MDN 等等, 都幫你管理好了,但是身為應用程式的開發者,你可能就要開始頭大了。

溝通內容

在第一個空行之後的文字,通常存放應用程式邏輯的內容,例如:

POST /give-me-ticket HTTP/2
header1: value
header2: value

我的名字是呂學洲,幫我訂機票。

至於存放的內容要用什麼格式, 就可以根據應用程式自己去選擇了。

標頭:參數設定

維運要注意的標頭

Access-Control-Max-Age

MDN

它是用來處理 Preflight 請求的快取。

資安要注意的標頭

X-Forwarded-For

把請求方的 IP 傳遞下去,但是在開放網路下,是可以被篡改,要小心使用。

Meta

set the cookie

Content-Encoding

避免 CRLF Injection

Connection

如果請求有這個 Header,服務方在回應後,就會主動關閉連線。 關閉連線的那方是要負擔較大的 TCP 開銷,並貯存 TCP TIME_WAIT 的連線。

ETag

用來標誌資源的版本,可以使用這個增加快取機制,例如:

sequenceDiagram participant c as Client participant s as Server c ->> s: Request content s ->> c: Content with eTag: `abc` c ->> s: Update content if-match: `abc` c ->> s: Request content if-none-match: `abc`

Server 收到 if-match 後就可以檢查是否有競賽問題(同時有兩個人更新該 content), 或者也可以透過 if-none-match 檢查是否為舊版的 content 然後就回傳 304(Not Modified), 避免網路資源的耗用。

靜態網站要注意的標頭

Cache-Control

設定 public max-age=0 must-revalidate 代表:

幫我快取這東西,但是馬上讓他過期,並重新和我驗證這東西的

More Aggressive Cache Header

不同版本的差異

目前 HTTP 主流上共有三種版本:1.1、2 和 3。 整個公開網路環境中,HTTP/3 的使用率在 2024/06 時,已經來到 30%, 但是 Cloudflare 的文章指出, 大部分使用 HTTP/3 的場景是在瀏覽器和服務的溝通。 如果是 API 形式,則主要還是 HTTP/1.1, 這也說明任何一個新協定的推廣都需要漫長的適應期。

一些常見的問題

我的應用程式是用 HTTP 溝通嗎

這其實是個很微妙的問題,通常來說,你的應用程式遵守了部分 HTTP 協定。

HTTP 相關的 RFC 非常非常多,光是版本就有四個:1.0/1.1/2.0/3.0, 其中你使用的套件可能也沒有實作 1xx 的回應編號(前面的範例是 200), 例如 101 Switching Protocol, 有時這個編號也被用來切換到 WebSocket

再例如,HTTP 2.0 拒絕實作 Transfer-Encoding, 你的 HTTP 套件有正確實作嗎? 當版本為 HTTP 1.1 時,又有正確實作其和 Content-Encoding 的差異嗎?