Bo2SS

Bo2SS

網路通信 | HTTP(S)那些事兒

大家好,這裡是 Bo2SS~真快,畢業過去一年了,公司又注入了新鮮的血液。部門裡有一個大前端新人培訓,自己斗膽報了名,做一個 HTTP 相關知識的分享。其實之前自己沒有系統地學習過 HTTP,所以提前了 2 個月準備這次分享。上週分享完後,據問卷反饋全員五星🌟好評,就在這裡記錄一下吧~

前言#

如題,今天要聊的是網路通信中 HTTP 和 HTTPS 的那些事兒。

Q:為什麼要分享這個主題?

  1. 🫡 生活中常見。HTTP 在整個互聯網是非常常見的,比如我們看劇、刷短視頻、面向 Google 編程,都会用到它。作為研發,我們有義務深入了解一下。

  2. 🤔 工作中常見。在我們工作中其實經常會遇到相關問題,比如在前後端接口聯調的時候,如果遇到了預期之外的情況,我們首先要去關注的,就是 HTTP 請求裡的一些信息。我們應該熟悉它的結構和一些規範。

  3. 📖 思想可借鑒。HTTP 它發展了 30 多年,3 個大版本,它的很多設計思路是值得我們在開發中去借鑒的。

Q:參考了哪些資料?

這次分享前我做了很多準備工作,主要參考的有:

  1. 極客時間上的《透視 HTTP 協議》課程。這個系列我可能也就聽了十吧遍吧,如果大家想深入了解更多的細節,可以去看看。作者還提供了可以實戰學習的倉庫chronolaw/http_study,通過它你可以很方便地搭建一個 Web 伺服器,然後通過瀏覽器去訪問裡面的資源來理解 HTTP。

  2. 小林 Coding。這是我關注比較久的一個公眾號,裡面有很多關於計算機基礎的圖解系列文章,現在也有了小林 Coding 網站版

  3. Bo2SS。這是我自己的公眾號,就在這裡啦👋。之前寫過 2 篇 HTTPS 相關的文章:

    1. 信息安全 | 互聯網時代,如何建立信任?

    2. 信息安全 | (加餐)互聯網時代,如何建立信任?

此外,文中也引用了一些第三方的圖片,就沒有一一列舉來源了,如有不當之處,歡迎聯繫。

Q:這次分享的目標?

  1. 🔍 快速定位 HTTP 問題。剛剛說到了我們在前後端聯調的時候,可能會經常遇到結果不符合預期的情況,我們應該首先能夠通過狀態碼去快速定位,這個問題是後端的還是前端的。

  2. 🥣 熟悉 HTTP 報文裡的常見頭字段。熟悉常見的頭字段後,我們不僅可以掌握 HTTP 的基本功能,還可以學到很多 HTTP 的設計思想。這個報文在哪裡可以看到呢?各端一般都會有抓包工具來查看。

  3. 🔐了解基本的加密知識。互聯網時代,用戶隱私、業務保密都是非常重要的。

🏁終極目標:看完這篇文章,你將擁有自行深入學習 HTTP 的能力了,比如通過 WireShark、Chrome、Telnet 工具,甚至可以去看 RFC 文檔,裡面幾乎包含了所有網路相關的重要資料。

➕一些資料:各工具的使用指南(WireSharkChromeTelnet),RFC 文檔匯總

Q:這次要分享哪些內容?

也就是這次分享的大綱:

簡單來說,今天要聊的就是HTTP 和 HTTPS 是什麼,然後它們怎麼發展的


好,下面進入正題:

什麼是 HTTP?#

HTTP 是什麼,又不是什么?#

image

HTTP 的全稱是 Hypertext Transfer Protocol,也就是超文本、傳輸、協議。我們從後往前解釋~

  1. 協議。什麼是協議,我們可以聯想我們的租房協議、三方協議,其實都是一樣的含義,協議的 “協”,代表有兩個以上的參與方,協議的 “議” 呢,代表約定和規範,約定你可以做什麼、不能做什麼。

  2. 傳輸。然後是傳輸,我們可以聯想快遞,專門在兩點之間傳輸,其關鍵有兩點。第一點是雙向性,我們可以寄快遞,也可以收快遞;第二點是傳輸過程可以有中轉,比如我們寄快遞,會經過快遞小哥、快遞公司、物流倉庫等,最後才會到達收件方,同時這些中間者也都遵守協議。

  3. 超文本。最後關於超文本,顧名思義就是超越了普通文本的文本。這裡我想問大家一個問題,超文本除了文字、圖片、音頻、視頻格式,還有一個最關鍵的格式,是什麼?對了,就是超鏈接。超鏈接能夠讓我們從一個 “超文本” 跳躍到另一個 “超文本”,讓我們的文本從以前的線性結構,變成了非線性的網狀結構。

一句話總結就是:“HTTP 是一個在計算機世界裡專門在兩點之間傳輸文字、圖片、音頻、視頻等超文本數據的約定和規範”。

image

這張圖是我們基本的 HTTP 通信過程中的參與者們。從這張圖,我們可以捋清 HTTP 它不是什么,從而對 HTTP 有一個更清晰的認識。

  • HTTP 不是實體,比如左邊的 Web 瀏覽器(發送方),右邊的 Web 伺服器(接收方)。

  • HTTP 不是互聯網,HTTP 傳輸的超文本資源是互聯網資源中的一部分。

  • HTTP 不是一門編程語言,但是 HTTP 支持各種編程語言來實現。

  • HTTP 不是HTML,HTTP 可以傳輸 HTML,HTML 是超文本的常見格式。

  • HTTP 不是一個孤立的協議。通常在 HTTP 的下方,會有一些底層協議支持,比如 TCP、IP、DNS 等等;在 HTTP 的上方,也有一些依賴於 HTTP 的協議,比如 WebSocket、HTTPDNS 等等。這些協議相互交織,構成了一個協議網,而 HTTP 則處於中心地位。

HTTP 世界全覽#

我們再對 HTTP 世界的全貌進行一下瀏覽,主要分為應用相關和理論相關。有了對 HTTP 通信鏈路更宏觀的認識,我們在定位問題的時候,能夠更清楚問題可能是由哪個環節導致的。

HTTP 相關應用#

image

我們從右往左看:

  • Internet - WWW:Internet 就是互聯網,裡面存儲著各種信息資源。WWW 是互聯網的一個子集,簡稱萬維網,它基於 HTTP,所以存儲的都是超文本資源,這些資源在互聯網中的占比大約在 90%。

  • Web Browser:Web 瀏覽器,是 HTTP 通信過程中的請求方,並可以顯示請求到的資源。

  • Web Server:Web 伺服器,是 HTTP 通信過程中的響應方,由它來管理網路資源。一般會將它分為硬體和軟體,硬體指物理伺服器、雲伺服器之類的機器,軟體指 Nginx、Apache 之類的應用程序。

  • CDN:Content Delivery Network,也就是內容分發網路。前面說到 HTTP 的傳輸過程可以有中轉,這裡 CDN 的定位就是中轉方,起到網路代理的作用。它可以緩存伺服器的資源,加快網路響應,還可以提供負載均衡、安全防護等能力。

  • Crawler:就是爬蟲。和 Web 瀏覽器類似,它也可以理解為是一種用戶代理,一般是給各大搜索引擎自動抓取數據、存入數據庫用的。

  • Others: 其它還有 HTML、Web 服務、WAF。Web 服務可以理解為運行在 Web 伺服器上的具體服務或者服務開發規範。WAF 的全稱是網路應用防火牆,它其實也是一種代理。

HTTP 相關理論#

image

同樣從右往左看,右邊的 HTTP/1.1、HTTPS、HTTP/2、HTTP/3 就是今天我們要聊的主要協議,左邊:

  • TCP/IP:它代表的其實是一個協議棧,裡面包含了很多網路通信協議。

image

這裡我們把基於 TCP/IP 的 HTTP 通信過程與快遞運輸又做一個類比:

1)超文本 => MAC:左圖中左邊這一列,要傳輸的超文本從應用層一直到鏈接層,每經過一層都會被加上對應的頭,比如 HTTP 頭、TCP 頭、IP 頭、MAC 頭,這就像快遞的打包過程;

2)MAC => 超文本:左圖中右邊這一列,被傳輸的數據每經過一層則都會被去除一個頭,這就像快遞的拆包過程。


  • URI - URL:URI(I - Identifier)是統一資源標識符,它又分為 URL 和 URN 兩種形式,但因為後者在互聯網世界並不常用,所以 URI 一般指的就是 URL。

1)URL(L - Locator):我們瀏覽器上方的地址就是 URL。

image

它的基本組成如上圖,我們可以先關注紅框部分:

  1. scheme:最左邊的 scheme 代表協議,如 http、https、ftp 等等。注意這裡協議後面緊跟的://符號是固定的、必須的。

  2. host:中間 host 是主機名,也叫域名,待會講 DNS 的時候再細說。

  3. path:最後邊的 path 代表資源路徑。

Q:這裡有一個問題,圖中示例 URL 的域名是www.creatorseo.com/嗎?

答案是否定的,最後面的斜杠/屬於 path,它代表的是所訪問主機的根目錄,因為早期互聯網上的計算機大多是 UNIX 系統,所以這裡的路徑格式是採用的是 UNIX 上的文件路徑風格。

下面還有一張圖,這張圖是 URL 的完整格式

image

比上圖還多了三個組成:

  1. user@:我們可以在 URL 裡就填上用戶密碼信息,但因為安全原因已經不推薦使用它了。

  2. ?query:這個部分可以附加一些對資源的額外要求,以?開始,由多個鍵值對k=v組成,每個鍵值對用&連接。

  3. #fragment:它代表一個片段標識,我們可以理解為資源內的一個錨,它是給客戶端使用的,不會發送給伺服器。平時我們在看一些博客時(如閱讀原文跳轉到我的博客網頁),點擊懸浮目錄中的某個標題做跳轉,就是用的這個部分。

💡 這裡還有兩個小提醒~一個是 host 後面還可以通過:port指定端口。另一個就是一般聊到 URL 時,還會聊到escape 轉義encode 編碼兩個概念,因為如果沒有它們,伺服器可能就無法正確地處理 URL。試著想一想如果 path 裡也有?符號,伺服器該怎麼解析出 query 的起始位置呢?

  1. escape - 轉義:針對特殊字符,我們一般會做轉義,直接將其轉換成 ASCII 碼的十六進制後再加一個%前綴,比如SPACE對應%20?對應%3F

  2. encode - 編碼:針對漢字等其它語言,我們一般會先進行 UTF-8 編碼,再轉義。如果你不信,可以試試把包含中文的 URL 複製粘貼到微信中(如閱讀原文跳轉到我的博客網頁)。

2)URN(N - Name):下面再回到 URI 的第二種形式 URN,它是通過命名空間加具體標識符的形式來標記資源,如urn:<NAMESPACE-IDENTIFIER>:<NAMESPACE-SPECIFIC-STRING>。它在我們上網時不常用,但如果你去買書,可能會發現每本書的條形碼位置有一串字符,如ISBN xxx-x-xx-xxxx,這其實就是 URN 的一種用法。

image


  • DNS:全稱 Domain Name System,即域名系統,它是用來做域名解析的應用層協議,也就是將域名轉換為 IP 地址。

我們先來看看域名的結構,還是這張圖,這裡紅框部分就是域名,它是一個有層次的結構,用.分隔,越靠右邊,層級越高。如從右到左,分別為頂級域名、二級域名、三級域名等等。

image

我們再來看看 DNS 的類型和 DNS 解析域名的步驟,如下圖:

image

1)DNS 類型。DNS 分為根 DNS、頂級 DNS、權威 DNS 和非權威 DNS。根 DNS 共有 13 組,遍布全球,它可以根據請求的頂級域名將 DNS 解析指定給下面對應的頂級 DNS。頂級 DNS 又根據二級域名指定權威 DNS,直到解析出域名對應的 IP。而一些大公司還會自建 DNS,又叫非權威 DNS,它們的分布更廣,比較知名的有 Google 的 8.8.8.8,Microsoft 的 4.2.2.1,還有 CloudFlare 的 1.1.1.1 等等。

2)DNS 解析域名步驟。實際的解析過程分為 4 步:系統首先會找 DNS 緩存,可能是瀏覽器裡的,也可能是系統裡的;如果找不到,再去查看 hosts 文件,裡面有我們自定義的域名 - IP 對應規則,Mac 下的 hosts 文件路徑為/etc/hosts;如果匹配不到,再去問非權威 DNS,一般默認是走我們網路運營商指定的;如果還是沒解析出來,就要走根 DNS 的解析流程喽~

💡 這裡還有一些域名解析相關的常用命令(dig、host、nslookup),如果你感興趣,可以去終端試一試~

1. DNS addressing process: dig www.baidu.com +trace @8.8.8.8
2. domain name <=> IP: host www.baidu.com
3. domain => IP: nslookup www.baidu.com

如果你知道用 WireShark,還可以通過filter: port 53過濾出 DNS 解析相關的抓包。


  • Proxy:就是代理。代理一般分為正向代理和反向代理,正向代理靠近客戶端,反向代理靠近伺服端。剛剛我們提到的 CDN 屬於反向代理,而我們訪問外網用的 VPN 就屬於正向代理。

HTTP 報文#

鋪墊了這麼多(實際上也是值得的),終於到了 HTTP 最重要的部分!

所謂 HTTP,超文本傳輸協議,其中最重要的部分其實是最後的協議,裡面約定了 HTTP 報文的格式和用法。

基本格式#

我們先來看看 HTTP 報文的基本格式,它可以簡單分為頭部身體兩部分:

image

1)頭部:一般可以包含起始行部分,也就是頭部由 Start line 和 Header 組成。下圖展示了一次請求中,請求頭和返回頭的結構:

image

  1. 請求頭

    1. Start line 由請求方法、URI、HTTP 版本、空格間隔符以及最後的換行符組成。

    2. Header 由一個個的key:value鍵值對和最後的換行符組成,注意這裡:前不能有多餘的空格,不信你用telnet命令試試(Mac 上可以使用brew install telnet來安裝telnet,並且推薦搭配極客時間提供的實戰倉庫chronolaw/http_study來用)。

  2. 返回頭

    1. Start line 由HTTP 版本、狀態碼、狀態碼對應解釋、空格間隔符以及最後的換行符組成。

    2. Header 結構和請求頭一樣。

2)身體:一般根據業務來約定 body 的具體內容,它是可有可無的。


下面我們再來看看請求行中的請求方法和狀態碼具體有哪些~

請求方法#

image

HTTP/1.1 裡規定了八種請求方法,這裡把它們分成了常用和不常用兩類,另外還有一類是擴展的請求方法,注意這些方法都必須是大寫的形式。

這裡主要介紹下常用的請求方法:

  1. GET 和 HEAD,用來獲取伺服器資源。兩者的區別在於 HEAD 只會獲取到頭部信息,GET 會獲取到完整的頭部和身體信息。所以如果你只是想確認某個資源存不存在或者只需要頭部信息,可以用 HEAD 請求,從而減少傳輸量。

  2. POST 和 PUT,用來發送資源給伺服器。兩者的區別在於前者是在伺服器創建資源,類似數據庫的 CREATE 操作,後者是修改伺服器的資源,類似 UPDATE 操作。兩者比較類似,實際應用中 PUT 使用較少。

💡 說到請求方法,一般還會提到安全和幂等兩個概念:

  1. 安全:指的是對伺服器資源不會有實質的修改,所以上面提到的 GET 和 HEAD 是安全的。

  2. 幂等:指的是相同的操作執行多次後,結果是否相同,所以上面提到的 GET、HEAD 和 PUT 都是幂等的。

返回狀態碼(5 類)#

這次到了我們的第一個目標:🔍 透過狀態碼快速定位 HTTP 問題。

image

狀態碼一般分為5 大類

1xx:提示信息類。一般是一次請求的中間狀態,比較少見。

2xx:成功類。這說明請求是符合預期的,也是我們最願意看到的。

3xx:重定向類。資源發生了變動,需要客戶端向另一個域名重發請求。

4xx:客戶端錯誤。看到這個就要想想請求報文是不是填錯了。

5xx:伺服端錯誤。看到這個就要找伺服端的同學確認問題原因了。

常見的具體狀態碼可參考下圖:

image

  • 301:永久重定向,可以把請求的 URL 改一下了。

  • 302:臨時重定向。

  • 304:伺服器資源沒有變,所以重定向到了本地緩存。

  • 401:未認證錯誤,一般與鑑權、登錄相關。

  • 403:訪問被拒絕,可能訪問到了敏感信息。

  • 404:資源沒找到,可能資源路徑寫錯了,也可能是沒有權限訪問(錯誤碼是伺服端自定義設置的,404 一般用於表示資源沒找到,但也可以延伸其用途,隱蔽具體原因)。

  • 405:請求方法不被允許。

  • 502:網關或代理返回的錯誤碼,一般是訪問網關或代理背後的伺服器出錯。

  • 503:伺服器暫時不可用,請稍後重試。

💡 對於 400、500,它們是比較籠統的錯誤碼,有時候是作為兜底錯誤碼返回的,說明發生了未知的錯誤;有時候是因為伺服端不想暴露過多的細節返回的。總之伺服端在盡可能遵守公共認知的情況下,是可以自定義狀態碼的。

常見頭字段(8 種)#

很快,我們又到了本文的第二個目標:🥣 熟悉 HTTP 報文裡的常見頭字段。

首先,我們把頭字段分為 Request、Response、Universal 三大類,Universal 類裡又包含 Entity 子類。Request 類頭字段是請求方用的,Response 類頭字段是響應方用的,Universal 類頭字段是請求方和響應方都可以用的,Entity 類頭字段一般是用來描述 body 屬性的。

image

上圖列出了常見的頭字段們,看著眼花繚亂,別急,我們分功能來解釋。補充:填充色相同的頭字段們一般是搭配著使用的或者相關的。

下面,我們將頭字段們按照功能主要分為 8 種來解釋。


1)Body:body 屬性相關,可以是描述請求報文中的,也可以是描述響應報文中的。

提到 body 的類型,我們首先需要了解什麼是 MIME(Multipurpose Internet Mail Extensions,多用途互聯網郵件擴展)類型,它是從電子郵件系統中誕生的,現在也被用來描述 body 的類型。這裡有MIME 類型匯總,你點開鏈接看一下就會覺得它們很眼熟,比如application/jsontext/htmltext/javascript…… 它的前半部分是一個大的類別,後半部分是具體的格式。

  • Accept表示的是請求方可以接受的 body 類型,可能不止一個。

  • Content-Type表示的是實際傳輸的 body 類型。

為了減少 body 的大小,我們一般會對它進行壓縮,常見的壓縮格式有 gzip、deflate 和 br,它們對 text 的壓縮效果很好。

  • Accept-Encoding表示的是請求方可以支持的壓縮格式,可能不止一個。

  • Content-Encoding表示的是傳輸的 body 實際採用的壓縮格式。

因為上面的壓縮方式一般只對文本有較好的壓縮率,對於壓縮效果不好的圖片、音視頻等多媒體格式,還有一種方式來解決大文件的問題,那就是分塊傳輸。

  • Transfer-Encoding: chunked:這表示數據是被分塊傳輸的。

對於視頻類型的 body,比如我們在 b 站上看視頻,這個視頻不可能是一次就請求下來的,我們可以對視頻進行分段請求。

  • Accept-Ranges: bytes:我們一般會通過 HEAD 請求先問問伺服端是否支持範圍請求,如果支持通過字節範圍請求,伺服端就會返回這個。

  • Range: bytes=x-y:在伺服端支持的情況下,請求方就可以明確要請求第 x~y 字節的內容。

  • Content-Range: bytes x-y/length:這表示伺服端返回的 body 是第 x~y 字節的內容,內容總長度為 length。

在國際化方面,我們還可以設置對語言的要求。

  • Accept-Language表示的是請求方可以理解的語言,可能不止一個。

  • Content-Language表示的是傳輸的 body 實際的語言。

這裡還要舉一個具體的例子:Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7,有兩個細節我們要注意:

  1. 在 HTTP 規範裡,,的優先級大於;,這和我們一般的編程語言語法相反,所以上面的en;q=0.9是一對。

  2. 上面的q是什麼?它其實代表一個權重,默認是 1,響應方會盡可能使用權重最大的語言返回內容。


2)Connection:長連接相關。

在 HTTP/1.1 之前,客戶端每次和伺服端通信都需要重新建立連接,如果頻繁通信,就會不斷地重複建立和關閉 TCP 連接,如下圖左邊所示,即短連接:

image

所以要是可以讓一次 TCP 連接保持久一點,每次連接兩端多通信幾次就好了,也就是圖中右邊所示,即長連接。這不,HTTP/1.1 就支持了。

  • Connection: keep-alive:即表示使用長連接,在 HTTP/1.1 中默認開啟。

  • Connection: close:主動關閉長連接,一般是由客戶端發出的。

對於伺服端,它也可以設置長連接的斷開時機,它們是在 Web 伺服器中配置的。比如在 Nginx 中,keepalive_timeout代表長連接的超時時間,如果長時間沒有數據收發就主動斷開連接; keepalive_requests代表長連接過程中最多接收請求多少次。

因為長連接的方式,客戶端也可以同時發起多個請求,而不必等第一個請求的結果回來再發第二個請求,這也就是管道通信。

不過不管短連接還是長連接,都還會有一個隊頭阻塞(HoL blocking)問題,這是因為 HTTP 的 “請求 - 應答” 模型規定報文必須是 “一發一收” 導致的,可以參考下圖:

image

無論如何,接收方都必須先處理完紅線請求後,才能處理後面發起的請求,即使後面的請求先到了,也就是 “先發必須先處理”。

為了緩解這個問題,有一個辦法,就是並發連接,也就是對一個域名發起多個長連接,每個長連接之間互不干擾。但是長連接的保持是需要消耗伺服器資源的,而且也可能被惡意攻擊,所以規定一般長連接的上限是 6~8 個。如果還不夠用,還有一個取巧的方式,就是域名分片,同一台伺服器有多個域名對應,那麼上限也就翻倍了。


3)Redirection:重定向相關。

在聊狀態碼時,我們提到過 301(永久)、302(臨時),不知道你還記不記得它們的含義。如果返回這種狀態碼,那麼在響應頭裡一定還會標識重定向的位置。

  • Location就是重定向的位置,一般有絕對路徑和相對路徑兩種形式,絕對路徑對應的就是 URL 的基本格式,相對路徑則沒有 scheme 和 host,默認使用原請求的 URL 裡的。

和重定向相關的還有 3 種狀態碼:303 類似 302,但請求方法只能是 GET;307、308 分別類似 302、301(這裡是反的……),但它們都不允許重定向後的請求有任何變動。


4)Cookie:解決 HTTP 無狀態特點帶來的問題。

首先我們要清楚無狀態指的是客戶端還是伺服端?沒錯,是伺服端,也就是伺服端不知道收到的這次請求和上次請求有什麼關聯,那這樣會讓伺服端處理一些特殊場景時方案變得複雜,比如購物。

所以這裡 Cookie 就是來解決這個問題的。簡單來說,就是伺服端給客戶端貼了一個小紙條,標識某個客戶端的身份,這個客戶端在每次請求時帶上這個小紙條,就可以證明自己的身份了。

  • Set-Cookie: a=xxxSet-Cookie: b=yyy:這是伺服端返回的,一個 Cookie 本質上就是一個鍵值對,並且每個 Cookie 是分開的。

  • Cookie: a=xxx; b=yyy:這是客戶端在發送請求時帶上的,也就是之前伺服端返回的 Cookie 們,它們是合在一起的。

注意,客戶端在收到這些 Cookie 後會將它們保存到客戶端裡,我們去看看 Chrome 瀏覽器就可以發現它們。

image

咦?Cookie 除了 Name 和 Value,怎麼還有這麼多屬性?其實,伺服端返回的 Cookie 一般長這樣:Set-Cookie: a=xxx; Domain=xx; Path=xx; Max-Age=xx; Expires=xx; HttpOnly; Secure; SameSite=xx...

  1. Domain、Path:只有在客戶端請求的 URL 匹配上它們時,這個 Cookie 才會被帶上。

  2. Max-Age、Expires:代表 Cookie 的失效時間,後面 Cache 也有類似的屬性,要注意的是 Max-Age 的優先級大於 Expires。

  3. HttpOnly:其為真時,代表這個 Cookie 只能通過 HTTP (S) 協議傳輸,禁止其他方式訪問,比如在 JS 裡就不再可以用 document.cookie 獲取它了,以防腳本攻擊。

  4. Secure:其為真時,代表這個 Cookie 只有在發起安全的 HTTPS 請求時,才會被帶上。

  5. SameSite=xxx:設置 SameSite=Strict 可以嚴格限定該 Cookie 不能跨站發送;SameSite=Lax 則略寬鬆一點,允許在 GET/HEAD 等安全的請求裡使用該 Cookie。


5)Cache:緩存相關。

緩存真的無處不在,HTTP 請求裡也不例外。這裡提到的緩存是存放在客戶端的,目的就是儘可能地減少網路請求或著返回數據的大小,從而提升網路傳輸效率。

  • Cache-Control

    • 伺服端可以返回的屬性有:max-age=10/no-store/no-cache/must-revalidate

      • max-age 的單位是秒,從返回那一刻就開始計算;

      • no-store 代表客戶端不允許緩存;

      • no-cache 代表客戶端使用緩存前必須先來伺服端驗證;

      • must-revalidate 代表緩存失效後必須驗證。

    • 客戶端可以發送的屬性有:max-age=0no-cache

      • 一般地,cmd + R 刷新頁面會帶上 max-age=0,意思是只要生存了 0 秒的數據,也就是不走本地緩存了,而是向伺服器要一個最新生成的報文;cmd + shift + R 強制刷新頁面會帶上 no-cache,和前者基本一致,看伺服端如何處理。

      • 那什麼時候緩存會生效呢?一般是在瀏覽器前進、後退或者重定向時,客戶端發起的請求就不會帶上上面的兩個屬性了。

此外,為了增加緩存控制的靈活性,這裡還有一些條件字段~

  • 伺服端返回的有:

    • Last-Modified代表文件的最後修改時間。

    • ETag全稱是 Entity Tag,代表資源的唯一標識,它是為了解決修改時間無法準確區分文件變化的問題。比如一個文件在一秒內修改了很多次,而修改時間的最小單位是秒;又或者一個文件修改了時間屬性,但內容沒有變化。Etag 還分為強 Etag、弱 Etag:

      • 前者不變的條件是資源在字節級別不變。

      • 後者不變的條件是資源在語義上不變即可,比如多了幾個空格之類的。另外,弱 Etag 的值會在前面加一個W/標記。

  • 客戶端請求時對應就用:

    • If-Modified-Since裡放的就是上次請求伺服端返回的 Last-Modified,如果伺服端資源沒有比這個時間更新的話,伺服端就會返回 304,表示客戶端用緩存就行。

    • If-None-Match裡放的就是上次請求伺服端返回的 ETag 了,如果伺服端資源的 Etag 沒變,伺服端也是返回 304。


6)Proxy:代理相關。

代理是具有雙重身份的,因為在客戶端眼裡,它是伺服端,而在伺服端眼裡,它又是客戶端。

前面也提到了代理一般分為正向代理和反向代理,反向代理一般被用來做負載均衡(合理分配任務,決定由後面的哪台伺服器來響應請求)、安全防護、加密卸載(在內網裡通信就不加密了,減少加解密成本)、內容緩存(暫存伺服器響應,這個下面會說,也就是代理緩存)等等。

image

在上面有代理伺服器的場景中,涉及到的頭字段是:

  • Via:代理伺服器會在發送請求時,把自己的主機名加端口信息追加到該字段的末尾。

但是伺服器一般需要知道客戶端的真實 IP 信息,方便做訪問控制、用戶畫像、統計分析等,所以在 HTTP 標準外還約定了下面的頭字段:

  • X-Forwarded-For:類似 Via 的追加方式,但追加的內容是請求方的 IP 地址。

  • X-Real-IP:只記錄客戶端的 IP 地址,它更簡潔一點。

不過上面的方式其實有一個很大的缺點:損耗性能!因為每次代理伺服器需要解析出 HTTP 報文頭,再修改報文數據;而且在有些情況下,報文是不允許甚至是不能(加密)被修改的。所以後來就又出現了一個專門的代理協議,這也是在標準外約定的。

基於這個協議,代理伺服器只需要在 HTTP 報文前再加一行文本即可。比如:

PROXY TCP4 1.1.1.1 2.2.2.2 55555 80\r\n
GET / HTTP/1.1\r\n
Host: www.xxx.com\r\n
\r\n

  • 開頭是PROXY五個大寫字母;

  • 然後是客戶端的 IP 地址類型,如TCP4或者TCP6

  • 再後面是請求方、應答方的地址,以及請求方、應答方的端口號;

  • 最後用一個回車換行結束。


7)Proxy Cache:代理緩存相關。

image

客戶端可以緩存,中間商代理伺服器當然也可以緩存。但因為代理的雙重身份性,所以Cache-Control針對代理緩存還增加了一些定制化的屬性~

  • 從伺服端到代理伺服器:

    • private代表數據只能在客戶端保存,不能緩存在代理上與別人共享,比如用戶的私人數據。
    • public代表數據完全開放,誰都可以緩存。
    • s-maxage代表緩存在代理伺服器上的生存時間。
    • no-transform代表禁止代理伺服器對數據做一些轉換操作,因為有的代理會提前對數據做一些格式轉換,方便後面的請求處理。
  • 從客戶端到代理伺服器:

    • max-stale代表接受緩存過期一段時間。

    • min-fresh則與上面相反,代表緩存必須還有一段時間的保質期。

    • only-if-cached代表客戶端只接受代理緩存。如果代理上沒有符合條件的緩存,客戶端也不要代理再去請求伺服端了。


8)Others

我們再看看這張常見頭字段圖,你是不是已經清楚每一個頭字段的含義和用法了呢~

image

等等,上面還漏了一些頭字段的說明,我把它們統一放在這裡補充一下:

  • Host代表要請求的主機名。它在 HTTP/1.1 裡是必須出現的,用來給伺服器區分具體選擇哪個主機來處理請求的(如果計算機上托管了多個虛擬主機就有這個作用,否則伺服器一般也不會去處理)。另外,在一般的網路框架裡,它會幫我們從 URL 裡解析出一個默認的 Host 值兜底,所以可能你沒有手動填也沒有問題,因為框架默認幫我們補充了。

  • User-Agent即用戶代理,它是用來描述請求方的身份的,伺服器可以根據它來返回合適的頁面佈局或者數據。不過由於歷史原因,它的用法已經有些混亂了,比如每個瀏覽器都自稱是 “Mozilla Chrome Safari” 之類的。

  • Date代表報文創建的時間,一般出現在響應頭裡。

  • Server展示的是提供 Web 服務的軟體名稱和版本號,但這樣暴露了伺服器的部分信息,可能存在安全隱患,所以有的時候返回裡沒有這個字段,或者僅僅是一個模糊的信息。

  • Content-Length代表報文裡 body 的長度。如果沒有這個字段,那麼一般會有另一個字段Transfer-Encoding: chunked,前面我們說到過它。

至此,我們已經講完了什麼是 HTTP 了,可以試著用 Chrome 開發者工具或者 WireShark 抓包加深理解。

什麼是 HTTPS?#

HTTPS 比 HTTP 多了個 S,這個 S 代表的是 SSL/TLS 協議。

現在來到了我們第三個目標:🔐了解基本的加密知識。

這一節因為之前寫過相關文章,所以我盡量減少篇幅,你可以點擊下面的鏈接參考:

  1. 信息安全 | 互聯網時代,如何建立信任?:三大常見密碼學算法,數字簽名,數字證書。

  2. 信息安全 | (加餐)互聯網時代,如何建立信任?:SSL/TLS,SSH,iOS 簽名,OpenSSL、WireShark 實踐。


要補充的內容是:基於ECDHE的 TLS 主流握手方式 VS. 基於RSA的 TLS 傳統握手方式。

兩者的關鍵區別在於通信密鑰生成過程中,第三個隨機數Pre-Master的生成方式:

  • 前者:兩端先隨機生成公私鑰,同時公鑰(加簽名)作為參數傳給對方,然後兩端基於雙方的參數,使用 ECDHE 算法生成Pre-Master

  • 後者:客戶端直接生成隨機數Pre-Master,然後用伺服器證書的公鑰加密後發給伺服器。

因為前者的公私鑰是隨機生成的,即使某次私鑰洩漏了或者被破解了,也只影響一次通信過程;而後者的公私鑰是固定的,只要私鑰洩漏或者被破解,那之前所有的通信記錄密文都會被破解,因為耐心的黑客一直在長期收集報文,等的就是這一天(據說斯諾登的稜鏡門事件就是利用了這一點)。

也就是說,前者 “一次一密”,具備前向安全;而後者存在 “今日截獲,明日破解” 的隱患,不具備前向安全

更多的細節可以參看《透視 HTTP 協議》裡TLS1.2 連接過程解析這一課,或者自己用 WireShark 抓包試一試~

兩者從抓包上的區別來看,主要是:

  • 前者比後者多了一個 “Server Key Exchange” 消息。

  • 前者客戶端可以不等連接完全建立就提前進行加密通信,也就是客戶端不用等伺服器發回 “Finished” 確認握手完畢,這個叫 “TLS False Start”。

HTTP 的發展#

我們通過下面表格梳理一下 HTTP 的發展過程,今天我們對 HTTP 發展有一個整體的認知就好~

TimeVersionMain changeNote
19893 key technologiesHTML, URI, HTTPPaper from Tim Berners-Lee.
1991HTTP/0.91. Request way: GET.
2. Data: HTML.
No RFC.
1996HTTP/1.01. +Request way: HEAD, POST.
2. +Data: img, audio.
3. +Other: HTTP Head, status code, protocol version.
RFC-1945 (1996).
Not a formal standard.
1999HTTP/1.11. +Request way: PUT, DELETE.
2. +Cache-control.
3. +Keep-Alive.
4. +Pipeline transmission(Content-Length), chunked transmission.
5. +Host head (Required).
+Google, Sina, Sohu, Netease, Tencent.
RFC-2616 (1999).
+Facebook, Twitter, Taobao, JD.
Divided to RFC-7230~7235 (2014).
RFC-9112 (2022).
2015HTTP/21. Transmission data format: text → binary data.
2. +Concurrent requests (use stream, abandon pipeline transmission).
3. +Header Compression.
4. +Allow the server to push.
5. +Combined with TLS 1.2+.
Based on SPDY in Chrome browser (2009).
RFC-7540 (2015).
RFC-9113 (2022).
2022HTTP/31. Transport layer protocol: TCP → QUIC (based on UDP, including TLS 1.3, IP → connection ID).
2. Header Compression: HPACK→QPACK
Based on QUIC in Chrome browser(2012).
RFC-9114 (2022).
  • 從 HTTP/1.0 開始,HTTP 已經被寫入 RFC 文檔(RFC 文檔匯總)。

  • HTTP/1.1 是 HTTP 第一個正式標準,大多數的功能我們在常見頭字段那一節介紹了。在這個階段早期,Google、Sina、Sohu、Netease、Tencent 等公司被創立了,後期 Facebook、Twitter、Taobao、JD 等公司慢慢也出來了。

  • HTTP/1.1 在各方面還比較完善了,但在性能和安全上還存在很大優化空間。所以 HTTP/2、HTTP/3 都主要是針對 HTTP 的性能方面做優化。


  • HTTP/2基於 Chrome 的 SPDY 協議,它是 Chrome 推動的。主要的變化有:
    • 傳輸數據格式從文本轉成了二進制,大大方便了計算機的解析。

    • 基於虛擬流的概念,實現了多路復用能力,同時替代了 HTTP/1.1 裡的管道功能。

    • 利用 HPACK 算法進行頭部壓縮,在之前都只針對 body 做壓縮。

    • 允許伺服端新建 “流” 主動推送消息。比如在瀏覽器剛請求 HTML 的時候就提前把可能會用到的 JS、CSS 文件發給客戶端。

    • 在安全方面,其實也做了一些強化,加密版本的 HTTP/2 規定其下層的通信協議必須在 TLS1.2 以上(因為之前的版本有很多漏洞),需要支持前向安全和 SNI(Server Name Indication,它是 TLS 的一個擴展協議,在該協議下,在握手過程開始時通過客戶端告訴它正在連接的伺服器的主機名稱),並把幾百個弱密碼套件給列入 “黑名單” 了。

PS:相比 HTTP/1.1 中的並發連接方式,虛擬流的概念更優美地解決了 HTTP 的隊頭阻塞問題。

image


  • HTTP/3基於 Chrome 的 QUIC 協議,它也是 Chrome 推動的。
    • 先看看 QUIC

      • 基於 UDP 實現了可靠傳輸,引入了類似 HTTP/2 的流概念。

      • 內含了 TLS1.3,加快了建連速度。

      • 連接使用 “不透明” 的連接 ID 來標記兩端,而不再通過 IP 地址和端口綁定,從而支持用戶無感的連接遷移。

    • 回到 HTTP/3

      • 它最大的改變就是把下層的傳輸層協議從 TCP 換成了 QUIC,完全解決了 TCP 的隊頭阻塞問題(注意,是 TCP 的,不是 HTTP 的),在弱網環境下表現更好。因為 QUIC 本身就已經支持了加密、流和多路復用等能力,所以 HTTP/3 的工作減輕了很多。

      • 頭部壓縮算法從 HPACK 升級為 QPACK。

      • 2022 年 6 月 6 日,HTTP/3 被正式寫入 RFC 文檔,同時,HTTP/1.1 和 HTTP/2 也更新了 RFC 文檔。

PS:TCP 為了保證可靠傳輸,有個特別的 “丟包重傳” 機制,即丟失的包必須要等待重新傳輸確認,其他的包即使已經收到了,也只能放在緩衝區(kernel)裡,上層的應用(user)拿不出來,可參考下圖:紅色方塊請求是阻塞 TCP 的關鍵。

image

(其實這裡有一點疑惑的是,合著 HTTP/3 之前解決的都只是 kernel 到 user 的阻塞問題?具體地說只是經過 TCP 之後的阻塞問題,在這個階段的阻塞問題有哪些呢🤔?歡迎大佬們答疑解惑~)

HTTPS 的發展#

這部分聊聊 TLS1.2 到 TLS1.3 的發展,在此之前的版本因為各種安全問題都已經被廢棄了,我們可以從信息安全 | (加餐)互聯網時代,如何建立信任?這篇文章裡了解到。

對於 TLS1.3 來說,它的主要優化目標有 3 個:

  1. 兼容 TLS1.2。為了保證老設備能夠更輕鬆地升級協議,TLS1.3 保持原有的記錄格式不變,利用擴展協議在原有記錄末尾增加一些 “擴展字段” 來增加新的功能,老版本的 TLS 不認識它們可以直接忽略,從而實現了 “後向兼容”。

  2. 更安全。TLS1.3 基於安全考慮對支持的密碼套件進行了瘦身,最終只剩下 5 個密碼套件。前面提到的基於 RSA 的 TLS 傳統握手方式就被廢除了。

  3. 更高性能。HTTPS 建立連接的過程除了 TCP 握手,還有 TLS 握手,在 TLS1.2 中 TLS 握手需要花費 2-RTT,而這個時間在 TLS1.3 中被優化到了 1-RTT。怎麼做的呢?

    1. 答案就在上一點中,因為密碼套件只有這麼多,TLS1.3 可以索性在 ClientHello 消息中帶上所有支持的密碼套件相關參數,伺服端選定其中一種就可以直接生成通信密鑰進行加密通信了!而客戶端也省去了 TLS1.2 中要等伺服端確認密碼套件後再發參數的過程。

    2. 除了標準的 1-RTT 握手,TLS1.3 在之前建立過連接從而緩存了伺服端密碼套件參數的情況下,還可以達到 0-RTT 握手,但這也存在前向不安全和重放攻擊的問題,所以需要使用者去權衡。

    3. 下面我再放上《透析 HTTP 協議》裡的通信過程對比圖,你可以再看看它們的差異。

image


尾聲(好消息)#

好啦,今天就聊到這裡啦,回到我們的目標,看看你還能想到具體的知識嗎?

  1. 🔍 快速定位 HTTP 問題。

  2. 🥣 熟悉 HTTP 報文裡的常見頭字段。

  3. 🔐了解基本的加密知識。

當然,別忘了我們的終極目標🏁:如果你對 HTTP 感興趣,試著通過 WireShark、Chrome、Telnet 工具以及 RFC 文檔,去自行深入學習 HTTP 吧~


這裡是 Bo2SS,咱們下次再見!

插播一條好消息,經過一年多的佛系經營,在 2022 年 8 月 14 日 10 時 45 分,Bo2SS 的粉絲基數悄悄突破 500 啦🎉!大家快來幫忙想想怎麼慶祝(薅)一下 Bo2SS?歡迎投票,或在下方留言!

投票

A. 發發紅包,沾沾運氣

B. 贈送書本,汲取知識

C. 建交流群,促進友誼

D. 別玩了,繼續出文章

E. 都可以,不要白不要

F. 都不行,我去留言啦

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。