一 、基礎(chǔ)概念
請求和響應(yīng)報文
客戶端發(fā)送一個請求報文給服務(wù)器,服務(wù)器根據(jù)請求報文中的信息進行處理,并將處理結(jié)果放入響應(yīng)報文中返回給客戶端。
請求報文結(jié)構(gòu):
- 第一行是包含了請求方法、URL、協(xié)議版本;
- 接下來的多行都是請求首部 Header,每個首部都有一個首部名稱,以及對應(yīng)的值。
- 一個空行用來分隔首部和內(nèi)容主體 Body
- 最后是請求的內(nèi)容主體
GET http://www.example.com/ HTTP/1.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Cache-Control: max-age=0
Host: www.example.com
If-Modified-Since: Thu, 17 Oct 2019 07:18:26 GMT
If-None-Match: "3147526947+gzip"
Proxy-Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 xxx
param1=1¶m2=2
響應(yīng)報文結(jié)構(gòu):
- 第一行包含協(xié)議版本、狀態(tài)碼以及描述,最常見的是 200 OK 表示請求成功了
- 接下來多行也是首部內(nèi)容
- 一個空行分隔首部和內(nèi)容主體
- 最后是響應(yīng)的內(nèi)容主體
HTTP/1.1 200 OK
Age: 529651
Cache-Control: max-age=604800
Connection: keep-alive
Content-Encoding: gzip
Content-Length: 648
Content-Type: text/html; charset=UTF-8
Date: Mon, 02 Nov 2020 17:53:39 GMT
Etag: "3147526947+ident+gzip"
Expires: Mon, 09 Nov 2020 17:53:39 GMT
Keep-Alive: timeout=4
Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT
Proxy-Connection: keep-alive
Server: ECS (sjc/16DF)
Vary: Accept-Encoding
X-Cache: HIT
<!doctype html>
<html>
<head>
<title>Example Domain</title>
// 省略...
</body>
</html>
URL
HTTP 使用 URL( U niform Resource Locator,統(tǒng)一資源定位符)來定位資源,它是 URI(Uniform Resource Identifier,統(tǒng)一資源標(biāo)識符)的子集,URL 在 URI 的基礎(chǔ)上增加了定位能力。URI 除了包含 URL,還包含 URN(Uniform Resource Name,統(tǒng)一資源名稱),它只是用來定義一個資源的名稱,并不具備定位該資源的能力。例如 urn:isbn:0451450523 用來定義一個書籍名稱,但是卻沒有表示怎么找到這本書。
- wikipedia:統(tǒng)一資源標(biāo)志符
- wikipedia: URL
- rfc2616:3.2.2 http URL
- What is the difference between a URI, a URL and a URN?
二、HTTP 方法
客戶端發(fā)送的 請求報文 第一行為請求行,包含了方法字段。
GET
獲取資源
當(dāng)前網(wǎng)絡(luò)請求中,絕大部分使用的是 GET 方法。
HEAD
獲取報文首部
和 GET 方法類似,但是不返回報文實體主體部分。
主要用于確認(rèn) URL 的有效性以及資源更新的日期時間等。
POST
傳輸實體主體
POST 主要用來傳輸數(shù)據(jù),而 GET 主要用來獲取資源。
更多 POST 與 GET 的比較請見第九章。
PUT
上傳文件
由于自身不帶驗證機制,任何人都可以上傳文件,因此存在安全性問題,一般不使用該方法。
PUT /new.html HTTP/1.1
Host: example.com
Content-type: text/html
Content-length: 16
<p>New File</p>
PATCH
對資源進行部分修改
PUT 也可以用于修改資源,但是只能完全替代原始資源,PATCH 允許部分修改。
PATCH /file.txt HTTP/1.1
Host: www.example.com
Content-Type: application/example
If-Match: "e0023aa4e"
Content-Length: 100
[description of changes]
DELETE
刪除文件
與 PUT 功能相反,并且同樣不帶驗證機制。
DELETE /file.html HTTP/1.1
OPTIONS
查詢支持的方法
查詢指定的 URL 能夠支持的方法。
會返回 Allow: GET, POST, HEAD, OPTIONS 這樣的內(nèi)容。
CONNECT
要求在與代理服務(wù)器通信時建立隧道
使用 SSL(Secure Sockets Layer,安全套接層)和 TLS(Transport Layer Security,傳輸層安全)協(xié)議把通信內(nèi)容加密后經(jīng)網(wǎng)絡(luò)隧道傳輸。
CONNECT www.example.com:443 HTTP/1.1
[圖片上傳失敗...(image-914301-1606704618612)]
TRACE
追蹤路徑
服務(wù)器會將通信路徑返回給客戶端。
發(fā)送請求時,在 Max-Forwards 首部字段中填入數(shù)值,每經(jīng)過一個服務(wù)器就會減 1,當(dāng)數(shù)值為 0 時就停止傳輸。
通常不會使用 TRACE,并且它容易受到 XST 攻擊(Cross-Site Tracing,跨站追蹤)。
三、HTTP 狀態(tài)碼
服務(wù)器返回的 響應(yīng)報文 中第一行為狀態(tài)行,包含了狀態(tài)碼以及原因短語,用來告知客戶端請求的結(jié)果。
| 狀態(tài)碼 | 類別 | 含義 |
|---|---|---|
| 1XX | Informational(信息性狀態(tài)碼) | 接收的請求正在處理 |
| 2XX | Success(成功狀態(tài)碼) | 請求正常處理完畢 |
| 3XX | Redirection(重定向狀態(tài)碼) | 需要進行附加操作以完成請求 |
| 4XX | Client Error(客戶端錯誤狀態(tài)碼) | 服務(wù)器無法處理請求 |
| 5XX | Server Error(服務(wù)器錯誤狀態(tài)碼) | 服務(wù)器處理請求出錯 |
1XX 信息
- 100 Continue :表明到目前為止都很正常,客戶端可以繼續(xù)發(fā)送請求或者忽略這個響應(yīng)。
2XX 成功
200 OK
204 No Content :請求已經(jīng)成功處理,但是返回的響應(yīng)報文不包含實體的主體部分。一般在只需要從客戶端往服務(wù)器發(fā)送信息,而不需要返回數(shù)據(jù)時使用。
206 Partial Content :表示客戶端進行了范圍請求,響應(yīng)報文包含由 Content-Range 指定范圍的實體內(nèi)容。
3XX 重定向
301 Moved Permanently :永久性重定向
302 Found :臨時性重定向
303 See Other :和 302 有著相同的功能,但是 303 明確要求客戶端應(yīng)該采用 GET 方法獲取資源。
注:雖然 HTTP 協(xié)議規(guī)定 301、302 狀態(tài)下重定向時不允許把 POST 方法改成 GET 方法,但是大多數(shù)瀏覽器都會在 301、302 和 303 狀態(tài)下的重定向把 POST 方法改成 GET 方法。
304 Not Modified :如果請求報文首部包含一些條件,例如:If-Match,If-Modified-Since,If-None-Match,If-Range,If-Unmodified-Since,如果不滿足條件,則服務(wù)器會返回 304 狀態(tài)碼。
307 Temporary Redirect :臨時重定向,與 302 的含義類似,但是 307 要求瀏覽器不會把重定向請求的 POST 方法改成 GET 方法。
4XX 客戶端錯誤
400 Bad Request :請求報文中存在語法錯誤。
401 Unauthorized :該狀態(tài)碼表示發(fā)送的請求需要有認(rèn)證信息(BASIC 認(rèn)證、DIGEST 認(rèn)證)。如果之前已進行過一次請求,則表示用戶認(rèn)證失敗。
403 Forbidden :請求被拒絕。
404 Not Found
5XX 服務(wù)器錯誤
500 Internal Server Error :服務(wù)器正在執(zhí)行請求時發(fā)生錯誤。
503 Service Unavailable :服務(wù)器暫時處于超負(fù)載或正在進行停機維護,現(xiàn)在無法處理請求。
四、HTTP 首部
有 4 種類型的首部字段:通用首部字段、請求首部字段、響應(yīng)首部字段和實體首部字段。
各種首部字段及其含義如下(不需要全記,僅供查閱):
通用首部字段
| 首部字段名 | 說明 |
|---|---|
| Cache-Control | 控制緩存的行為 |
| Connection | 控制不再轉(zhuǎn)發(fā)給代理的首部字段、管理持久連接 |
| Date | 創(chuàng)建報文的日期時間 |
| Pragma | 報文指令 |
| Trailer | 報文末端的首部一覽 |
| Transfer-Encoding | 指定報文主體的傳輸編碼方式 |
| Upgrade | 升級為其他協(xié)議 |
| Via | 代理服務(wù)器的相關(guān)信息 |
| Warning | 錯誤通知 |
請求首部字段
| 首部字段名 | 說明 |
|---|---|
| Accept | 用戶代理可處理的媒體類型 |
| Accept-Charset | 優(yōu)先的字符集 |
| Accept-Encoding | 優(yōu)先的內(nèi)容編碼 |
| Accept-Language | 優(yōu)先的語言(自然語言) |
| Authorization | Web 認(rèn)證信息 |
| Expect | 期待服務(wù)器的特定行為 |
| From | 用戶的電子郵箱地址 |
| Host | 請求資源所在服務(wù)器 |
| If-Match | 比較實體標(biāo)記(ETag) |
| If-Modified-Since | 比較資源的更新時間 |
| If-None-Match | 比較實體標(biāo)記(與 If-Match 相反) |
| If-Range | 資源未更新時發(fā)送實體 Byte 的范圍請求 |
| If-Unmodified-Since | 比較資源的更新時間(與 If-Modified-Since 相反) |
| Max-Forwards | 最大傳輸逐跳數(shù) |
| Proxy-Authorization | 代理服務(wù)器要求客戶端的認(rèn)證信息 |
| Range | 實體的字節(jié)范圍請求 |
| Referer | 對請求中 URI 的原始獲取方 |
| TE | 傳輸編碼的優(yōu)先級 |
| User-Agent | HTTP 客戶端程序的信息 |
響應(yīng)首部字段
| 首部字段名 | 說明 |
|---|---|
| Accept-Ranges | 是否接受字節(jié)范圍請求 |
| Age | 推算資源創(chuàng)建經(jīng)過時間 |
| ETag | 資源的匹配信息 |
| Location | 令客戶端重定向至指定 URI |
| Proxy-Authenticate | 代理服務(wù)器對客戶端的認(rèn)證信息 |
| Retry-After | 對再次發(fā)起請求的時機要求 |
| Server | HTTP 服務(wù)器的安裝信息 |
| Vary | 代理服務(wù)器緩存的管理信息 |
| WWW-Authenticate | 服務(wù)器對客戶端的認(rèn)證信息 |
實體首部字段
| 首部字段名 | 說明 |
|---|---|
| Allow | 資源可支持的 HTTP 方法 |
| Content-Encoding | 實體主體適用的編碼方式 |
| Content-Language | 實體主體的自然語言 |
| Content-Length | 實體主體的大小 |
| Content-Location | 替代對應(yīng)資源的 URI |
| Content-MD5 | 實體主體的報文摘要 |
| Content-Range | 實體主體的位置范圍 |
| Content-Type | 實體主體的媒體類型 |
| Expires | 實體主體過期的日期時間 |
| Last-Modified | 資源的最后修改日期時間 |
五、具體應(yīng)用
連接管理
1. 短連接與長連接
當(dāng)瀏覽器訪問一個包含多張圖片的 HTML 頁面時,除了請求訪問的 HTML 頁面資源,還會請求圖片資源。如果每進行一次 HTTP 通信就要新建一個 TCP 連接,那么開銷會很大。
長連接只需要建立一次 TCP 連接就能進行多次 HTTP 通信。
- 從 HTTP/1.1 開始默認(rèn)是長連接的,如果要斷開連接,需要由客戶端或者服務(wù)器端提出斷開,使用
Connection : close; - 在 HTTP/1.1 之前默認(rèn)是短連接的,如果需要使用長連接,則使用
Connection : Keep-Alive。
2. 流水線
默認(rèn)情況下,HTTP 請求是按順序發(fā)出的,下一個請求只有在當(dāng)前請求收到響應(yīng)之后才會被發(fā)出。由于受到網(wǎng)絡(luò)延遲和帶寬的限制,在下一個請求被發(fā)送到服務(wù)器之前,可能需要等待很長時間。
流水線是在同一條長連接上連續(xù)發(fā)出請求,而不用等待響應(yīng)返回,這樣可以減少延遲。
Cookie
HTTP 協(xié)議是無狀態(tài)的,主要是為了讓 HTTP 協(xié)議盡可能簡單,使得它能夠處理大量事務(wù)。HTTP/1.1 引入 Cookie 來保存狀態(tài)信息。
Cookie 是服務(wù)器發(fā)送到用戶瀏覽器并保存在本地的一小塊數(shù)據(jù),它會在瀏覽器之后向同一服務(wù)器再次發(fā)起請求時被攜帶上,用于告知服務(wù)端兩個請求是否來自同一瀏覽器。由于之后每次請求都會需要攜帶 Cookie 數(shù)據(jù),因此會帶來額外的性能開銷(尤其是在移動環(huán)境下)。
Cookie 曾一度用于客戶端數(shù)據(jù)的存儲,因為當(dāng)時并沒有其它合適的存儲辦法而作為唯一的存儲手段,但現(xiàn)在隨著現(xiàn)代瀏覽器開始支持各種各樣的存儲方式,Cookie 漸漸被淘汰。新的瀏覽器 API 已經(jīng)允許開發(fā)者直接將數(shù)據(jù)存儲到本地,如使用 Web storage API(本地存儲和會話存儲)或 IndexedDB。
1. 用途
- 會話狀態(tài)管理(如用戶登錄狀態(tài)、購物車、游戲分?jǐn)?shù)或其它需要記錄的信息)
- 個性化設(shè)置(如用戶自定義設(shè)置、主題等)
- 瀏覽器行為跟蹤(如跟蹤分析用戶行為等)
2. 創(chuàng)建過程
服務(wù)器發(fā)送的響應(yīng)報文包含 Set-Cookie 首部字段,客戶端得到響應(yīng)報文后把 Cookie 內(nèi)容保存到瀏覽器中。
HTTP/1.0 200 OK
Content-type: text/html
Set-Cookie: yummy_cookie=choco
Set-Cookie: tasty_cookie=strawberry
[page content]
客戶端之后對同一個服務(wù)器發(fā)送請求時,會從瀏覽器中取出 Cookie 信息并通過 Cookie 請求首部字段發(fā)送給服務(wù)器。
GET /sample_page.html HTTP/1.1
Host: www.example.org
Cookie: yummy_cookie=choco; tasty_cookie=strawberry
3. 分類
- 會話期 Cookie:瀏覽器關(guān)閉之后它會被自動刪除,也就是說它僅在會話期內(nèi)有效。
- 持久性 Cookie:指定過期時間(Expires)或有效期(max-age)之后就成為了持久性的 Cookie。
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT;
4. 作用域
Domain 標(biāo)識指定了哪些主機可以接受 Cookie。如果不指定,默認(rèn)為當(dāng)前文檔的主機(不包含子域名)。如果指定了 Domain,則一般包含子域名。例如,如果設(shè)置 Domain=mozilla.org,則 Cookie 也包含在子域名中(如 developer.mozilla.org)。
Path 標(biāo)識指定了主機下的哪些路徑可以接受 Cookie(該 URL 路徑必須存在于請求 URL 中)。以字符 %x2F ("/") 作為路徑分隔符,子路徑也會被匹配。例如,設(shè)置 Path=/docs,則以下地址都會匹配:
- /docs
- /docs/Web/
- /docs/Web/HTTP
5. JavaScript
瀏覽器通過 document.cookie 屬性可創(chuàng)建新的 Cookie,也可通過該屬性訪問非 HttpOnly 標(biāo)記的 Cookie。
document.cookie = "yummy_cookie=choco";
document.cookie = "tasty_cookie=strawberry";
console.log(document.cookie);
6. HttpOnly
標(biāo)記為 HttpOnly 的 Cookie 不能被 JavaScript 腳本調(diào)用。跨站腳本攻擊 (XSS) 常常使用 JavaScript 的 document.cookie API 竊取用戶的 Cookie 信息,因此使用 HttpOnly 標(biāo)記可以在一定程度上避免 XSS 攻擊。
Set-Cookie: id=a3fWa; Expires=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly
7. Secure
標(biāo)記為 Secure 的 Cookie 只能通過被 HTTPS 協(xié)議加密過的請求發(fā)送給服務(wù)端。但即便設(shè)置了 Secure 標(biāo)記,敏感信息也不應(yīng)該通過 Cookie 傳輸,因為 Cookie 有其固有的不安全性,Secure 標(biāo)記也無法提供確實的安全保障。
8. Session
除了可以將用戶信息通過 Cookie 存儲在用戶瀏覽器中,也可以利用 Session 存儲在服務(wù)器端,存儲在服務(wù)器端的信息更加安全。
Session 可以存儲在服務(wù)器上的文件、數(shù)據(jù)庫或者內(nèi)存中。也可以將 Session 存儲在 Redis 這種內(nèi)存型數(shù)據(jù)庫中,效率會更高。
使用 Session 維護用戶登錄狀態(tài)的過程如下:
- 用戶進行登錄時,用戶提交包含用戶名和密碼的表單,放入 HTTP 請求報文中;
- 服務(wù)器驗證該用戶名和密碼,如果正確則把用戶信息存儲到 Redis 中,它在 Redis 中的 Key 稱為 Session ID;
- 服務(wù)器返回的響應(yīng)報文的 Set-Cookie 首部字段包含了這個 Session ID,客戶端收到響應(yīng)報文之后將該 Cookie 值存入瀏覽器中;
- 客戶端之后對同一個服務(wù)器進行請求時會包含該 Cookie 值,服務(wù)器收到之后提取出 Session ID,從 Redis 中取出用戶信息,繼續(xù)之前的業(yè)務(wù)操作。
應(yīng)該注意 Session ID 的安全性問題,不能讓它被惡意攻擊者輕易獲取,那么就不能產(chǎn)生一個容易被猜到的 Session ID 值。此外,還需要經(jīng)常重新生成 Session ID。在對安全性要求極高的場景下,例如轉(zhuǎn)賬等操作,除了使用 Session 管理用戶狀態(tài)之外,還需要對用戶進行重新驗證,比如重新輸入密碼,或者使用短信驗證碼等方式。
9. 瀏覽器禁用 Cookie
此時無法使用 Cookie 來保存用戶信息,只能使用 Session。除此之外,不能再將 Session ID 存放到 Cookie 中,而是使用 URL 重寫技術(shù),將 Session ID 作為 URL 的參數(shù)進行傳遞。
10. Cookie 與 Session 選擇
- Cookie 只能存儲 ASCII 碼字符串,而 Session 則可以存儲任何類型的數(shù)據(jù),因此在考慮數(shù)據(jù)復(fù)雜性時首選 Session;
- Cookie 存儲在瀏覽器中,容易被惡意查看。如果非要將一些隱私數(shù)據(jù)存在 Cookie 中,可以將 Cookie 值進行加密,然后在服務(wù)器進行解密;
- 對于大型網(wǎng)站,如果用戶所有的信息都存儲在 Session 中,那么開銷是非常大的,因此不建議將所有的用戶信息都存儲到 Session 中。
緩存
1. 優(yōu)點
- 緩解服務(wù)器壓力;
- 降低客戶端獲取資源的延遲:緩存通常位于內(nèi)存中,讀取緩存的速度更快。并且緩存服務(wù)器在地理位置上也有可能比源服務(wù)器來得近,例如瀏覽器緩存。
2. 實現(xiàn)方法
- 讓代理服務(wù)器進行緩存;
- 讓客戶端瀏覽器進行緩存。
3. Cache-Control
HTTP/1.1 通過 Cache-Control 首部字段來控制緩存。
3.1 禁止進行緩存
no-store 指令規(guī)定不能對請求或響應(yīng)的任何一部分進行緩存。
Cache-Control: no-store
3.2 強制確認(rèn)緩存
no-cache 指令規(guī)定緩存服務(wù)器需要先向源服務(wù)器驗證緩存資源的有效性,只有當(dāng)緩存資源有效時才能使用該緩存對客戶端的請求進行響應(yīng)。
Cache-Control: no-cache
3.3 私有緩存和公共緩存
private 指令規(guī)定了將資源作為私有緩存,只能被單獨用戶使用,一般存儲在用戶瀏覽器中。
Cache-Control: private
public 指令規(guī)定了將資源作為公共緩存,可以被多個用戶使用,一般存儲在代理服務(wù)器中。
Cache-Control: public
3.4 緩存過期機制
max-age 指令出現(xiàn)在請求報文,并且緩存資源的緩存時間小于該指令指定的時間,那么就能接受該緩存。
max-age 指令出現(xiàn)在響應(yīng)報文,表示緩存資源在緩存服務(wù)器中保存的時間。
Cache-Control: max-age=31536000
Expires 首部字段也可以用于告知緩存服務(wù)器該資源什么時候會過期。
Expires: Wed, 04 Jul 2012 08:26:05 GMT
- 在 HTTP/1.1 中,會優(yōu)先處理 max-age 指令;
- 在 HTTP/1.0 中,max-age 指令會被忽略掉。
4. 緩存驗證
需要先了解 ETag 首部字段的含義,它是資源的唯一標(biāo)識。URL 不能唯一表示資源,例如 http://www.google.com/ 有中文和英文兩個資源,只有 ETag 才能對這兩個資源進行唯一標(biāo)識。
ETag: "82e22293907ce725faf67773957acd12"
可以將緩存資源的 ETag 值放入 If-None-Match 首部,服務(wù)器收到該請求后,判斷緩存資源的 ETag 值和資源的最新 ETag 值是否一致,如果一致則表示緩存資源有效,返回 304 Not Modified。
If-None-Match: "82e22293907ce725faf67773957acd12"
Last-Modified 首部字段也可以用于緩存驗證,它包含在源服務(wù)器發(fā)送的響應(yīng)報文中,指示源服務(wù)器對資源的最后修改時間。但是它是一種弱校驗器,因為只能精確到一秒,所以它通常作為 ETag 的備用方案。如果響應(yīng)首部字段里含有這個信息,客戶端可以在后續(xù)的請求中帶上 If-Modified-Since 來驗證緩存。服務(wù)器只在所請求的資源在給定的日期時間之后對內(nèi)容進行過修改的情況下才會將資源返回,狀態(tài)碼為 200 OK。如果請求的資源從那時起未經(jīng)修改,那么返回一個不帶有實體主體的 304 Not Modified 響應(yīng)報文。
Last-Modified: Wed, 21 Oct 2015 07:28:00 GMT
If-Modified-Since: Wed, 21 Oct 2015 07:28:00 GMT
內(nèi)容協(xié)商
通過內(nèi)容協(xié)商返回最合適的內(nèi)容,例如根據(jù)瀏覽器的默認(rèn)語言選擇返回中文界面還是英文界面。
1. 類型
1.1 服務(wù)端驅(qū)動型
客戶端設(shè)置特定的 HTTP 首部字段,例如 Accept、Accept-Charset、Accept-Encoding、Accept-Language,服務(wù)器根據(jù)這些字段返回特定的資源。
它存在以下問題:
- 服務(wù)器很難知道客戶端瀏覽器的全部信息;
- 客戶端提供的信息相當(dāng)冗長(HTTP/2 協(xié)議的首部壓縮機制緩解了這個問題),并且存在隱私風(fēng)險(HTTP 指紋識別技術(shù));
- 給定的資源需要返回不同的展現(xiàn)形式,共享緩存的效率會降低,而服務(wù)器端的實現(xiàn)會越來越復(fù)雜。
1.2 代理驅(qū)動型
服務(wù)器返回 300 Multiple Choices 或者 406 Not Acceptable,客戶端從中選出最合適的那個資源。
2. Vary
Vary: Accept-Language
在使用內(nèi)容協(xié)商的情況下,只有當(dāng)緩存服務(wù)器中的緩存滿足內(nèi)容協(xié)商條件時,才能使用該緩存,否則應(yīng)該向源服務(wù)器請求該資源。
例如,一個客戶端發(fā)送了一個包含 Accept-Language 首部字段的請求之后,源服務(wù)器返回的響應(yīng)包含 Vary: Accept-Language 內(nèi)容,緩存服務(wù)器對這個響應(yīng)進行緩存之后,在客戶端下一次訪問同一個 URL 資源,并且 Accept-Language 與緩存中的對應(yīng)的值相同時才會返回該緩存。
內(nèi)容編碼
內(nèi)容編碼將實體主體進行壓縮,從而減少傳輸?shù)臄?shù)據(jù)量。
常用的內(nèi)容編碼有:gzip、compress、deflate、identity。
瀏覽器發(fā)送 Accept-Encoding 首部,其中包含有它所支持的壓縮算法,以及各自的優(yōu)先級。服務(wù)器則從中選擇一種,使用該算法對響應(yīng)的消息主體進行壓縮,并且發(fā)送 Content-Encoding 首部來告知瀏覽器它選擇了哪一種算法。由于該內(nèi)容協(xié)商過程是基于編碼類型來選擇資源的展現(xiàn)形式的,響應(yīng)報文的 Vary 首部字段至少要包含 Content-Encoding。
范圍請求
如果網(wǎng)絡(luò)出現(xiàn)中斷,服務(wù)器只發(fā)送了一部分?jǐn)?shù)據(jù),范圍請求可以使得客戶端只請求服務(wù)器未發(fā)送的那部分?jǐn)?shù)據(jù),從而避免服務(wù)器重新發(fā)送所有數(shù)據(jù)。
1. Range
在請求報文中添加 Range 首部字段指定請求的范圍。
GET /z4d4kWk.jpg HTTP/1.1
Host: i.imgur.com
Range: bytes=0-1023
請求成功的話服務(wù)器返回的響應(yīng)包含 206 Partial Content 狀態(tài)碼。
HTTP/1.1 206 Partial Content
Content-Range: bytes 0-1023/146515
Content-Length: 1024
...
(binary content)
2. Accept-Ranges
響應(yīng)首部字段 Accept-Ranges 用于告知客戶端是否能處理范圍請求,可以處理使用 bytes,否則使用 none。
Accept-Ranges: bytes
3. 響應(yīng)狀態(tài)碼
- 在請求成功的情況下,服務(wù)器會返回 206 Partial Content 狀態(tài)碼。
- 在請求的范圍越界的情況下,服務(wù)器會返回 416 Requested Range Not Satisfiable 狀態(tài)碼。
- 在不支持范圍請求的情況下,服務(wù)器會返回 200 OK 狀態(tài)碼。
分塊傳輸編碼
Chunked Transfer Encoding,可以把數(shù)據(jù)分割成多塊,讓瀏覽器逐步顯示頁面。
多部分對象集合
一份報文主體內(nèi)可含有多種類型的實體同時發(fā)送,每個部分之間用 boundary 字段定義的分隔符進行分隔,每個部分都可以有首部字段。
例如,上傳多個表單時可以使用如下方式:
Content-Type: multipart/form-data; boundary=AaB03x
--AaB03x
Content-Disposition: form-data; name="submit-name"
Larry
--AaB03x
Content-Disposition: form-data; name="files"; filename="file1.txt"
Content-Type: text/plain
... contents of file1.txt ...
--AaB03x--
虛擬主機
HTTP/1.1 使用虛擬主機技術(shù),使得一臺服務(wù)器擁有多個域名,并且在邏輯上可以看成多個服務(wù)器。
通信數(shù)據(jù)轉(zhuǎn)發(fā)
1. 代理
代理服務(wù)器接受客戶端的請求,并且轉(zhuǎn)發(fā)給其它服務(wù)器。
使用代理的主要目的是:
- 緩存
- 負(fù)載均衡
- 網(wǎng)絡(luò)訪問控制
- 訪問日志記錄
代理服務(wù)器分為正向代理和反向代理兩種:
- 用戶察覺得到正向代理的存在。
- 而反向代理一般位于內(nèi)部網(wǎng)絡(luò)中,用戶察覺不到。
2. 網(wǎng)關(guān)
與代理服務(wù)器不同的是,網(wǎng)關(guān)服務(wù)器會將 HTTP 轉(zhuǎn)化為其它協(xié)議進行通信,從而請求其它非 HTTP 服務(wù)器的服務(wù)。
3. 隧道
使用 SSL 等加密手段,在客戶端和服務(wù)器之間建立一條安全的通信線路。
六、HTTPS
HTTP 有以下安全性問題:
- 使用明文進行通信,內(nèi)容可能會被竊聽;
- 不驗證通信方的身份,通信方的身份有可能遭遇偽裝;
- 無法證明報文的完整性,報文有可能遭篡改。
HTTPS 并不是新協(xié)議,而是讓 HTTP 先和 SSL(Secure Sockets Layer)通信,再由 SSL 和 TCP 通信,也就是說 HTTPS 使用了隧道進行通信。
通過使用 SSL,HTTPS 具有了加密(防竊聽)、認(rèn)證(防偽裝)和完整性保護(防篡改)。
[圖片上傳失敗...(image-88f5ed-1606704618611)]
加密
1. 對稱密鑰加密
對稱密鑰加密(Symmetric-Key Encryption),加密和解密使用同一密鑰。
- 優(yōu)點:運算速度快;
- 缺點:無法安全地將密鑰傳輸給通信方。
[圖片上傳失敗...(image-808052-1606704618611)]
2.非對稱密鑰加密
非對稱密鑰加密,又稱公開密鑰加密(Public-Key Encryption),加密和解密使用不同的密鑰。
公開密鑰所有人都可以獲得,通信發(fā)送方獲得接收方的公開密鑰之后,就可以使用公開密鑰進行加密,接收方收到通信內(nèi)容后使用私有密鑰解密。
非對稱密鑰除了用來加密,還可以用來進行簽名。因為私有密鑰無法被其他人獲取,因此通信發(fā)送方使用其私有密鑰進行簽名,通信接收方使用發(fā)送方的公開密鑰對簽名進行解密,就能判斷這個簽名是否正確。
- 優(yōu)點:可以更安全地將公開密鑰傳輸給通信發(fā)送方;
- 缺點:運算速度慢。
3. HTTPS 采用的加密方式
上面提到對稱密鑰加密方式的傳輸效率更高,但是無法安全地將密鑰 Secret Key 傳輸給通信方。而非對稱密鑰加密方式可以保證傳輸?shù)陌踩裕虼宋覀兛梢岳梅菍ΨQ密鑰加密方式將 Secret Key 傳輸給通信方。HTTPS 采用混合的加密機制,正是利用了上面提到的方案:
- 使用非對稱密鑰加密方式,傳輸對稱密鑰加密方式所需要的 Secret Key,從而保證安全性;
- 獲取到 Secret Key 后,再使用對稱密鑰加密方式進行通信,從而保證效率。(下圖中的 Session Key 就是 Secret Key)
[圖片上傳失敗...(image-2ad650-1606704618611)]
認(rèn)證
通過使用 證書 來對通信方進行認(rèn)證。
數(shù)字證書認(rèn)證機構(gòu)(CA,Certificate Authority)是客戶端與服務(wù)器雙方都可信賴的第三方機構(gòu)。
服務(wù)器的運營人員向 CA 提出公開密鑰的申請,CA 在判明提出申請者的身份之后,會對已申請的公開密鑰做數(shù)字簽名,然后分配這個已簽名的公開密鑰,并將該公開密鑰放入公開密鑰證書后綁定在一起。
進行 HTTPS 通信時,服務(wù)器會把證書發(fā)送給客戶端。客戶端取得其中的公開密鑰之后,先使用數(shù)字簽名進行驗證,如果驗證通過,就可以開始通信了。
[圖片上傳失敗...(image-f2c0e0-1606704618611)]
完整性保護
SSL 提供報文摘要功能來進行完整性保護。
HTTP 也提供了 MD5 報文摘要功能,但不是安全的。例如報文內(nèi)容被篡改之后,同時重新計算 MD5 的值,通信接收方是無法意識到發(fā)生了篡改。
HTTPS 的報文摘要功能之所以安全,是因為它結(jié)合了加密和認(rèn)證這兩個操作。試想一下,加密之后的報文,遭到篡改之后,也很難重新計算報文摘要,因為無法輕易獲取明文。
HTTPS 的缺點
- 因為需要進行加密解密等過程,因此速度會更慢;
- 需要支付證書授權(quán)的高額費用。
七、HTTP/2.0
HTTP/1.x 缺陷
HTTP/1.x 實現(xiàn)簡單是以犧牲性能為代價的:
- 客戶端需要使用多個連接才能實現(xiàn)并發(fā)和縮短延遲;
- 不會壓縮請求和響應(yīng)首部,從而導(dǎo)致不必要的網(wǎng)絡(luò)流量;
- 不支持有效的資源優(yōu)先級,致使底層 TCP 連接的利用率低下。
二進制分幀層
HTTP/2.0 將報文分成 HEADERS 幀和 DATA 幀,它們都是二進制格式的。
[圖片上傳失敗...(image-c33985-1606704618611)]
在通信過程中,只會有一個 TCP 連接存在,它承載了任意數(shù)量的雙向數(shù)據(jù)流(Stream)。
- 一個數(shù)據(jù)流(Stream)都有一個唯一標(biāo)識符和可選的優(yōu)先級信息,用于承載雙向信息。
- 消息(Message)是與邏輯請求或響應(yīng)對應(yīng)的完整的一系列幀。
- 幀(Frame)是最小的通信單位,來自不同數(shù)據(jù)流的幀可以交錯發(fā)送,然后再根據(jù)每個幀頭的數(shù)據(jù)流標(biāo)識符重新組裝。
[圖片上傳失敗...(image-46deec-1606704618611)]
服務(wù)端推送
HTTP/2.0 在客戶端請求一個資源時,會把相關(guān)的資源一起發(fā)送給客戶端,客戶端就不需要再次發(fā)起請求了。例如客戶端請求 page.html 頁面,服務(wù)端就把 script.js 和 style.css 等與之相關(guān)的資源一起發(fā)給客戶端。
[圖片上傳失敗...(image-743ab9-1606704618611)]
首部壓縮
HTTP/1.1 的首部帶有大量信息,而且每次都要重復(fù)發(fā)送。
HTTP/2.0 要求客戶端和服務(wù)器同時維護和更新一個包含之前見過的首部字段表,從而避免了重復(fù)傳輸。
不僅如此,HTTP/2.0 也使用 Huffman 編碼對首部字段進行壓縮。
八、HTTP/1.1 新特性
詳細(xì)內(nèi)容請見上文
- 默認(rèn)是長連接
- 支持流水線
- 支持同時打開多個 TCP 連接
- 支持虛擬主機
- 新增狀態(tài)碼 100
- 支持分塊傳輸編碼
- 新增緩存處理指令 max-age
九、GET 和 POST 比較
作用
GET 用于獲取資源,而 POST 用于傳輸實體主體。
參數(shù)
GET 和 POST 的請求都能使用額外的參數(shù),但是 GET 的參數(shù)是以查詢字符串出現(xiàn)在 URL 中,而 POST 的參數(shù)存儲在實體主體中。不能因為 POST 參數(shù)存儲在實體主體中就認(rèn)為它的安全性更高,因為照樣可以通過一些抓包工具(Fiddler)查看。
因為 URL 只支持 ASCII 碼,因此 GET 的參數(shù)中如果存在中文等字符就需要先進行編碼。例如 中文 會轉(zhuǎn)換為 %E4%B8%AD%E6%96%87,而空格會轉(zhuǎn)換為 %20。POST 參數(shù)支持標(biāo)準(zhǔn)字符集。
GET /test/demo_form.asp?name1=value1&name2=value2 HTTP/1.1
POST /test/demo_form.asp HTTP/1.1
Host: w3schools.com
name1=value1&name2=value2
安全
安全的 HTTP 方法不會改變服務(wù)器狀態(tài),也就是說它只是可讀的。
GET 方法是安全的,而 POST 卻不是,因為 POST 的目的是傳送實體主體內(nèi)容,這個內(nèi)容可能是用戶上傳的表單數(shù)據(jù),上傳成功之后,服務(wù)器可能把這個數(shù)據(jù)存儲到數(shù)據(jù)庫中,因此狀態(tài)也就發(fā)生了改變。
安全的方法除了 GET 之外還有:HEAD、OPTIONS。
不安全的方法除了 POST 之外還有 PUT、DELETE。
冪等性
冪等的 HTTP 方法,同樣的請求被執(zhí)行一次與連續(xù)執(zhí)行多次的效果是一樣的,服務(wù)器的狀態(tài)也是一樣的。換句話說就是,冪等方法不應(yīng)該具有副作用(統(tǒng)計用途除外)。
所有的安全方法也都是冪等的。
在正確實現(xiàn)的條件下,GET,HEAD,PUT 和 DELETE 等方法都是冪等的,而 POST 方法不是。
GET /pageX HTTP/1.1 是冪等的,連續(xù)調(diào)用多次,客戶端接收到的結(jié)果都是一樣的:
GET /pageX HTTP/1.1
GET /pageX HTTP/1.1
GET /pageX HTTP/1.1
GET /pageX HTTP/1.1
POST /add_row HTTP/1.1 不是冪等的,如果調(diào)用多次,就會增加多行記錄:
POST /add_row HTTP/1.1 -> Adds a 1nd row
POST /add_row HTTP/1.1 -> Adds a 2nd row
POST /add_row HTTP/1.1 -> Adds a 3rd row
DELETE /idX/delete HTTP/1.1 是冪等的,即使不同的請求接收到的狀態(tài)碼不一樣:
DELETE /idX/delete HTTP/1.1 -> Returns 200 if idX exists
DELETE /idX/delete HTTP/1.1 -> Returns 404 as it just got deleted
DELETE /idX/delete HTTP/1.1 -> Returns 404
可緩存
如果要對響應(yīng)進行緩存,需要滿足以下條件:
- 請求報文的 HTTP 方法本身是可緩存的,包括 GET 和 HEAD,但是 PUT 和 DELETE 不可緩存,POST 在多數(shù)情況下不可緩存的。
- 響應(yīng)報文的狀態(tài)碼是可緩存的,包括:200, 203, 204, 206, 300, 301, 404, 405, 410, 414, and 501。
- 響應(yīng)報文的 Cache-Control 首部字段沒有指定不進行緩存。
XMLHttpRequest
為了闡述 POST 和 GET 的另一個區(qū)別,需要先了解 XMLHttpRequest:
XMLHttpRequest 是一個 API,它為客戶端提供了在客戶端和服務(wù)器之間傳輸數(shù)據(jù)的功能。它提供了一個通過 URL 來獲取數(shù)據(jù)的簡單方式,并且不會使整個頁面刷新。這使得網(wǎng)頁只更新一部分頁面而不會打擾到用戶。XMLHttpRequest 在 AJAX 中被大量使用。
- 在使用 XMLHttpRequest 的 POST 方法時,瀏覽器會先發(fā)送 Header 再發(fā)送 Data。但并不是所有瀏覽器會這么做,例如火狐就不會。
- 而 GET 方法 Header 和 Data 會一起發(fā)送。
轉(zhuǎn)自:https://github.com/CyC2018/CS-Notes/blob/master/notes/HTTP.md