一、HTTP 請求流程
最初,HTTP 協定的出現主要是為了解決文本傳輸的難題,由于協定本身非常簡單,于是在此基礎上設想了很多應用方法并投入了實際使用。現在 HTTP 協定已經超出了 Web 這個架構的局限,被運用到了各種場景裡。
目前主流的 HTTP 版本還是 HTTP/1.1。
HTTP 協定基于 TCP/IP 協定,會通過分層順序與對方進行通信。首先作為發送端的用戶端在應用層(HTTP 協定)發出一個想看某個 Web 頁面的 HTTP 請求;接着,在傳輸層(TCP 協定)把從應用層處收到的資料(HTTP 請求封包)進行分割,并在各個封包上打上标記序号及端口号轉發給網絡層;然後,在網絡層(IP 協定)增加作為通信目的地的 MAC 位址(ARP 協定)後轉發給鍊路層;最後接收端的伺服器在鍊路層接收到資料,按序往上層發送,一直到應用層。

TCP 協定提供可靠的位元組流服務,并且為了更容易傳送大資料進而把資料分割,而且 TCP 協定能夠确認資料最終是否送達到對方(三次握手)。
IP(Internet Protocol)協定的作用是把各種資料包傳送給對方。
ARP 是一種用以解析位址的協定,根據通信方的 IP 位址就可以反查出對應的 MAC 位址。
DNS 提供域名到 IP 位址之間的解析服務。
二、HTTP 協定結構
1.請求封包
請求封包是由請求方法、請求 URI、協定版本、可選的請求首部字段和内容實體構成的。
表示隻要用戶端或服務端任意一端沒有明确提出斷開連接配接,則保持 TCP 連接配接狀态。這樣不會導緻每次的請求造成無謂的 TCP 連接配接建立和斷開,進而減少通信量的開銷和服務端的負載,也能使頁面的顯示速度顯著提高。
除了常見的 get/post 方法,HTTP/1.1 中還有諸多可使用的方法。
HTTP 方法 | 作用 | 說明 |
---|---|---|
GET | 擷取資源 | 用來請求通路已被 URI 識别的資源 |
POST | 傳輸實體主體 | 用來傳輸實體的主體 |
PUT | 傳輸檔案 | 自身不帶驗證機制,存在安全性問題 |
HEAD | 獲得封包首部 | 不傳回封包主體部分。用于确認 URI 的有效性及資源更新的日期時間等 |
DELETE | 删除檔案 | |
OPTIONS | 詢問支援的方法 | 用來查詢針對請求 URI 指定的資源支援的方法 |
TRACE | 追蹤路徑 | 容易引發 XST(Cross-Site Tracing, 跨站追蹤)攻擊,不建議使用 |
CONNECT | 用隧道協定連接配接代理 | 代理伺服器通信時建立隧道,實作用隧道協定進行 TCP 通信 |
TRACE 方法是讓 Web 伺服器端将之前的請求通信環回給用戶端的方法。發送請求時,在 Max-Forwards 首部字段中填入數值,每經過一個伺服器端就将該數字減 1,當數值剛好減到 0 時, 就停止繼續傳輸,最後接收到請求的伺服器端則傳回狀态碼 200 OK 的響應。
CONNECT 方法要求在與代理伺服器通信時建立隧道,實作用隧道協定進行 TCP 通信。主要使用 SSL(Secure Sockets Layer,安全套接層)和 TLS(Transport Layer Security,傳輸層安全)協定把通信内容加密後經網絡隧道傳輸給代理伺服器。
2.響應封包
響應封包基本上由協定版本、狀态碼(表示請求成功或失敗的數字代碼)、用以解釋狀态碼的原因短語、可選的響應首部字段以及實體主體構成。
響應狀态碼的類别:
狀态碼 | 類别 | 描述 |
---|---|---|
1xx | Informational(資訊性狀态碼) | 接收的請求正在處理 |
2xx | Success(成功狀态碼) | 請求正常處理完畢 |
3xx | Redirection(重定向狀态碼) | 需要進行附加操作以完成請求 |
4xx | Client Error(用戶端錯誤狀态碼) | 伺服器無法處理請求 |
5xx | Server Error(伺服器錯誤狀态碼) | 伺服器處理請求出錯 |
常見的響應狀态碼含義:
含義 | ||
---|---|---|
200 | OK | 請求正常處理并響應 |
204 | No Content | 請求正常處理,但在傳回的響應封包中不含實體的主體部分,用戶端不做更新 |
206 | Partial Content | 用戶端進行範圍請求,響應封包中由 Content-Range 指定範圍的實體内容 |
301 | Moved Permanently | 永久性重定向,響應封包中的 Location 首部字段會帶回新的 URL 位址 |
302 | Found | 臨時性重定向,希望使用者(本次)能使用新的 URI 通路 |
303 | See Other | 臨時性重定向,與 302 類似,但 303 狀态碼明确表示用戶端應當采用 GET 方法擷取資源 |
304 | Not Modified | 服務端判定資源沒有改變,不響應資源,用戶端可以從本地緩存中擷取資源 |
400 | Bad Request | 用戶端請求封包中存在文法錯誤 |
401 | Unauthorized | 用戶端請求需要 HTTP 認證資訊 |
403 | Forbidden | 用戶端請求資源的通路被伺服器拒絕了,多見于權限問題 |
404 | Not Found | 伺服器無法找到請求的資源 |
500 | Internal Server Error | 服務端執行請求時發生了錯誤 |
503 | Service Unavailable | 服務端暫時處于超負載或正在進行停機維護,現在無法處理請求 |
當 301、302、303 響應狀态碼傳回時,幾乎所有的浏覽器都會把 POST 改成 GET,并删除請求封包内的主體,之後請求會自動再次發送。
三、HTTP 狀态管理
HTTP 是一種不儲存狀态,即無狀态(stateless)協定。HTTP 協定自身不對請求和響應之間的通信狀态進行儲存。也就是說,每當有新的請求發送時,就會有對應的新響應産生。協定本身并不保留之前一切的請求或響應封包的資訊。這是為了更快地處理大量事務,確定協定的可伸縮性,以及減少伺服器 CPU 和記憶體資源的損耗,進而特意把 HTTP 協定設計成如此簡單的。
那麼 HTTP 協定怎麼管理狀态呢?這就要說到 Cookie 技術。Cookie 技術通過在請求和響應封包中寫入 Cookie 資訊來控制用戶端的狀态。
Cookie 會根據從伺服器端發送的響應封包内的一個叫做 Set-Cookie 的首部字段資訊,通知用戶端儲存 Cookie。當下次用戶端再往該伺服器發送請求時,用戶端會自動在請求封包中加入 Cookie 值後發送出去。
伺服器端發現用戶端發送過來的 Cookie 後,會去檢查究竟是從哪一個用戶端發來的連接配接請求,然後對比伺服器上的記錄,最後得到之前的狀态資訊。
四、其他
1、HTTP 内容編碼是指在不丢失實體資訊的前提下所進行的壓縮,内容編碼後的實體由用戶端接收并負責解碼。常見的内容編碼有以下幾種:
- gzip(GUN zip)
- compress(UNIX 系統的标準壓縮)
- deflate(zlib)
- identity(不進行編碼)
2、HTTP 分塊傳輸編碼(Chunked Transfer Coding)會将實體主體分成多個部分(塊)。每一塊都會用十六進制來标記塊的大小,而實體主體的最後一塊會使用“0(CR+LF)”來标記,分塊傳輸編碼的實體主體由用戶端接收并負責解碼。
3、HTTP 發送的一份封包主體中可含有多類型實體,通常是在圖檔或文本檔案等上傳時使用,多類型實體的每個部分類型中,都可以含有首部字段,多類型實體包含的對象如下:
- multipart/form-data,在 web 表單檔案上傳時使用。
- multipart/byteranges,狀态碼 206(Partial Content,部分内容)響應封包包含了多個範圍的内容時使用。
Content-Type: multipart/form-data; boundary=AaB03x
--AaB03x
Content-Disposition: form-data; name="field1"
Joe Blow
--AaB03x
Content-Disposition: form-data; name="pics"; filename="file1.txt"
Content-Type: text/plain
...(file1.txt的資料) ...
--AaB03x
HTTP 協定的 header 中的 Range 是用來指定資源的 byte 範圍,比如:“Range:bytes=5001-10000” 表示請求 5001- 10000 位元組的資源。針對範圍請求,響應會傳回狀态碼為 206 Partial Content 的響應封包,并在 header 字段指明 “ContentType:multipart/byteranges”
4、Comet 是一種進階的 Ajax 技術,通過延遲應答,模拟實作伺服器端向用戶端推送(Server Push)的功能。通常,伺服器端接收到請求,在處理完畢後就會立即傳回響應,但為了實作推送功能,Comet 會先将響應置于挂起狀态,當伺服器端有内容更新時,再傳回該響應。是以,伺服器端一旦有更新,就可以立即回報給用戶端。
内容上雖然可以做到實時更新,但為了保留響應,一次連接配接的持續時間也變長了。期間,為了維持連接配接會消耗更多的資源。另外,Comet 也仍未解決 HTTP 協定本身存在的問題。
5、HTTP 協定的缺點?
- 一條連接配接上隻可發送一個請求。
- 請求隻能從用戶端開始。用戶端不可以接收除響應以外的指令。
- 請求/響應首部未經壓縮就發送。首部資訊越多延遲越大。
- 發送冗長的首部。每次互相發送相同的首部造成的浪費較多。
- 可任意選擇資料壓縮格式。非強制壓縮發送。