《HTTP權威指南》學習總結1——HTTP協議概述

引言

如今我們已進入Web x.0的時代(求x = ?),HTTP協議是Web系統的最基本協議,但是很多開發者都很少系統瞭解過,殊不知了解HTTP協議可以解決很多開發過程中的疑難雜症。因爲自己在兩年前學習網絡編程的時候想着做個什麼東西出來,當時就選了一個HTTP服務器,從此與HTTP結下了不解之緣,工作以後到目前爲止主要工作內容就是Web開發,於是就想深入瞭解下HTTP協議,在看完《HTTP權威指南》後寫下了這幾篇博客來作爲自己對最近一段時間學習的總結。

什麼是HTTP協議?

HTTP的全稱是Hypertext Transfer Protocal(超文本傳輸協議),最初是一幫搞核物理實驗的人發明出來用來做文件共享的,沒想到竟然成爲了因特網的發展史上的一次“革命”,讓我們感謝一下T. Berners-Lee。。。最新的HTTP/2草案已經由IETF在2015年5月發佈,但目前HTTP被使用的版本仍以HTTP/1.1爲主,所以我也以介紹HTTP/1.1爲主(關鍵是HTTP/2還沒有開始瞭解,後面可能會寫一篇介紹HTTP/2的文章)。

本段信息翻譯自rfc1945
HTTP(Hypertext Transfer Protocol,超文本傳輸協議)是一個應用層協議,它擁有分佈式的、協作的、超媒體的信息系統對靈活性及速度的要求。它是一個通用的、無狀態的、面向對象的協議,通過對它的請求方法(命令)進行擴展,可以被用於多種用途,例如名稱服務器、分佈式對象管理系統。HTTP的一個特性是它的數據表現類型允許系統的構建不需要依賴所傳輸的數據。
HTTP自從1990年就在WWW(World-Wide Web,萬維網)上被廣泛使用。

本段摘自維基百科
名稱服務器(英語:name server或nameserver)是指提供域名服務協議的程序或服務器。它可以將“人類可識別”的標識符,映射爲系統內部通常爲數字形式的標識碼。域名系統(DNS)服務器是最著名的名稱服務器。

所以我們知道了HTTP實際上主要是用來做資源共享的,它是Web服務器與Web客戶端使用的應用層通信協議:Web客戶端向Web服務器請求Web資源,Web服務器做一定的處理將資源以及相關信息發給客戶端。
那麼問題來了,什麼是Web資源?

Web資源與URI

所有能提供Web內容的東西都是Web資源,包括靜態資源和動態資源。URI提供了一種統一的資源命名方式,它標記了資源的訪問方式。URI分爲URL和URN,一般情況下,URI指的就是URL。

URI:Uniform Resource Identifier,統一資源標識符
URL:Uniform Resource Locator,統一資源定位符
URN:Uniform Resource Name,統一資源名稱

URL擁有固定的語法格式,因方案(scheme)而異,看下面的例子:

HTTP:http://<host>:<port>/<path>?<query>#<frag>
HTTPS:https://<host>:<port>/<path>?<query>#<frag>
FTP:ftp://<user>:<password>@<host>:<port>/<path>;<params>
FILE:file://<host>/<path>

關於URL格式的更詳細信息可以參考rfc1738。

注意,方案即代表着協議,URL並不侷限於HTTP協議。下面我們詳細討論一下HTTP的URL。

組件 意義 是否可選
scheme 方案 指明瞭解析URL的程序使用什麼協議,方案名是大小寫無關的 必須
host 主機 標識了資源所在的宿主機器,也就是服務器 必須
port 端口 說明了服務器正在監聽的網絡端口 有默認值
path 路徑 指明瞭資源所在的地方 可選
query 查詢字符串 額外傳遞給服務器的信息 可選
frag 片段 表示資源內部的一個片段,僅由客戶端使用,客戶端不能將片段傳送給服務器 可選

HTTP的URL與HTTPS的URL很像,不過HTTP的端口默認值爲80,HTTPS的端口默認值爲443。

URL分爲相對URL和絕對URL。上面討論的都是絕對URL,單獨的相對URL沒有任何意義。相對URL可以通過基礎URL來推導成絕對URL。基礎URL可能由包含相對URL的資源顯式指定(HTML裏的標籤可以指定基礎URL),如果沒有顯式指定,則可以由包含相對URL的資源確定。相對URL推導成絕對URL的算法在rfc1808中指定,後來又合併到rfc2396中。

URL由ASCII中除空格外的可打印字符表示,其他的字符我們稱之爲受限字符或者不安全字符,這些字符在表示URL時需要轉義。

客戶端應該且只應該對所有受限/不安全字符進行轉義,有些惡意的客戶端會對額外的字符進行轉義以繞過Web過濾程序的模式匹配。

URL中要轉義的值最好在ASCII字符範圍內(0 ~ 127),包含其他字符可能會導致一些問題。總之,URL對國際化的支持還是不夠友好,儘量只使用英文字符。

URL表示了資源的位置,一旦資源被移走,URL就會失效。URN則爲特定內容提供了一個穩定的名稱,與位置無關。例如urn:ietf:rfc:2616用來表示文檔rfc2616。URN當前仍處於試驗階段,它需要一個架構來解析資源的位置,目前缺乏此類架構。

永久統一資源定位符(PURL)可以用URL來實現與URN相同的功能,引入一箇中間層對資源的實際URL登記和跟蹤,當使用失效的URL訪問時,該中間層重定向到實際的URL上去。

HTTP報文

在計算機網絡中,我們一般將應用層要發送的數據稱爲報文。報文是來自客戶端的請求數據或者是來自服務端的響應數據。HTTP報文是基於文本行的格式化數據,儘管這對計算機來說可能不夠友好(影響效率又容易出錯),可是這也增加了HTTP的可擴展性和可調試性(當我們拿到一條報文時不必去查找二進制與實際意義的對應關係)。這樣還是有些好處的。

HTTP規範中要求應該用<CR><LF>兩個字符(即\r\n,包含一個回車符和一個換行符)來表示文本行的結束,但並不是所有程序都遵守這個規範,穩健的程序應該接受單個換行符作爲行的終止。

HTTP報文由三個部分組成:一個對報文進行描述的起始行(start line),零個或多個說明屬性的首部(header),以及可選的、包含數據的實體主體(entity-body,實體的主體部分)。

實體(entity)由實體首部與實體主體組成,實體首部描述了與實體相關的一些屬性。如不做特殊說明,下面的“實體的主體部分”、“實體主體”是等價的。

HTTP請求報文(request message)的語法如下:

<method> <request-uri> <HTTP-version>
<headers>

<entity-body>

HTTP響應報文(response message)的語法如下:

<HTTP-version> <status-code> <reason-phrase>
<headers>

<entity-body>

先對各部分的作一下簡要說明:

  • 方法 <method>
    指明瞭將要以何種方式來訪問由請求URI指定的資源。方法是大小寫敏感的。
  • 請求URI <request-uri>
    包含資源絕對路徑的URL,服務器可以假定自己是URL的主機/端口。
  • HTTP版本 <HTTP-version>
    包含HTTP版本的主版本號和次要版本號,如HTTP/0.9,HTTP/1.1等。版本號是爲了讓通信雙方瞭解彼此的能力。
  • 狀態碼 <status-code>
    由三位數字組成,表明了請求是否被理解或被滿足,描述了請求過程中發生的情況。
  • 原因短語 <reason-phrase>
    用簡短的文字來描述狀態代碼產生的原因。原因短語不做特殊限制,通常使用通用的描述方便交流。客戶端不需要檢查或顯示原因短語。
  • 首部 <headers>
    可以有零個或多個首部,每個首部包含一個名字,後面跟着一個冒號(:),然後是一個可選的空格,接着是一個值,最後是一個。首部描述了請求或者響應的一些屬性,有些HTTP版本要求報文中必須包含某些特定的首部。
  • 實體主體 <entity-body>
    包含一個由任意數據組成的數據塊。並不是所有的報文都包含實體主體,由請求方法與響應狀態碼決定。

注意,起始行和首部都是純文本的,但實體主體沒有此限制。

下面是一個請求報文的例子:

GET /example.html HTTP/1.1
Host: 127.0.0.1:8080
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, sdch, br
Accept-Language: zh-CN,zh;q=0.8

下面是一個響應報文的例子:

HTTP/1.1 200 OK
Date: Sun, 16 Apr 2017 01:40:46 GMT
Server: Apache/2.4.23 (Win64) PHP/5.6.24
Last-Modified: Sun, 16 Apr 2017 01:40:01 GMT
ETag: "8a-54d3ec0d24918"
Accept-Ranges: bytes
Content-Length: 138
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: text/html

<!DOCTYPE html>
<html>
  <head>
    <title>EXAMPLE</title>
  </head>
  <body>
    <p>Hello, This is an example page.</p>
  </body>
</html>

TIPS:藉助瀏覽器的F12調試工具可以看到報文的具體內容哦~

下面稍微詳細介紹一下報文的各個部分:

1. 協議版本
HTTP的第一個版本是HTTP/0.9,協議內容非常簡單:請求報文只有一個請求行且沒有版本描述,響應報文只有實體主體。HTTP/0.9只支持GET方法,而且服務器只能迴應HTML格式的字符串,不能迴應別的格式。定義它的初衷只是爲了獲取簡單的HTML對象。很快它就被HTTP/1.0取代了。

HTTP/1.0是第一個得到廣泛使用的HTTP版本,它於1996年5月發佈,定義於rfc1945當中。協議的內容大大增加,添加了版本號、首部、一些額外的方法以及服務器響應的狀態碼。HTTP/1.0引入了POST方法和HEAD方法,而且任何格式的內容都可以發送了。這使得互聯網不僅可以傳輸文字,還能傳輸圖像、視頻、二進制文件。這爲互聯網的大發展奠定了基礎。HTTP/1.0的新特性使得HTTP的可用性大大提高,爲了滿足商業需要,各個Web客戶端和服務器廠商都在向HTTP中添加各種新特性,於是產生了一個HTTP的非正式擴展版本,已經成爲非官方的事實標準,通常稱之爲HTTP/1.0+。

HTTP/1.1對HTTP/1.0+進行改進,主要關注的是設計中的缺陷與語義明確,引入了一些性能優化措施,並刪除了一些不好的特性。HTTP/1.1最初於1997年1月發佈,定義於rfc2068中,後來又在1999年6月重新修訂,形成了現在廣爲人知的rfc2616。再後來又由多個rfc文檔進行補充詳細說明。

本段信息翻譯自rfc2616
超文本傳輸協議(HTTP)是一個分佈式、協作、超媒體信息系統的應用層協議。在1990年WWW全球信息剛剛起步的時候HTTP就得到了應用。HTTP的第一個版本我們稱之爲HTTP/0.9,是一種爲互聯網原始數據傳輸服務的簡單協議。由rfc1945定義的HTTP/1.0進一步完善了這個協議。它允許消息以類MIME消息的格式傳送,它包括傳輸數據的元信息和對請求/響應語義的修飾。但是,HTTP/1.0沒有充分考慮到分層代理、緩存的影響和持久連接及虛擬主機的需求。並且隨着不完善的HTTP/1.0應用程序的激增,這就迫切需要一個新的版本,以便能使兩個通信程序能夠確定彼此的真實能力。

此規範定義的協議叫做“HTTP/1.1”,這個協議與HTTP/1.0相比,此規範更爲嚴格,以確保各個協議的特徵得到可靠實現。

HTTP/2已經在2015年5月發佈了草案,定義於rfc7540。這個版本刪除了次要版本號,所以你看到的是HTTP/2而不是HTTP/2.0。這個版本將HTTP的語義進行了優化,比如採用了二進制描述。(這個版本的rfc文檔已不再由T. Berners-Lee起草,而是由SPDY的相關人員編寫)

本段信息翻譯自https://http2.github.io/faq/
Q: HTTP/2會替換HTTP/1.x嗎?
A: 工作組的目標是HTTP/1.x的典型用途可以使用HTTP/2並看到一些好處。話雖如此,我們不能強迫世界遷移,並且由於人們部署代理和服務器的方式,HTTP/1.x很可能仍然再使用一段時間。

現在已經有一部分客戶端服務器已能支持HTTP/2(的部分特性?),但HTTP/1.1在一段時間內仍然會是主流版本。

2. 方法
方法除了HTTP協議預定義的一些請求方法外是可以擴展的,下表列出的方法被大多數商業服務器所實現。

方法描述 是否包含實體起始版本
GET從服務器獲取一份文檔HTTP/0.9
HEAD向服務器獲取以GET方法請求時文檔的首部HTTP/1.0
POST向服務器發送要處理的數據HTTP/1.0
TRACE對可能經過代理服務器的報文進行跟蹤HTTP/1.1
OPTIONS瞭解服務器可以對資源執行哪些方法HTTP/1.1

並不是所有的服務器都實現了所有方法。如果服務器不理解客戶端發送的方法,就會返回501或者405的狀態碼。

HTTP將不會在服務器上產生任何結果的方法稱爲安全方法(這裏的產生結果是指修改數據),GET和HEAD被設計爲安全方法。實際上安全方法並非真的不會產生結果,這取決於Web開發者。安全方法被設計的目的在於提示/通知用戶不安全的方法可能會導致一些後果(比如點擊支付按鈕的可能導致賬戶扣款)。

3. 狀態碼

方法告訴服務器做什麼事情,狀態碼則告訴客戶端發生了什麼事情。狀態碼由三位數字組成,根據第一位數字分成五大類:
+ 1xx:信息型狀態碼
+ 2xx:成功狀態碼
+ 3xx:重定向狀態碼
+ 4xx:客戶端錯誤狀態碼
+ 5xx:服務端錯誤狀態碼

原因短語通常和狀態碼一起使用,但原因短語是給人看的,比如,對客戶端或服務器來說,它對200 OK200 Done的處理方式完全一樣。儘管如此,大多數服務器還是會返回HTTP協議推薦的原因短語。

下表列出了一些常見的錯誤碼:

狀態碼原因短語 含義
200OK成功請求,實體包含所請求的資源
304Not Modified客戶端發起條件GET請求,同時所請求資源未修改就返回這個狀態碼,表明可以使用緩存。返回這個狀態碼時不應該包含實體主體
400Bad Request告知客戶端發了一個錯誤的請求
403Forbidden服務器拒絕請求,返回實體可能說明拒絕原因,但這個狀態碼通常是在服務器不想說明拒絕原因的時候使用的
404Not Found請求資源不存在
414Request URI Too Large請求的URI太長,超過服務器能處理的範圍
500Internal Server Error服務器遇到了一個妨礙完成請求的錯誤
502Bad Gateway作爲代理或網關的服務器收到了錯誤響應時使用此狀態碼\*\*

** 經驗告訴我們,從一個運行良好的站點收到此狀態碼錶明這個站點連接數過多超出了後端服務器處理的能力,也就是網站所支持的併發連接數不夠。

當客戶端收到了它不理解的狀態碼時會根據這個狀態碼所在的大類來確定如何處理響應。

4. 首部

首部和方法、響應碼配合工作,來決定客戶端和服務器能做什麼事情。首部可以分爲五個主要類型。

  • 通用首部:客戶端和服務器都可以使用的首部。
  • 請求首部:請求報文特有的首部
  • 響應首部:響應報文特有的首部
  • 實體首部:對應於實體的主體部分的首部
  • 擴展首部:由應用程序開發者創建的非標準首部

下面舉幾個例子:

類別首部 含義
通用首部`Date`提供日期和時間,說明報文的創建時間
`Cache-Control`用於控制緩存
請求首部`Host`說明所請求的服務器主機
`If-Modified-Since`只有在指定日期後資源被修改過才完成這個請求,否則返回304
響應首部`Server`服務器應用程序軟件的名稱和版本
實體首部`Content-Length`說明實體主體的長度
`Content-Type`說明實體的MIME類型
`Last-Modified`說明實體的最後一次修改日期和時間


理解HTTP協議的關鍵在於對HTTP在不同場景所產生報文的理解,也就是理解不同的狀態碼、首部具體應用場景。後面幾篇文章將會介紹一些重點的模塊,想要了解更多可以繼續關注我的博客,參考資料中給出了更爲完整更爲權威的文檔,也可以讀一下。

HTTP的複雜性主要在於通信的客戶端或服務器所支持的HTTP協議可能不同,或者沒有正確實現協議要求的某些內容。

到現在爲止我們已經知道HTTP報文的基本組成,但是HTTP協議在網絡中是如何工作的呢?

HTTP事務

HTTP協議是應用層的通信協議,它依賴傳輸層的TCP協議來完成報文的發送與接收。TCP協議是面向字節流的可靠的傳輸層協議,它可以保證數據無誤的從發送端到達接收端,因此我們在使用HTTP協議的時候,無需考慮報文是否被正確傳輸,這些事情TCP都幫我們做了。

HTTPS協議是基於SSL/TLS的HTTP協議。抽象的看,HTTPS主要是在TCP和HTTP之間加了一層“安全層”(確切來說應該是OSI中的表示層和會話層)利用SSL/TLS來對數據進行加密解密。

                                    +-------------------+
                                    |        HTTP       |
+-------------------+               |-------------------|
|        HTTP       |               |     SSL or TLS    |
|-------------------|               |-------------------|
|        TCP        |               |        TCP        |
|-------------------|               |-------------------|
|         IP        |               |         IP        |
|-------------------|               |-------------------|
| Network Interface |               | Network Interface |
+-------------------+               +-------------------+
      HTTP協議棧                           HTTPS協議棧

HTTP在傳輸數據前,先建立TCP連接,連接建立好後就在客戶端和服務器間形成了一條虛擬電路,HTTP報文可以沿着這條虛擬電路到達指定位置,報文發送完畢可以根據具體情況決定是否需要關閉連接。

關於TCP的信息這裏不做過多闡述,我們重點關注HTTP的工作過程。

我們將一條客戶端發送到服務器的請求報文與服務器返回給客戶端的響應報文合起來稱爲一個HTTP事務。
最初一個TCP連接僅處理一個HTTP事務。後來爲了優化性能提出了持久連接的概念。儘管如此,我們仍需知道HTTP協議是無狀態的,也就是說在多個HTTP事務中,每個事務都是獨立的,無論TCP連接是持久的還是完成一個事務就斷開,HTTP都不會記錄此事務的連接狀態(對比FTP,FTP在進行用戶認證後會一直保持和這個用戶的會話,直至關閉連接,但HTTP不會)。

一個HTTP事務的處理過程舉例:
1. 客戶端從URL中解析出服務器的主機名
2. 客戶端將服務器主機名轉化爲IP地址
3. 客戶端從URL中解析出端口號(如果有)
4. 客戶端建立與服務器的TCP連接
5. 客戶端向服務器發送一條HTTP請求報文
6. 服務器處理客戶端發送的請求,生成響應報文發回客戶端
7. 客戶端收到響應報文,關閉連接

Web結構組件

在這篇文章中,我不停提到服務器和客戶端,那麼到底什麼是服務器和客戶端,HTTP的服務器和客戶端到底都有哪些呢?
首先得聲明,服務器和客戶端既可以指軟件應用程序,也可以指運行對應軟件的硬件設備,可以根據語境區分出具體含義。下面的解釋主要針對軟件的概念來描述。

1. 服務器(server)
Web服務器是Web資源的擁有者,它可以處理來自Web客戶端的請求。這是一個被動的過程,Web服務器不會主動向Web客戶端發送報文。Web服務器因爲使用HTTP作爲通信協議,所以也稱爲HTTP服務器。常見的Web服務器有Nginx、Apache httpd、Apache tomcat、Lighttpd,Microsoft IIS等等。HTTP響應報文中的Server首部表明了服務器的名稱和版本。

值得一提的是上面說到的Apache tomcat和Microsoft IIS,它們是一種應用服務器,也可以提供與HTTP服務器相同的功能。不過因爲它們主要處理動態內容,所以處理靜態頁面的性能遠遠不如Nginx、httpd等專注處理靜態頁面的HTTP服務器。在實際應用中,通常會將其作爲Nginx/httpd的後端服務器來專門處理動態內容。

2. 客戶端(client)
Web客戶端可以發送HTTP請求報文的程序,通常還會解析收到的HTTP響應報文。最常見的Web客戶端就是各種瀏覽器。除了瀏覽器還有一類自動發送HTTP請求的程序,我們將其稱爲Web機器人(也叫爬蟲,網絡蜘蛛)。某些客戶端程序如Telnet也可以當作Web客戶端來使用。

可以代替用戶生成請求報文的客戶端,我們稱之爲用戶代理(user agent),Web客戶端主要以用戶代理爲主,很少有人會主動自己編寫請求報文發送給服務器。用戶代理在發送請求的時候通常會帶上User-Agent首部來說明其信息。至於上面我舉例的請求報文中爲什麼既有Mozilla又有AppleWebKit又有Safari,我表示也是億臉矇蔽,實際上我使用的瀏覽器是Chrome。

除了服務器與客戶端,還有一些其他Web組件。
3. 代理(proxy)
代理是位於服務器與客戶端之間的HTTP中間實體,可以負責轉發流量、內容過濾、性能優化等功能。也稱爲代理服務器。下一篇博客我會詳細介紹代理的更多細節。

關於Agent與Proxy
兩者翻譯成中文都是代理,這很容易引起困惑。可以這麼理解:Agent是客戶端,Proxy既是服務器又是客戶端(即使說成是代理服務器仍不能掩飾它充當客戶端角色的本質)

4. 緩存(cache)
緩存是一種特殊的代理服務器,它可以將經過代理的Web響應複製保存起來,以便下次更快地提供服務。正確部署和使用緩存可以很大的提高效率。緩存也叫Web緩存或者代理緩存。

5. 網關(gateway)
網關分爲協議網關和資源網關。
協議網關相當於一個協議轉換程序,比如用戶發送了一個獲取文件請求,服務器可能通過HTTP/FTP網關從FTP服務器上將文件獲取到發送給客戶端。
資源網關則指應用程序獲取另一個程序提供的數據,我們稱之爲網關API。第一個流行的網關API是CGI,由於性能的問題,人們又開發出了fastcgi。(關於CGI相關理論的學習還在我的計劃列表中,暫時沒法提供更多信息)

6. 隧道(tunnel)
Web隧道可以通過HTTP連接來發送非HTTP的流量。至於具體實現方式我還沒有理解清楚。。

More…

[1] rfc1945(HTTP/1.0):https://www.ietf.org/rfc/rfc1945.txt
[2] rfc2616(HTTP/1.1):https://www.ietf.org/rfc/rfc2616.txt
[3] rfc7540(HTTP/2):https://www.ietf.org/rfc/rfc7540.txt
[4] 關於HTTP/2的一些常見問題:https://http2.github.io/faq/
[5] Nginx官網:http://nginx.org/

Next. 《HTTP權威指南》學習總結2——代理和緩存

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章