原文鏈接 https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Overview
HTTP是一種能夠獲取如 HTML 這樣的網絡資源的 protocol(通訊協議)。它是在 Web 上進行數據交換的基礎,是一種 client-server 協議,也就是說,請求通常是由像瀏覽器這樣的接受方發起的。一個完整的Web文檔通常是由不同的子文檔拼接而成的,像是文本、佈局描述、圖片、視頻、腳本等等。
客戶端和服務端通過交換各自的消息(與數據流正好相反)進行交互。由像瀏覽器這樣的客戶端發出的消息叫做 requests,被服務端迴應的消息叫做 responses。
HTTP被設計於上20世紀90年代初期,是一種可擴展的協議。它是應用層的協議,通過TCP,或者是TLS-加密的TCP連接來發送,理論上任何可靠的傳輸協議都可以使用。因爲其良好的擴展性,時至今日,它不僅被用來傳輸超文本文檔,還用來傳輸圖片、視頻或者向服務器發送如HTML表單這樣的信息。HTTP還可以根據網頁需求,僅獲取部分Web文檔內容更新網頁。
基於HTTP的組件系統
HTTP是一個client-server協議:請求通過一個實體被髮出,實體也就是用戶代理。大多數情況下,這個用戶代理都是指瀏覽器,當然它也可能是任何東西,比如一個爬取網頁生成維護搜索引擎索引的機器爬蟲。
每一個發送到服務器的請求,都會被服務器處理並返回一個消息,也就是response。在這個請求與迴應之間,還有許許多多的被稱爲proxies的實體,他們的作用與表現各不相同,比如有些是網關,還有些是caches等。
實際上,在一個瀏覽器和處理請求的服務器之間,還有路由器、調制解調器等許多計算機。由於Web的層次設計,那些在網絡層和傳輸層的細節都被隱藏起來了。HTTP位於最上層的應用層。雖然底層對於分析網絡問題非常重要,但是大多都跟對HTTP的描述不相干。
客戶端:user-agent
user-agent 就是任何能夠爲用戶發起行爲的工具。這個角色通常都是由瀏覽器來扮演。一些例外情況,比如是工程師使用的程序,以及Web開發人員調試應用程序。
瀏覽器總是作爲發起一個請求的實體,他永遠不是服務器(雖然近幾年已經出現一些機制能夠模擬由服務器發起的請求消息了)。
要展現一個網頁,瀏覽器首先發送一個請求來獲取頁面的HTML文檔,再解析文檔中的資源信息發送其他請求,獲取可執行腳本或CSS樣式來進行頁面佈局渲染,以及一些其它頁面資源(如圖片和視頻等)。然後,瀏覽器將這些資源整合到一起,展現出一個完整的文檔,也就是網頁。瀏覽器執行的腳本可以在之後的階段獲取更多資源,並相應地更新網頁。
一個網頁就是一個超文本文檔。也就是說,有一部分顯示的文本可能是鏈接,啓動它(通常是鼠標的點擊)就可以獲取一個新的網頁,使得用戶可以控制客戶端進行網上衝浪。瀏覽器來負責發送HTTP請求,並進一步解析HTTP返回的消息,以向用戶提供明確的響應。
Web服務端
在上述通信過程的另一端,是由Web Server來服務並提供客戶端所請求的文檔。Server只是虛擬意義上代表一個機器:它可以是共享負載(負載均衡)的一組服務器組成的計算機集羣,也可以是一種複雜的軟件,通過向其他計算機(如緩存,數據庫服務器,電子商務服務器 ...)發起請求來獲取部分或全部資源。
Server 不一定是一臺機器,但一個機器上可以裝載的衆多Servers。在HTTP/1.1 和Host
頭部中,它們甚至可以共享同一個IP地址。
代理(Proxies)
在瀏覽器和服務器之間,有許多計算機和其他設備轉發了HTTP消息。由於Web棧層次結構的原因,它們大多都出現在傳輸層、網絡層和物理層上,對於HTTP應用層而言就是透明的,雖然它們可能會對應用層性能有重要影響。還有一部分是表現在應用層上的,被稱爲代理(Proxies)。代理(Proxies)既可以表現得透明,又可以不透明(“改變請求”不會通過它們)。代理主要有如下幾種作用:
- 緩存(可以是公開的也可以是私有的,像瀏覽器的緩存)
- 過濾(像反病毒掃描,家長控制...)
- 負載均衡(讓多個服務器服務不同的請求)
- 認證(對不同資源進行權限管理)
- 日誌記錄(允許存儲歷史信息)
HTTP 的基本性質
HTTP 是簡單的
雖然下一代HTTP/2協議將HTTP消息封裝到了幀(frames)中,HTTP大體上還是被設計得簡單易讀。HTTP報文能夠被人讀懂,還允許簡單測試,降低了門檻,對新人很友好。
HTTP 是可擴展的
在 HTTP/1.0 中出現的 HTTP headers 讓協議擴展變得非常容易。只要服務端和客戶端就新 headers 達成語義一致,新功能就可以被輕鬆加入進來。
HTTP 是無狀態,有會話的
HTTP是無狀態的:在同一個連接中,兩個執行成功的請求之間是沒有關係的。這就帶來了一個問題,用戶沒有辦法在同一個網站中進行連續的交互,比如在一個電商網站裏,用戶把某個商品加入到購物車,切換一個頁面後再次添加了商品,這兩次添加商品的請求之間沒有關聯,瀏覽器無法知道用戶最終選擇了哪些商品。而使用HTTP的頭部擴展,HTTP Cookies就可以解決這個問題。把Cookies添加到頭部中,創建一個會話讓每次請求都能共享相同的上下文信息,達成相同的狀態。
注意,HTTP本質是無狀態的,使用Cookies可以創建有狀態的會話。
HTTP 和連接
一個連接是由傳輸層來控制的,這從根本上不屬於HTTP的範圍。HTTP並不需要其底層的傳輸層協議是面向連接的,只需要它是可靠的,或不丟失消息的(至少返回錯誤)。在互聯網中,有兩個最常用的傳輸層協議:TCP是可靠的,而UDP不是。因此,HTTP依賴於面向連接的TCP進行消息傳遞,但連接並不是必須的。
HTTP/1.0爲每一個請求/響應都打開一個TCP連接,導致了2個缺點:打開一個TCP連接需要多次往返消息傳遞,因此速度慢。但當多個消息週期性發送時,這樣就變得更加高效:暖連接比冷連接更高效。
爲了減輕這些缺陷,HTTP/1.1引入了流水線(被證明難以實現)和持久連接的概念:底層的TCP連接可以通過Connection
頭部來被部分控制。HTTP/2則發展得更遠,通過在一個連接複用消息的方式來讓這個連接始終保持爲暖連接。
爲了更好的適合HTTP,設計一種更好傳輸協議的進程一直在進行。Google就研發了一種以UDP爲基礎,能提供更可靠更高效的傳輸協議QUIC。
HTTP 能控制什麼
多年以來,HTTP良好的擴展性使得越來越多的Web功能歸其控制。緩存和認證很早就可以由HTTP來控制了。另一方面,對同源同域的限制到2010年纔有所改變。
以下是可以被HTTP控制的常見特性。
- 緩存
文檔如何緩存能通過HTTP來控制。服務端能告訴代理和客戶端哪些文檔需要被緩存,緩存多久,而客戶端也能夠命令中間的緩存代理來忽略存儲的文檔。 - 開放同源限制
爲了防止網絡窺聽和其它隱私泄漏,瀏覽器強制對Web網站做了分割限制。只有來自於相同來源的網頁才能夠獲取網站的全部信息。這樣的限制有時反而成了負擔,HTTP可以通過修改頭部來開放這樣的限制,因此Web文檔可以是由不同域下的信息拼接成的(某些情況下,這樣做還有安全因素考慮)。 - 認證
一些頁面能夠被保護起來,僅讓特定的用戶進行訪問。基本的認證功能可以直接通過HTTP提供,使用Authenticate
相似的頭部即可,或用HTTP Cookies來設置指定的會話。 - 代理和隧道
通常情況下,服務器和/或客戶端是處於內網的,對外網隱藏真實 IP 地址。因此 HTTP 請求就要通過代理越過這個網絡屏障。但並非所有的代理都是 HTTP 代理。例如,SOCKS協議的代理就運作在更底層,一些像 FTP 這樣的協議也能夠被它們處理。 - 會話
使用HTTP Cookies允許你用一個服務端的狀態發起請求,這就創建了會話。雖然基本的HTTP是無狀態協議。這很有用,不僅是因爲這能應用到像購物車這樣的電商業務上,更是因爲這使得任何網站都能輕鬆爲用戶定製展示內容了。
HTTP 流
當客戶端想要和服務端進行信息交互時(服務端是指最終服務器,或者是一箇中間代理),過程表現爲下面幾步:
- 打開一個TCP連接:TCP連接被用來發送一條或多條請求,以及接受迴應消息。客戶端可能打開一條新的連接,或重用一個已經存在的連接,或者也可能開幾個新的TCP連接連向服務端。
- 發送一個HTTP報文:HTTP報文(在HTTP/2之前)是語義可讀的。在HTTP/2中,這些簡單的消息被封裝在了幀中,這使得報文不能被直接讀取,但是原理仍是相同的。
-
GET / HTTP/1.1 Host: developer.mozilla.org Accept-Language: fr
- 讀取服務端返回的報文信息:
HTTP/1.1 200 OK Date: Sat, 09 Oct 2010 14:28:02 GMT Server: Apache Last-Modified: Tue, 01 Dec 2009 20:18:22 GMT ETag: "51142bc1-7449-479b075b2891b" Accept-Ranges: bytes Content-Length: 29769 Content-Type: text/html <!DOCTYPE html... (here comes the 29769 bytes of the requested web page)
- 關閉連接或者爲後續請求重用連接。
當HTTP流水線啓動時,後續請求都可以不用等待第一個請求的成功迴應就被髮送。然而HTTP流水線已被證明很難在現有的網絡中實現,因爲現有網絡中有很多老舊的軟件與現代版本的軟件共存。因此,HTTP流水線已被在有多請求下表現得更穩健的HTTP/2的幀所取代。
HTTP 報文
HTTP/1.1以及更早的HTTP協議報文都是語義可讀的。在HTTP/2中,這些報文被嵌入到了一個新的二進制結構,幀。幀允許實現很多優化,比如報文頭部的壓縮和複用。即使只有原始HTTP報文的一部分以HTTP/2發送出來,每條報文的語義依舊不變,客戶端會重組原始HTTP/1.1請求。因此用HTTP/1.1格式來理解HTTP/2報文仍舊有效。
有兩種HTTP報文的類型,請求與迴應,每種都有其特定的格式。
請求
HTTP請求的一個例子:
請求由以下元素組成:
- 一個HTTP的method,經常是由一個動詞像
GET
,POST
或者一個名詞像OPTIONS
,HEAD
來定義客戶端的動作行爲。通常客戶端的操作都是獲取資源(GET方法)或者發送HTML form表單值(POST方法),雖然在一些情況下也會有其他操作。 - 要獲取的資源的路徑,通常是上下文中就很明顯的元素資源的URL,它沒有protocol(
http://),
domain(developer.mozilla.org)
,或是TCP的port(HTTP一般在80端口)。 - HTTP協議版本號。
- 爲服務端表達其他信息的可選頭部headers。
- 對於一些像POST這樣的方法,報文的body就包含了發送的資源,這與迴應報文的body類似。
迴應
HTTP迴應的一個例子:
迴應報文包含了下面的元素:
- HTTP協議版本號。
- 一個狀態碼(status code),來告知對應請求執行成功或失敗,以及失敗的原因。
- 一個狀態信息,這個信息是非權威的狀態碼描述信息,可以由服務端自行設定。
- HTTP headers,與請求頭部類似。
- 可選項,比起請求報文,響應報文中更常見地包含獲取的資源body。
總結
HTTP是一種簡單可擴展的協議,其Client-Server的結構以及輕鬆擴展頭部信息的能力使得HTTP可以和Web共同發展。
即使HTTP/2爲了提高性能將HTTP報文嵌入到幀中這一舉措增加了複雜度,但是從Web應用的角度看,報文的基本結構沒有變化,從HTTP/1.0發佈起就是這樣的結構。會話流依舊簡單,通過一個簡單的 HTTP message monitor就可以查看和糾錯。