HTTP協議

什麼是HTTP?

HTTP是超文本傳輸協議,是一種用於在萬維網,傳輸文件,不管是HTML文件,仍是圖片,請求等數據的網絡協議。一般HTTP協議是創建在TCP/IP socket通訊基礎之上的。css

HTTP協議是客戶端(client)和服務器(servers)通訊的協議。瀏覽器就是一個客戶端,由於它發送請求給HTTP服務器(也就是web服務器),web服務器返回響應給客戶端。符合HTTP協議的服務器,默認監聽80端口,固然也能夠從新指定任何一個端口。html

什麼是資源?

HTTP協議是用來傳播資源,而不只僅只是文件。資源就是一個URL連接所對應的一些信息。咱們廣泛見到的資源就是文件,同時資源也多是:經過不一樣編程語言寫的CGI腳本,動態生成,並輸出。返回請求結果的文件。web

學習HTTP,有助於理解資源相似於文件的概念。實際場景中,HTTP資源不是靜態文件,就是服務器端腳本動態生成的結果。django

HTTP的傳輸結構

就像大多數的網絡協議,HTTP也是C/S模式:客戶端向服務器發送請求鏈接和請求的信息內容,服務器返回響應信息。一般包含請求的資源。服務器發送完響應後,關閉鏈接。(HTTP是一個無狀態的鏈接)編程

請求和響應的格式長得差很少,它們都是由:瀏覽器

  • 一條初始行
  • 零或多條頭信息
  • 一個空行
  • 一個可選的消息體
    組成的,格式以下,:
<initial line, different for request vs. response> Header1: value1 Header2: value2 Header3: value3 <optional message body goes here, like file contents or query data; it can be many lines long, or even binary data $&*%@!^$@>

initial lineheaders必須由回車結尾。緩存

Initial Request Line(請求行)

請求的第一行和響應的第一行不同!請求的第一行有三個部分:方法名,請求資源的路徑(也就是/分隔的路徑),使用的HTTP協議版本。訪問個人博客首頁時,請求頭以下:安全

GET / HTTP/1.1

注意:服務器

  • GET方法是HTTP中最經常使用的方法,他的意思是:‘我要獲得這個資源’。另一個經常使用的方法是:POST後面會在作詳細的說明。方法名所有大寫。
  • 域名後面的部分就是路徑,默認是‘/’。
  • HTTP版本形如:‘HTTP/x.x’,所有大寫。

Initial Response Line(響應聲明行或狀態行)

響應的初始行,稱做‘狀態行’。也是由三個部分組成的:HTTP協議版本,狀態碼,狀態碼描述。一樣以我博客爲例子,狀態行以下:網絡

HTTP/1.1 304 Not Modified

注意

  • HTTP版本的內容,形式跟上面同樣。
  • 狀態碼是三位的整數,第一位一般分爲以下幾類:
    • 1xx 這一類型的狀態碼,表明請求已被接受,須要繼續處理。(消息)
    • 2xx 這一類型的狀態碼,表明請求已成功被服務器接收、理解、並接受。(成功)
    • 3xx 這類狀態碼錶明須要客戶端採起進一步的操做才能完成請求。(重定向)
    • 4xx 這類的狀態碼錶明瞭客戶端看起來可能發生了錯誤,妨礙了服務器的處理。(客戶端錯誤)
    • 5xx 這類狀態碼錶明瞭服務器在處理請求的過程當中有錯誤或者異常狀態發生。(服務器錯誤)

常見的狀態碼:
200 OK:請求成功,接收到資源。
404 Not Found:請求失敗,未找到資源。
301 Moved Permanently:永久性轉移。
302 Moved Temporarily:暫時性轉移。
303 See Other:請求的資源已經移到了另一個URL上了,客戶端會自動跳轉。這個一般是CGI腳本使用redirect,使得客戶端,重定向到另一個URL。
500 Server Error:一個未知的服務器錯誤。

Header Lines(請求頭)

請求頭提供了請求或響應的信息,或者是關於發送的消息體(message body)的信息。

請求頭的格式爲:每條頭信息佔一行例如「Header-Name: value」,以回車結尾。這個格式也被用於郵件等,更加詳細的說明:

  • 請求頭是區分大小寫的
  • 冒號‘:’後面能夠有任意多個空格

下面的兩種格式,效果是同樣的:

Header1: some-long-value-1a, some-long-value-1b HEADER1: some-long-value-1a, some-long-value-1b

HTTP1.0定義了16種頭,沒有強制要帶的。而HTTP1.1定義了46種頭,而且請求時必須帶(Host:)。請求時,一些約定俗稱的規定(不遵照沒問題,可是最好遵照)。

  • From頭:包含誰請求的,或者這個操做作了什麼。
  • User-Agent:它包含了誰請求的信息(用戶身份),例如:User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36

上面說的這些頭,幫助網絡管理員分析問題。這些信息也提供了用戶的身份(這些信息能夠僞造)。
若是你在寫一個‘servers’,考慮返回響應時把下面這些頭加上:

  • Server:相似User-Agent頭,表示的是服務的身份。
  • Last-Modified:記此文件在服務期端最後被修改的時間。一般用於緩存,節省帶寬。例如:Last-Modified: Fri, 31 Dec 1999 23:59:59 GMT

Message Body(消息實體)

一個HTTP消息,頭信息後面可能有一個消息實體。響應的時候,這個消息實體就是:客戶端請求的資源,或者是提示的錯誤信息。請求時,消息實體就是:用戶輸入的數據,或者上傳的文件。

若是一個HTTP消息包含消息實體,那麼一般就會下面的幾個頭,用來描述消息實體,例如:

  • Content-type: 用來表示消息實體的數據類型例如:‘text/html’或者‘image/gif’
  • Content-Length: 表示消息體的大小(bytes)

HTTP交互的例子

例如請求一個文件:http://www.somehost.com/path/file.html

首先與目標網站:'www.somehost.com'的80端口創建起socket鏈接。以後經過socket鏈接發送相似:

GET /path/file.html HTTP/1.0 From: someuser@jmarshall.com User-Agent: HTTPTool/1.0 [blank line here]

這時服務器回返迴響應,內容格式以下:

HTTP/1.0 200 OK Date: Fri, 31 Dec 1999 23:59:59 GMT Content-Type: text/html Content-Length: 1354 <html> <body> <h1>Happy New Millennium!</h1> (more file contents) . . . </body> </html>

發送完響應以後,服務器會關閉這個socket鏈接。

HTTP代理

HTTP代理就是服務器和客戶端的一箇中間程序。它從客戶端接受請求,而後把這些請求再發送給服務器。響應返回的時候也是同理,須要經過代理。

代理一般用於防火牆,局域網的安全等。

當客戶端使用代理,它就會把全部的請求發送給代理,而不是發給服務器。經過代理請求和普通的請求有一點不一樣:第一行,代理請求使用完整的URL,而不是隻有path。例如:

經過代理請求:GET http://www.somehost.com/path/file.html HTTP/1.0 普通請求:GET /path/file.html HTTP/1.0

經過這種方式,代理就知道請求的服務器地址了。

‘寬容待人’

就像常說的:「嚴格的發送,寬容的接收。」你交互信息過程當中,其它的客戶端和服務器,它們發送的信息都有可能有瑕疵。可是,你應該嘗試預料到這些問題,從而使一切正常的運行。下面有些建議:

  • 即便規定必須以回車(CRLF)結尾,可是一些人可能只用換行(LF),因此請同時接受這二者。
  • 發送的消息內部,每個部分必須由一個空行進行隔離。可是,其它程序有可能使用幾個空行來隔離。因此也必定要考慮接受這種狀況。

固然還有一些其餘的狀況,總之要多兼容。

結尾

這是HTTP的基本知識。若是你想知道更多,你須要查看官方的資料。

到此爲止只講理HTTP1.0的知識,下面會講HTTP1.1的知識,因此休息一下,讓咱們升級一下!

HTTP1.1

想不少的協議同樣,HTTP是不斷升級的。HTTP1.1完善了HTTP1.0的一些缺點。總的來講,改善的地方包括:

  • 更快的響應,在一個鏈接上容許多個HTTP的請求和響應。(叫作:HTTP持久鏈接)
  • 增長緩存的支持,節省了帶寬,提升了響應的速度。
  • 更快的響應和生成頁面,由於支持分塊編碼,容許發送的數據能夠分紅多個部分,好處是:發送數據以前,不須要預先知道發送內容的總大小。
  • 由於增長了Host頭字段,web瀏覽器可使用一個IP地址配置多個虛擬web站點。

HTTP1.1須要再服務器和客戶端增長一些額外的東西。接下來的兩個部分,分別講:如何編寫遵循HTTP1.1協議的客戶端和服務器。固然,你若是隻寫客戶端,只須要看客戶端的部分。能夠根據本身的需求選擇閱讀。

HTTP1.1 Clients(客戶端)

爲了符合HTTP1.1,客戶端必須:

  • 每次請求必須包含Host頭。
  • 容許響應是chunked data(分塊傳輸編碼)。
  • 每一個請求,必須在頭信息中,聲明是否支持持久鏈接。
  • 支持響應返回狀態碼:‘100 Continue’。

Host Header(Host頭)

HTTP1.1開始,支持一個IP對應多個虛擬主機。好比:「www.host1.com」和「www.host2.com」能夠是同一個服務器(同一IP)。

一個服務器,有多個域名就像:不一樣的人,共享一個手機。打電話的人知道他們要找誰,可是接電話的人不知道!因此,打來電話的人須要明確的指出他要找誰。同理,每個HTTP請求必須在Host頭中,明確地指出請求的host。例如:

GET /path/file.html HTTP/1.1 Host: www.host1.com:80 [blank line here]

其中:80不需特別指出,由於默認就是訪問80端口。
HTTP1.1協議下的請求,請求頭中必須包含Host。沒有它,每個域名須要一個獨一無二的IP地址,IP地址的數據量正在急劇減小,而網站(域名)卻以爆炸的速度增加。Host能夠有效的緩解IP地址緊張的現狀。

分塊傳輸編碼

若是服務器想要在知道響應數據總量以前,就發送響應(好比特別長的響應內容,這樣計算數據總量會耗費很長的時間),那麼就會用到‘分塊傳輸編碼’。它把完整的響應數據,分紅不少個大小相同的數據塊,而後發送。你能夠一樣接收這樣的數據,由於頭已經包含了‘Transfer-Encoding: chunked’。全部的HTTP1.1客戶端必須能夠接收分塊的信息。

分塊的消息內容須要包含:有一行是‘0’,用於表示內容的結束。有'footers',和一個空行。必須包含兩個部分:

  • 有一行是用16進製表示這個塊的大小,後面的額外參數用分號隔離。
  • 數據以回車分割。

例如:

HTTP/1.1 200 OK Date: Fri, 31 Dec 1999 23:59:59 GMT Content-Type: text/plain Transfer-Encoding: chunked a; ignore-stuff-here abcdefghijklmnopqrstuvwxyz 10 234567890abcdef 0 some-footer: some-value another-footer: another-value [blank line here]

不要忘記,最後有一行空行。文本內容的大小是42bytes(1a+10=16+10+16=42),內容是:‘abcdefghijklmnopqrstuvwxyz1234567890abcdef’。

分塊數據能夠包含任意的二進制數據。下面內容是同樣的,可是沒有使用‘分塊傳輸編碼’的響應。以下:

HTTP/1.1 200 OK Date: Fri, 31 Dec 1999 23:59:59 GMT Content-Type: text/plain Content-Length: 42 some-footer: some-value another-footer: another-value abcdefghijklmnopqrstuvwxyz1234567890abcdef

Persistent Connections(持久鏈接)

在HTTP1.0以前,每一個請求和響應完成後都會關閉TCP鏈接,因此每次獲取都是一個單獨,獨立的���接。建立和關閉TCP鏈接花費大量的CPU資源,帶寬和內存。實際操做中,組成一個網頁的多個文件都是在一臺服務器上。因此,多個請求和響應均可以經過一個持久鏈接進行傳輸。

HTTP1.1默認是持久鏈接,因此若是沒有特殊的需求使用的就是持久鏈接。只須要創建一個鏈接,就是能夠發送多個請求和讀取返回的響應。若是你這麼作,必定要注意讀取響應返回的長度,以確保正確的區別他們。

若是一個客戶端在請求頭中聲明瞭「Connection: close」的話,鏈接會在響應送達後關閉。好比,這種操做的場景:若是你知道這是這個鏈接的,最後一個請求。一樣的,若是響應頭包含這個聲明,服務器會在發送完響應以後,關閉鏈接。因此,客戶端不能經過這個連接,發送任何請求。

服務器可能在發送任何一個響應以前關閉鏈接。因此,客戶端必須保持時刻檢查Persistent Connections頭的值。以確保選擇的鏈接是通路。

100 Connections(100狀態碼)

客戶端使用HTTP1.1協議向服務器發送請求,服務器可能返回臨時響應:‘100 Coninue’。它表示服務器接收到了請求的第一部分,後面還有一些緩慢的數據傳輸。因此,不管如何HTTP1.1的客戶端必須能正確處理‘100’狀態碼的響應。

返回的‘100 Continue’狀態碼,和咱們前面說的‘200 OK’都是同樣的,符合正常的響應的格式。惟一不一樣的是,響應的內容。以下:

HTTP/1.1 100 Continue #沒有過完整的響應內容。 HTTP/1.1 200 OK Date: Fri, 31 Dec 1999 23:59:59 GMT Content-Type: text/plain Content-Length: 42 some-footer: some-value another-footer: another-value abcdefghijklmnoprstuvwxyz1234567890abcdef

爲了解決上面的這種狀況(100 狀態碼沒有數據),一個簡單的HTTP1.1客戶端能夠經過socket讀取響應;若是狀態碼是100,就忽略此次響應,轉而讀取下個響應。

HTTP1.1服務器

爲了聽從HTTP1.1,服務器必須:

  • 從客戶端的請求中獲得host頭。
  • 接受絕對url的請求。
  • 能夠接收分塊傳輸編碼。
  • 支持‘持續鏈接’。
  • 恰當的使用‘100 Continue’。
  • 每一個響應中包含‘Date’頭。
  • 可以處理‘If-Modified-Since’和‘If-Unmodified-Since’頭。
  • 最起碼要支持‘GET’和‘HEAD’方法。
  • 兼容HTTP1.0的請求。

須要Host頭

每一個請求,必須包含Host頭,不然就會返回‘400 Bad Request’響應,以下:

HTTP/1.1 400 Bad Request Content-Type: text/html Content-Length: 111 <html><body> <h2>No Host: header received</h2> HTTP 1.1 requests must include the Host: header. </body></html>

接受絕對地址

Host頭其實是一個過渡的解決區別host的辦法。之後的HTTP版本,請求將要使用絕對地址代替路徑,好比:GET http://www.somehost.com/path/file.html HTTP/1.2

HTTP1.1服務器必須接受這種格式的請求,儘管HTTP1.1客戶端不發送這樣的請求。若是客戶端沒有host頭,服務器還必需要報告錯誤。

分塊傳輸編碼

就像HTTP1.1客戶必須接受分塊的響應,服務器必須接受分塊的請求。服務器不須要生成,分塊的信息。只要可以接受分塊請求就能夠了。

持久鏈接

若是HTTP1.1客戶端經過一個鏈接,發送了多個請求。爲了支持持久鏈接,服務器返回響應的順序應該和請求的順序是同樣的。

若是過一個請求包含‘Connection: close’頭,表示這是這個鏈接的最後一個請求,服務器須要在返回響應後關閉鏈接。服務器也會關閉超時閒置的鏈接。(一般設置10s超時)

若是你不想支持持久鏈接,響應頭中包含‘Connection: close’。這就是表示:返回當前這個響應以後,鏈接就會關閉。正確的支持HTTP1.1客戶端能正確的接受這個頭信息。

100 Coninue

正如HTTP1.1客戶端那段描述的那樣,這個響應是爲了處理反應慢的鏈接的。

當一個HTTP1.1服務器收到一個HTTP1.1請求,若是不是返回‘100 Continue’就是錯誤代碼。若是它發送‘100 Continue’響應,那麼接下來服務還會發送另一個響應。‘100 Continue’不須要頭,可是必須含有空行。以下:

HTTP/1.1 100 Continue [blank line here] [another HTTP response will go here]

不要向HTTP1.0客戶端發送‘100 Continue’

Date頭

緩存是HTTP1.1的一個重大改善,同時離不開響應的時間戳。因此,服務器返回的每一個響應,必須包含Date頭,表示當前時間。格式以下:Date: Fri, 31 Dec 1999 23:59:59 GMT

除了‘1xx’的狀態碼的響應,全部的響應必須包括Date頭。時間同一用:格林威治標準時間表示。

If-Modified-Since和If-Unmodified-Since頭

避免發送沒必要要的資源,這樣就節省了帶寬。HTTP1.1定義了‘If-Modified-Since’和‘If-Unmodified-Since’請求頭。用於表示:「只有在這個時間以後修改過的才發送」;客戶端不需這些,可是HTTP1.1須要這些信息。

不幸的是,早期的HTTP版本,時間值的格式不統一,例如:

If-Modified-Since: Fri, 31 Dec 1999 23:59:59 GMT If-Modified-Since: Friday, 31-Dec-99 23:59:59 GMT If-Modified-Since: Fri Dec 31 23:59:59 1999

因此此次,HTTP統一使用格林威治標準時間表示。

儘管服務器必須接受三種時間格式,HTTP1.1客戶端和服務器只能生成一種時間格式。若是沒有這個頭,請求將返回不成功的狀態碼。

If-Modified-Since頭被用在GET請求上。若是請求資源在給的這段時間修改過,忽略這個頭,正常的返回資源。不然返回‘304 Not Modified’響應,包含Date頭,同時沒有消息實體。好比:

HTTP/1.1 304 Not Modified Date: Fri, 31 Dec 1999 23:59:59 GMT [blank line here]

If-Unmodified-Since頭和If-Modified-Since頭是類似的,可是不能用於任何方法。指定的請求資源只有在字段值內指定的日期時間以後,未發生更新的狀況下,才能處理請求。若是在指定日期時間後發生更新,則以狀態碼412 Precondition Failed做爲響應返回。例如:

HTTP/1.1 412 Precondition Failed [blank line here]

支持GET和HEAD方法

HTTP1.1服務器必須支持GET和HEAD方法。若是你正在使用CGI腳本,你須要也支持POST方法。

HTTP1.1定義的其它四個方法(PUT, DELETE, OPTIONS, and TRACE),它們不常常被用到。若是客戶端請求的方法,服務器不支持,則返回‘501 Not Implemented’,以下:

HTTP/1.1 501 Not Implemented [blank line here]

支持HTTP1.0請求

爲了兼容老的瀏覽器,HTTP1.1服務器必須支持HTTP1.0請求。當一個請求使用HTTP1.0:

  • 不須要Host頭
  • 不能發‘100 Continue’響應
相關文章
相關標籤/搜索