## HTTP協議發展歷史
| 版本 | 誕生時間 |
| --- | --- |
| [HTTP 0.9](https://www.w3.org/Protocols/HTTP/AsImplemented.html) | 1991年|
| [HTTP 1.0](https://tools.ietf.org/html/rfc1945) | 1996年5月 |
| [HTTP 1.1](https://tools.ietf.org/html/rfc2068) | 1997年1月 |
| [HTTP 2](https://tools.ietf.org/html/rfc7540) (NG) | 2015年5月 |
**HTTP 0.9**: 請求和響應均不帶協議及版本,以及首部。 請求無首部,無body, 只有請求方法和URL; 響應無首部, 無狀態碼, 只有body。 HTTP 0.9支持的響應內容類型只有html.
## HTTP報文交換過程
HTTP是一個建立在TCP之上的應用協議,所以HTTP的在瀏覽器和服務器數據交換之前,需要先建立TCP連接。一個TCP連接包含三個部分:三次握手建立連接,數據傳輸,四次揮手斷開連接。HTTP的數據交換就是傳輸部分。

圖中展現的是HTTP 1.1的數據傳輸過程,即客戶端發送請求之后,服務端響應一個請求,客戶端收到響應之后才能繼續發送請求。
HTTP 0.9, HTTP 1.0 中每次TCP連接只能發送一個請求和一個響應。HTTP 2則可以同時發送多個請求和接收多個響應。如圖所示。

## HTTP 報文結構
**請求報文結構**
<table>
<tr>
<td>請求方法(HTTP Verb)</td>
<td>空格(Space)</td>
<td>資源路徑(URI)</td>
<td>空格</td>
<td>HTTP版本(HTTP Version)</td>
<td>回車換行(CRLF)</td>
</tr>
<tr>
<td>首部名稱(Header Field Name)</td>
<td>:</td>
<td colspan="3">首部值(Header Field Value)</td>
<td>回車換行</td>
</tr>
<tr>
<td colspan="6">...</td>
</tr>
<tr>
<td>首部名稱</td>
<td>:</td>
<td colspan="3">首部值</td>
<td>回車換行</td>
</tr>
<tr>
<td colspan="6">回車換行</td>
</tr>
<tr>
<td colspan="6">請求體(Body)</td>
</tr>
</table>
1. 第1行稱為起始行(Start Line), 包含了請求方法(標準叫法為HTTP謂詞), 資源路徑,和請求協議及版本。 這三個之間以一個空格分隔,并以回車換行(CRLF)結束。請求方法詳見《HTTP 請求方法》一節。
2. 第2行起至第1個空行(僅CRLF)之間為HTTP首部,每個一行,并以回車換行結束。每一個首部以KeyValue的形式呈現,Key和Value之間以冒號和空格分隔。 詳見《HTTP 首部》一節。
3. 空行之后的內容為請求體(body), 請求體是可選的, 可以是文本,也可以是二進制文件,還可以為空。
例如Chrome使用百度搜索hello world, 請求報文結構如下:
```xxx
GET /s?wd=hello%20world HTTP/1.1
Host: www.baidu.com
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: en,zh-CN;q=0.9,zh;q=0.8,zh-TW;q=0.7,ja;q=0.6
Cookie: locale=zh; sug=3
```
#### 響應報文結構
<table>
<tr>
<td>HTTP版本</td>
<td>空格</td>
<td>狀態碼(Status Code)</td>
<td>空格</td>
<td>狀態注釋(Status Annotation)</td>
<td>回車換行</td>
</tr>
<tr>
<td>首部名稱</td>
<td>:</td>
<td colspan="3">首部值</td>
<td>回車換行</td>
</tr>
<tr>
<td colspan="6">...</td>
</tr>
<tr>
<td>首部名稱</td>
<td>:</td>
<td colspan="3">首部值</td>
<td>回車換行</td>
</tr>
<tr>
<td colspan="6">回車換行</td>
</tr>
<tr>
<td colspan="6">響應體(Body)</td>
</tr>
</table>
1. 第1行稱為起始行,包含了協議及版本,狀態碼和狀態注釋, 這三個之間用空格分隔,并以回車換行結束。狀態注釋在實際使用中用處不大。
2. 和請求報文一行,第2行起至第1個空行之間為HTTP首部,每個一行,并以回車結束。//由于客戶端和服務器之間采用的是協商機制,客戶端請求部分首部會提供多個值給服務器端選擇,而服務器端往往提供確定的一個值告訴客戶端服務器端選擇的值。當然,有時候服務器端響應部分首部也會提供多個值給客戶端選擇,以便進一步的協商。
3. 空行之后的內容為響應體(body). 響應體是可選的,可以是文本,也可以是二進制文件,還可以為空。
依然以Chrome使用百度搜索hello world, 響應報文結構如下:
```xxx
HTTP/1.1 200 OK
Bdpagetype: 3
Bdqid: 0xbb459f9700043636
Cache-Control: private
Ckpacknum: 2
Ckrndstr: 700043636
Connection: Keep-Alive
Content-Encoding: gzip
Content-Type: text/html;charset=utf-8
Date: Fri, 01 Jun 2018 00:57:37 GMT
P3p: CP=" OTI DSP COR IVA OUR IND COM "
Server: BWS/1.1
Set-Cookie: PSINO=1; domain=.baidu.com; path=/
Set-Cookie: BDSVRTM=32; path=/
Strict-Transport-Security: max-age=172800
Vary: Accept-Encoding
X-Ua-Compatible: IE=Edge,chrome=1
Transfer-Encoding: chunked
<!DOCTYPE html>
<html>
...
</html>
```
#### HTTP 0.9報文結構
以上是標準的請求和響應報文結構I, HTTP 0.9相對簡單,他們的請求和響應報文結構如下:
請求報文結構
```xxx
GET /path?query
```
響應報文結構
```xxx
<html>
...
</html>
```
## HTTP 請求方法
| 請求方法 | 支持協議版本 | 說明|
| --- | --- |--- |
|GET | 0.9, 1.0, 1.1, 2.0 |獲取資源實體(entity),如果請求的是數據處理程序處理的,那么應當返回實體, GET方法有很多緩存策略, 條件請求策略來減少網絡寬帶的使用。 GET請求理論上也可以傳輸body,不過服務端不應該去處理body或者應當響應錯誤,**主流瀏覽器并不支持在GET請求里傳輸body,普通請求和Ajax請求均不支持**|
| POST | 1.0, 1.1, 2.0 |提交信息到服務器, 在語義上,它指創建新的資源,非冪等的, 理論上提交成功之后,服務端應返回新資源你的地址(URI)。這一點在創建RESTful接口的時候有明顯體現|
| HEAD | 1.0, 1.1, 2.0 |HEAD方法和GET類似, 只是響應的結果沒有實體,只有頭部信息,這個方法可以用來檢查連接的有效性,可訪問性和是否放生過改變|
| OPTIONS |1.1, 2.0 |用于獲取服務所支持的通信選項, 如請求方法,請求來源等, 該請求的響應沒有body, 只有首部。主要用于:<br>1. 檢測服務器所支持的請求方法, 服務器返回Allow首部表明允許使用的請求方法 <br>2. 跨域時預檢請求|
| PUT |1.1, 2.0 |提交信息到服務器, 在語義上,它指更新對應的資源,冪等的. 理論上,可以不返回新資源的地址, 因為請求提交的地址就是資源的地址|
| DELETE |1.1, 2.0 |指示服務器刪除一個資源, 這只是一個指示, 具體如何執行由服務器決定(例如有可能軟刪除),即使返回成功,也不一定表示服務器上的資源真的刪除了|
| TRACE | 1.1, 2.0 |回顯服務端收到的請求,用于測試或診斷|
|CONNECT | 2.0 |使客戶端和最終服務器開啟直接連接的通道, 使客戶端和最終服務器可以直接相互傳遞信息(https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Methods/CONNECT)|
1. 在協議上,請求方法是大小寫敏感的,在發送請求時應該大寫。
2. GET請求可以帶body, 不過一般瀏覽器不支持,普通請求和Ajax都不支持,但是其他的一些客戶端可以支持,例如curl。
3. POST和PUT提交信息的時候需要加入`Content-Type`(詳見HTTP首部部分)首部,表明所提交的信息的數據類型,以便服務端解析。
4. OPTIONS方法跨域發送預檢請求時, 應當帶上`Origin`, `Access-Control-Request-Method`, `Access-Control-Request-Headers` 三個首部,告知服務端請求來源域名,將要請求的方法以及自定義的請求頭部。詳細參考《專題:跨域請求》一節。
5. CONNECT舉例:

詳細參考[https://stackoverflow.com/a/11698002](https://stackoverflow.com/a/11698002)
6. 過去Web API的實現大部分只用GET和POST方法,在語義上存在誤用,且響應狀態碼也存在濫用,這不但提高了他人使用API的難度,而且提高了代理和客戶端緩存實現的難度。近年來RESTful風格Web API逐漸占主流,這種風格的API首先要求請求方法(謂詞)使用準確,返回的狀態碼也要準確,在API路由上也更加清晰。
## HTTP 狀態碼
1xx 表示請求已經接受,但是在進一步處理請求時遇到了一些問題。
2xx 表示服務器已經成功處理了請求。
3xx 表示要完成請求還需要進一步的動作, 多表示重定向。
4xx 表示客戶端請求有誤,錯誤的地址,錯誤的方法,錯誤的授權等。
5xx 表示服務端處理請求錯誤,代碼錯誤,網關/代理請求超時等。
| 狀態碼 | 支持協議版本 | 說明|
| --- | --- |--- |
| 100 |1.1 |服務端接收到了客戶端發送的首部,客戶端可以繼續傳輸請求體(body)了|
| 101 |1.1 |服務端已經了解客戶端的請求,并且通知客戶端切換協議,服務端通過返回`Upgrade`首部來告知客戶端可以切換的協議及版本|
| **200** |1.0, 1.1 |請求響應成功|
| 201 |1.0, 1.1 |請求執行了并且創建了一個新的資源,新資源的URL將在設置放置到響應的`Location`首部|
| 202 |1.0, 1.1 |請求已接收,但是請求還未處理完成。對于一些程序處理耗時較長的請求(如果轉存資源到CDN), 可以提前釋放TCP連接|
| 203 |1.1 |文檔被正常的返回,但返回的實體頭部元信息不是在原始服務器上有效的確定集合,而是來自本地或者第三方的拷貝|
| 204 | 1.0, 1.1 |服務端處理請求成功,返回了正確的首部信息,但是并沒有返回響應實體。如果請求方是瀏覽器的話,頁面視圖上不應該有任何變化。|
| 205 |1.1 |和204一樣,服務器成功處理了請求,返回了正確的首部信息,但是并沒有返回響應實體。 與204不同之處在于,205要求客戶端(瀏覽器)重置文檔視圖,比如清空表單已輸入信息|
| 206 |1.1 |服務器已經成功處理了部分 GET 請求。在下載過程中斷點續傳可以使用該狀態碼。打算獲得206響應的請求的請求頭中必須攜帶`Range`首部,響應中必須包含`Content-Range`或`Content-Length`首部|
| 300 |1.1 |被請求的資源有多個地址,客戶端(瀏覽器)可以選擇其中一個重定向。響應的內容(body)應該是一個包含資源名稱和響應連接的列表|
| **301** |1.0, 1.1 |請求永久重定向,表示資源已經永久性的使用新的訪問地址了,在響應的`Location`首部中會給出資源的新地址,客戶端應當使用新地址重新發送請求。一般來說,301請求是會被客戶端緩存的。**該請求只適用于GET和HEAD請求方法, 如果POST或者其他請求方法返回301的話,可能導致重定向之后的請求時GET請求**。|
| **302** |1.0, 1.1 |請求臨時重定向,表示資源只是臨時使用該地址訪問,在響應的`Location`首部中會給資源的出臨地址, 客戶端需要使用新地址發送請求獲得響應。一般來說,302請求時不緩存的,除非包含`cache-control`,`expires`等首部。和301一樣,只適用GET和HEAD請求,POST請求重定向之后會變成GET請求|
| 303 |1.1 |對應當前請求的響應可以在另一個 URI 上被找到,而且客戶端應當采用 GET 的方式訪問那個資源。303的主要作用是POST請求成功之后跳轉到新生成的資源地址上。303請求不應當被緩存。|
| **304** |1.0, 1.1 |資源未修改,服務器應該只響應頭部,不返回實體,客戶端(瀏覽器)將從本地緩存中讀取資源|
| 305 |1.1 |被請求的資源必須通過指定的代理(如Nginx)才能被訪問, 響應的首部`Location`提供了代理所在的地址|
| **307** |未知 |請求臨時重定向, 和302的區別支出在于307支持POST請求,重定向之后POST請求還是POST請求|
| 400 |1.0, 1.1 |語義有誤,服務端無法理解當前請求, 除非修改請求,否則不應重復提交。如請求方法不合法,請求體太大,請求體的編碼格式和`Content-Type`中不一致等|
| **401** |1.0, 1.1 |當前請求需要用戶認證。用戶訪問網站時,服務端會返回401和`WWW-Authenticate`首部,如果這個首部的值為空,客戶端(瀏覽器)會要求用戶輸入用戶名和密碼,客戶端將添加`Authorization`首部再次發送請求,驗證通過則返回200和帶有值的`WWW-Authenticate`首部, 否則繼續返回401和無值得`WWW-Authenticate`首部, 具體過程見《HTTP首部》部分|
| 402 |1.1 |需要支付,該狀態碼給未來預留, 暫無瀏覽器支持|
| **403** |1.0, 1.1 |服務器能夠理解請求,但是拒絕執行。理論上響應體中應返回拒絕的原因,但目前的服務器返回的原因并不具體|
| **404**|1.0, 1.1 |請求失敗,所請求的資源在服務器端不存在。理論上這個請求的響應不應該返回響應體,如果需要返回響應體,應該用403比較準確。|
| 405 |1.1 |請求方法不適用。響應中會包含`Allow`首部來表明所請求的URI允許使用的請求方法|
| 406 |1.1 |請求的資源的內容特性無法滿足請求頭中的條件,因而無法生成響應實體。|
| 407 |1.1 |和401類似,區別之處是客戶端需要代理服務器(如nginx)的授權,而401是客戶端需要應用的授權,但是在實際使用中,往往用401,即使在代理服務器層面 |
| 408 |1.1 |請求超時,服務端已經準備接收客戶端請求,但是客戶端沒有在指定的時間內發送請求。例如客戶端在資源接收完畢之后沒有發送`Connection`:`close`首部|
| 409 |1.1 |請求與當前服務器端的狀態相沖突。沖突最有可能發生在對 PUT 請求的響應中。例如,當上傳文件的版本比服務器上已存在的要舊,從而導致版本沖突的時候,那么就有可能收到狀態碼為 409 的響應。|
| 410 |1.1 |請求的資源在服務器端已經不存在了,和404類似,區別在于410會被客戶端或者代理服務器緩存。|
| 411 |1.1 |客戶端發送請求的首部中未包含`Content-Length`. 當使用chunk將數據傳輸到服務器時,需要用`Content-Length`指明每一個chunk的長度, 否則服務器會返回411錯誤|
| 412 |1.1 ||
| 413 |1.1 |請求體過大, 服務器無法處理。服務器返回413的同時會中斷TCP連接,以防后面發送更多的請求。|
| 414 |1.1 |客戶端所請求的 URI 超過了服務器允許的范圍。造成這個的原因可能是 1. 誤將POST請求寫成了GET請求 2. 黑客試圖攻擊服務器以圖找到漏洞|
| 415 |1.1 |服務器不支持的媒體類型。對于媒體類型的探測可能利用`Content-Type`首部,也可能具體檢測實體的類型, 具體看服務器的實現|
| 421 |2 |部分資源獲取時,所指定的部分無法滿足。比如一個資源的大小是100字節, 客戶端請求第120到150字節部分,則服務器無法滿足請求|
| **500** |1.0, 1.1 |服務器內部異常, 通常是應用程序在處理請求時出bug了|
| 501 |1.0, 1.1 |請求方法不支持, 比如PUT,DELETE請求在HTTP 1.1才開始支持, 如果是1.0版本發送該請求,則會返回501|
| **502** |1.0, 1.1 |網關或代理無法收到上游應用的響應結果。比如Nginx代理了Rails程序,而Rails程序已經停止運行,或代理到無法處理請求的服務,則請求Nginx時會返回502|
| **503** |1.0, 1.1 |由于臨時的服務器維護或者過載,服務器當前無法處理請求|
| **504** |1.1 |網關超時,代理或者網關在規定的時間內無法獲得上游服務的響應|
| 505 |1.1 |服務器不支持客戶端請求的HTTP的版本|
HTTP 0.9 由于響應報文只有響應實體(body), 所以并無狀態碼。
## HTTP 首部
| 首部 | 支持協議版本 | 類型|說明|可選值|
| --- | --- |--- |--- |--- |
| Date | 1.0 |通用 |--- |--- |
| Pragma | 1.0 |通用 |--- |--- |
| Authorization | 1.0 |請求 |--- |--- |
| From | 1.0 |請求 |--- |--- |
| User-Agent | 1.0 |請求 |--- |--- |
| If-Modified-Since | 1.0 |請求 |--- |--- |
| Referer | 1.0 |請求 |--- |--- |
| Allow |1.0 |響應 |--- |--- |
| Content-Type | 1.0 |響應 |--- |--- |
| Content-Type | 1.0 |響應 |--- |--- |
|Content-Length |1.0 | 響應|--- |--- |
|Content-Encoding |1.0 |響應 |--- |--- |
|Expires | 1.0 | 響應|--- |--- |
| Last-Modified |1.0 | 響應|--- |--- |
| Location |1.0 | 響應|--- |--- |
|Server | 1.0 |響應 |--- |--- |
| WWW-Authenticate |1.0 | 響應|--- |--- |
## HTTP 2.0
## HTTPS詳解
## 專題: Web應用典型網絡架構
隨著Web技術的發展,在應對大流量,Web應用的穩定性,可訪問性方面,已經有一套比較成熟的網絡架構了,下圖Web應用的典型網絡架構。雖然各大公司在很多細節方面會有所改進,但是大體結構不會脫離這個。

目前主要的細節改進主要體現在:
1. 智能DNS: 用戶訪問相關Web應用前,服務器會根據域名查詢域名所解析的IP,普通DNS直接返回域名的固定IP, 而智能DNS可以根據用戶的具體情況返回不同的IP, 比如用戶訪問 www.google.com, 如果用戶是電信寬帶,智能DNS會返回谷歌電信機房服務器的IP,如果是聯通寬帶,則返回谷歌聯通機房服務器的IP, 如果是美國用戶,返回谷歌美國機房服務器的IP, 香港用戶則返回香港機房服務器的IP. 總之,智能DNS可以根據用戶的請求信息返回最適合用戶訪問的服務器的IP,具體策略可以在智能DNS上設置。
2. 動態CDN:普通CDN
3. WAF
4. ELB
5. 內部
## 專題: 跨域請求
## 專題: 緩存策略
## 專題: 性能優化
## 專題: 安全