HTTP/1.1 pipelining and HOL Blocking
前言
打完 portSwigger 的 HTTP Request Smuggling 之後,我開始在真實世界研究這種技巧,結果卻不小心踩到 HTTP/1.1 pipelining 的坑,想說趁此機會來研究這個機制,於是這篇文章就誕生了
Why pipelining ?
去年的文章有提到,瀏覽器針對每個 Host 有限制 MaxTCPConnection = 6
雖然有 Keep-Alive 的機制可以讓 TCP Connection 複用,但每條 TCP Connection 同時只能發送一個 HTTP Request,現代前端網站架構複雜,框架 bundle 完,動輒十幾個 js, css, img 要載入,從第 7 個 HTTP Request 開始就要等待,導致效能不佳
pipelining
HTTP/1.1 曾提出了 pipelining 來解決上述問題,根據 RFC 9112 section-9.3.2 的描述
A client that supports persistent connections MAY "pipeline" its requests (i.e., send multiple requests without waiting for each response).
簡單來說,就是在一個 TCP Connection 發送
GET /style.css HTTP/1.1
Host: localhost
GET /script.js HTTP/1.1
Host: localhost
GET /image.jpg HTTP/1.1
Host: localhost
資訊
去年寫的 深入解說 HTTP message 有提到 HTTP/1.1 的傳輸格式
HTTP/1.1 Server 就會依序回傳
HTTP/1.1 200 OK
Content-Length: ...
Content-Type: text/css
<style>css here...</style>
HTTP/1.1 200 OK
Content-Length: ...
Content-Type: text/javascript
console.log('hello world')
HTTP/1.1 200 OK
Content-Length: ...
Content-Type: image/jpg
jpg here...
看起來很美好,但是有一些限制
pipelining 限制 1: HTTP/1.1 HOL Blocking
根據 RFC 9112 section-9.3.2 的描述
it MUST send the corresponding responses in the same order that the requests were received.
假設某個 HTTP Request 花了比較久
最終 Request C 還是得等到 Request B 完成,才能回傳
這個現象,有個專有名詞叫做 Head-of-line blocking (HOL Blocking),來看看 MDN 文件 的解說:
Unfortunately the design of HTTP/1.1 means that responses must be returned in the same order as the requests were received, so HOL blocking can still occur if a request takes a long time to complete.
而 HTTP/2 解決了 HTTP/1.1 的 HOL Blocking,解法也很簡單,就是在每個 Request/Response 都加上流水號 ID,細節我會在未來的 HTTP/2 談到