HTTP協議學習(上)
阿新 • • 發佈:2019-01-09
這時,就可以輸入HTTP協議本身了:
GET / HTTP/1.1
Host: www.baidu.com
輸入完之後,連敲兩個回車,就會出現以下內容:
HTTP/1.1 200 OK
Date: Sat, 29 Oct 2016 10:12:11 GMT
Content-Type: text/html
Content-Length: 14613
Last-Modified: Tue, 18 Oct 2016 06:34:00 GMT
Connection: Keep-Alive
。。。略
Pragma: no-cache
Cache-control: no-cache
Accept-Ranges: bytes
<!DOCTYPE html><!--STATUS OK-->
<html>
。。。。。。略
</html>
請求頭分為三個部分:請求行、請求頭、請求體(可選)。每一部分都必須以<CR><LF>結尾,也就是一個回車。
這裡需要注意的是,請求頭和請求體之間需要加一個空行,即此行內容只有只有<CR><LF>。為什麼要這樣呢?這得從請求頭說起,請求頭實際上是一組鍵值對,每個鍵值對的格式是“鍵: 值”,注意冒號後面有個空格。但是鍵值對之間如何分隔呢?答案是<CR><LF>。我試過把兩個鍵值對放在一行,中間用一個空格分開,然後伺服器主動關閉連線了。。。這說明,鍵值對之間必須以回車分隔。
然後問題來了,請求頭內部就以回車來分隔了,那它如何跟請求體分隔呢?所以,只有加一個空行了。
當然,我是一個善於思考的淫,我很好奇為啥請求行與請求頭之間不來一個空行呢?甚至,三者之間為什麼一定要分隔呢?
對於第二個問題,三者解讀的方式應該是不一樣的,所以必須要分隔。比如請求行,一共三個部分,分別是請求方法、請求資源的路徑和協議版本,它們之間以空格分隔,當遇到第一個回車時,意味著請求行的結束,下面的資料就是請求頭了(第一個問題順便解決)。但是請求頭每個鍵值對都是一行,到底遇到哪個回車才算完呢?所以需要一個空行代表請求頭的結束。在這之後就是請求體了,這部分是可選的,基本上只有請求方法是POST的時候,這部分才是必須的。同樣的,這部分也需要一個回車作為結束標誌。
好,下面分析請求行。先看第二部分,即資源路徑。在寫這個路徑的時候,我們不必再加上IP和埠號了,因為TCP已經幫HTTP做好了——IP定位到某臺機器,而埠號定位到機器上的某個程序。這時候,我們可以認為請求的資源是存放一個檔案系統中的,尤其是靜態資源,動態資源先不管。根路徑“/”就代表了程序所規定的根目錄,各級子路徑代表對應的子目錄。要注意的是,“/”所代表的根目錄未必是該伺服器檔案系統真正的根目錄。實際上,出於安全的考慮,前者肯定不能是真正的根目錄,只能是某個子目錄。因為一旦黑客通過攻擊該程序而獲取了整臺機器的最高許可權,那就什麼都完了。
第三部分是協議版本,目前HTTP最新的版本時1.1,這也是使用最廣泛的一個版本。不過為了學習,我們還是看一下最初的版本長啥樣:
GET /index.html
。。。。是的,只有一行,兩部分。第一部分是請求方法,只有GET一個型別,且因為是第一版(HTTP/0.9),連版本號都省略了。對這個請求的迴應,也只能是HTML字串,不能有其他東西。。多簡陋啊,所以有了第二個版本HTTP/1.0,首先就是增加了POST和HEAD兩個請求方法,同時由於版本多了,所以請求行多了一個協議版本部分;然後就是增加了請求頭,比如User-Agent、Accept等。
下面以HTTP/1.1為藍本介紹請求格式。
首先是常見的請求方法:
GET - get嘛,就是“獲取”的意思,向伺服器請求特定的、由URI標識的資源,但不會改變伺服器的資料,僅僅是被動的接收。
POST - 用於向指定URI提交資料,資料被包含在請求體中。正如上文所說,POST請求是在第二個版本中增加的,因為第一個版本中只有GET,瀏覽器只能被動接收伺服器的資料;但一些場景下,需要瀏覽器主動向伺服器傳送資料,以對伺服器產生影響。這就是POST方法的用途。
一般情況下,我們只會用到這兩個請求方法,面試的時候經常會被問到這兩個方法的區別,在這裡也捎帶說一下:
1、GET方法實際上也可以向伺服器傳送資料,但是這個資料依然不會對伺服器造成改變,這些引數只是為了限定查詢條件,類似於SQL查詢的where子句;POST方法也可以不帶任何引數,純粹當個GET來使,只不過有點浪費而已。
2、GET方法的引數是放在URL裡面,在請求資源路徑的後面用一個問號分隔;引數的形式也是鍵值對,但是使用等號相連;鍵值對之間以“&”符號分隔。然而GET的引數有個限制,因為引數是放在URL裡面,但是URL的總長度是有限制的,所以GET方法的引數也不能太長,最多隻有1024位元組。而POST方法將引數置於請求體中,理論上是沒有長度限制的。
更正(來源不詳,是我抄的):
因為GET是通過URL提交資料,那麼GET可提交的資料量就跟URL的長度有直接關係了。而實際上,URL不存在引數上限的問題,HTTP協議規範沒有對URL長度進行限制。這個限制是特定的瀏覽器及伺服器對它的限制。IE對URL長度的限制是2083位元組(2K+35)。對於其他瀏覽器,如Netscape、FireFox等,理論上沒有長度限制,其限制取決於作業系統的支援。注意這是限制是整個URL長度,而不僅僅是你的引數值資料長度。
3、傳送給伺服器的時候,GET的引數會直接顯示在位址列裡面;而POST的引數不顯示在位址列裡,而後臺傳送,想要檢視只能通過瀏覽器的控制檯。置於所謂的安全性,講真,對黑客來說沒太大區別,普通人也不會有“從URL裡面找到密碼”的意識。
4、後臺解析方式不同。在ASP中,服務端獲取GET請求引數用Request.QueryString,獲取POST請求引數用Request.Form。在JSP中,用request.getParameter(\"XXXX\")來獲取,雖然jsp中也有request.getQueryString()方法,但使用起來比較麻煩,比如:傳一個test.jsp?name=hyddd&password=hyddd,用request.getQueryString()得到的是:name=hyddd&password=hyddd。在PHP中,可以用$_GET和$_POST分別獲取GET和POST中的資料,而$_REQUEST則可以獲取GET和POST兩種請求中的資料。
更多的區別參見 這裡,還有一篇標題很二的文章也講二者的區別,都他媽分析到TCP層面上了,我也是服了。
PUT - 向指定資源位置上傳其最新內容。
DELETE - 請求伺服器刪除Request-URI所標識的資源。
這兩個方法用的很少,理論上它們可以做到的POST都可以做到,先不多說。
OPTIONS - 返回伺服器針對特定資源所支援的HTTP請求方法。也可以利用向Web伺服器傳送'*'的請求來測試伺服器的功能性。看個例子:
[email protected]:$ telnet localhost 8080
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
OPTIONS * HTTP/1.1
host: localhost:8080
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Allow: GET, HEAD, POST, PUT, DELETE, OPTIONS
Content-Length: 0
Date: Sat, 29 Oct 2016 23:59:08 GMT
HEAD- 向伺服器索要與GET請求相一致的響應,只不過響應體將不會被返回。這一方法可以在不必傳輸整個響應內容的情況下,就可以獲取包含在響應訊息頭中的元資訊。該方法常用於測試超連結的有效性,是否可以訪問,以及最近是否更新。栗子:
[email protected]:$ telnet localhost 8080
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
HEAD / HTTP/1.1
host: localhost:8080
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=ISO-8859-1
Transfer-Encoding: chunked
Date: Sun, 30 Oct 2016 14:52:37 GMT
TRACE- 回顯伺服器收到的請求,主要用於測試或診斷。這個用的也很少,略。
然後是常見的請求頭的屬性:
Host:客戶端IP和埠,傳送請求時,該報頭域是必需的。我試過不加這個,真不行。。瀏覽器傳送的請求訊息中,就會包含Host請求報頭域,如下:
Host:www.guet.edu.cn
此處使用預設埠號80,若指定了埠號,則變成:Host:www.guet.edu.cn:指定埠號
User-Agent:瀏覽器資訊
Content-Length:表示請求訊息正文的長度。
Cookie:最重要的請求頭之一, 將cookie的值傳送給HTTP伺服器。
Accept:瀏覽器能接收的資料型別
Accept-encoding:瀏覽器能夠進行解碼的資料編碼方式,比如gzip。
Accept-charset:瀏覽器可接受的字符集。
Accept-Language:瀏覽器所希望的語言種類,當伺服器能夠提供一種以上的語言版本時要用到。
Authorization:請求報頭域主要用於證明客戶端有權檢視某個資源。當瀏覽器訪問一個頁面時,如果收到伺服器的響應程式碼為401(未授權),可以傳送一個包含Authorization請求報頭域的請求,要求伺服器對其進行驗證。
Pragma:指定“no-cache”值表示伺服器必須返回一個重新整理後的文件,即使它是代理伺服器而且已經有了頁面的本地拷貝;在HTTP/1.1版本中,它和Cache-Control:no-cache作用一模一樣。Pargma只有一個用法, 例如: Pragma: no-cache
GET / HTTP/1.1
Host: www.baidu.com
輸入完之後,連敲兩個回車,就會出現以下內容:
HTTP/1.1 200 OK
Date: Sat, 29 Oct 2016 10:12:11 GMT
Content-Type: text/html
Content-Length: 14613
Last-Modified: Tue, 18 Oct 2016 06:34:00 GMT
Connection: Keep-Alive
。。。略
Pragma: no-cache
Cache-control: no-cache
Accept-Ranges: bytes
<!DOCTYPE html><!--STATUS OK-->
<html>
。。。。。。略
</html>
請求頭分為三個部分:請求行、請求頭、請求體(可選)。每一部分都必須以<CR><LF>結尾,也就是一個回車。
這裡需要注意的是,請求頭和請求體之間需要加一個空行,即此行內容只有只有<CR><LF>。為什麼要這樣呢?這得從請求頭說起,請求頭實際上是一組鍵值對,每個鍵值對的格式是“鍵: 值”,注意冒號後面有個空格。但是鍵值對之間如何分隔呢?答案是<CR><LF>。我試過把兩個鍵值對放在一行,中間用一個空格分開,然後伺服器主動關閉連線了。。。這說明,鍵值對之間必須以回車分隔。
然後問題來了,請求頭內部就以回車來分隔了,那它如何跟請求體分隔呢?所以,只有加一個空行了。
當然,我是一個善於思考的淫,我很好奇為啥請求行與請求頭之間不來一個空行呢?甚至,三者之間為什麼一定要分隔呢?
對於第二個問題,三者解讀的方式應該是不一樣的,所以必須要分隔。比如請求行,一共三個部分,分別是請求方法、請求資源的路徑和協議版本,它們之間以空格分隔,當遇到第一個回車時,意味著請求行的結束,下面的資料就是請求頭了(第一個問題順便解決)。但是請求頭每個鍵值對都是一行,到底遇到哪個回車才算完呢?所以需要一個空行代表請求頭的結束。在這之後就是請求體了,這部分是可選的,基本上只有請求方法是POST的時候,這部分才是必須的。同樣的,這部分也需要一個回車作為結束標誌。
好,下面分析請求行。先看第二部分,即資源路徑。在寫這個路徑的時候,我們不必再加上IP和埠號了,因為TCP已經幫HTTP做好了——IP定位到某臺機器,而埠號定位到機器上的某個程序。這時候,我們可以認為請求的資源是存放一個檔案系統中的,尤其是靜態資源,動態資源先不管。根路徑“/”就代表了程序所規定的根目錄,各級子路徑代表對應的子目錄。要注意的是,“/”所代表的根目錄未必是該伺服器檔案系統真正的根目錄。實際上,出於安全的考慮,前者肯定不能是真正的根目錄,只能是某個子目錄。因為一旦黑客通過攻擊該程序而獲取了整臺機器的最高許可權,那就什麼都完了。
第三部分是協議版本,目前HTTP最新的版本時1.1,這也是使用最廣泛的一個版本。不過為了學習,我們還是看一下最初的版本長啥樣:
GET /index.html
。。。。是的,只有一行,兩部分。第一部分是請求方法,只有GET一個型別,且因為是第一版(HTTP/0.9),連版本號都省略了。對這個請求的迴應,也只能是HTML字串,不能有其他東西。。多簡陋啊,所以有了第二個版本HTTP/1.0,首先就是增加了POST和HEAD兩個請求方法,同時由於版本多了,所以請求行多了一個協議版本部分;然後就是增加了請求頭,比如User-Agent、Accept等。
下面以HTTP/1.1為藍本介紹請求格式。
首先是常見的請求方法:
GET - get嘛,就是“獲取”的意思,向伺服器請求特定的、由URI標識的資源,但不會改變伺服器的資料,僅僅是被動的接收。
POST - 用於向指定URI提交資料,資料被包含在請求體中。正如上文所說,POST請求是在第二個版本中增加的,因為第一個版本中只有GET,瀏覽器只能被動接收伺服器的資料;但一些場景下,需要瀏覽器主動向伺服器傳送資料,以對伺服器產生影響。這就是POST方法的用途。
一般情況下,我們只會用到這兩個請求方法,面試的時候經常會被問到這兩個方法的區別,在這裡也捎帶說一下:
1、GET方法實際上也可以向伺服器傳送資料,但是這個資料依然不會對伺服器造成改變,這些引數只是為了限定查詢條件,類似於SQL查詢的where子句;POST方法也可以不帶任何引數,純粹當個GET來使,只不過有點浪費而已。
2、GET方法的引數是放在URL裡面,在請求資源路徑的後面用一個問號分隔;引數的形式也是鍵值對,但是使用等號相連;鍵值對之間以“&”符號分隔。然而GET的引數有個限制,因為引數是放在URL裡面,但是URL的總長度是有限制的,所以GET方法的引數也不能太長,最多隻有1024位元組。而POST方法將引數置於請求體中,理論上是沒有長度限制的。
更正(來源不詳,是我抄的):
因為GET是通過URL提交資料,那麼GET可提交的資料量就跟URL的長度有直接關係了。而實際上,URL不存在引數上限的問題,HTTP協議規範沒有對URL長度進行限制。這個限制是特定的瀏覽器及伺服器對它的限制。IE對URL長度的限制是2083位元組(2K+35)。對於其他瀏覽器,如Netscape、FireFox等,理論上沒有長度限制,其限制取決於作業系統的支援。注意這是限制是整個URL長度,而不僅僅是你的引數值資料長度。
3、傳送給伺服器的時候,GET的引數會直接顯示在位址列裡面;而POST的引數不顯示在位址列裡,而後臺傳送,想要檢視只能通過瀏覽器的控制檯。置於所謂的安全性,講真,對黑客來說沒太大區別,普通人也不會有“從URL裡面找到密碼”的意識。
4、後臺解析方式不同。在ASP中,服務端獲取GET請求引數用Request.QueryString,獲取POST請求引數用Request.Form。在JSP中,用request.getParameter(\"XXXX\")來獲取,雖然jsp中也有request.getQueryString()方法,但使用起來比較麻煩,比如:傳一個test.jsp?name=hyddd&password=hyddd,用request.getQueryString()得到的是:name=hyddd&password=hyddd。在PHP中,可以用$_GET和$_POST分別獲取GET和POST中的資料,而$_REQUEST則可以獲取GET和POST兩種請求中的資料。
更多的區別參見
PUT - 向指定資源位置上傳其最新內容。
DELETE - 請求伺服器刪除Request-URI所標識的資源。
這兩個方法用的很少,理論上它們可以做到的POST都可以做到,先不多說。
OPTIONS - 返回伺服器針對特定資源所支援的HTTP請求方法。也可以利用向Web伺服器傳送'*'的請求來測試伺服器的功能性。看個例子:
[email protected]:$ telnet localhost 8080
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
OPTIONS * HTTP/1.1
host: localhost:8080
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Allow: GET, HEAD, POST, PUT, DELETE, OPTIONS
Content-Length: 0
Date: Sat, 29 Oct 2016 23:59:08 GMT
HEAD- 向伺服器索要與GET請求相一致的響應,只不過響應體將不會被返回。這一方法可以在不必傳輸整個響應內容的情況下,就可以獲取包含在響應訊息頭中的元資訊。該方法常用於測試超連結的有效性,是否可以訪問,以及最近是否更新。栗子:
[email protected]:$ telnet localhost 8080
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
HEAD / HTTP/1.1
host: localhost:8080
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=ISO-8859-1
Transfer-Encoding: chunked
Date: Sun, 30 Oct 2016 14:52:37 GMT
TRACE- 回顯伺服器收到的請求,主要用於測試或診斷。這個用的也很少,略。
然後是常見的請求頭的屬性:
Host:客戶端IP和埠,傳送請求時,該報頭域是必需的。我試過不加這個,真不行。。瀏覽器傳送的請求訊息中,就會包含Host請求報頭域,如下:
Host:www.guet.edu.cn
此處使用預設埠號80,若指定了埠號,則變成:Host:www.guet.edu.cn:指定埠號
User-Agent:瀏覽器資訊
Content-Length:表示請求訊息正文的長度。
Cookie:最重要的請求頭之一, 將cookie的值傳送給HTTP伺服器。
Accept:瀏覽器能接收的資料型別
Accept-encoding:瀏覽器能夠進行解碼的資料編碼方式,比如gzip。
Accept-charset:瀏覽器可接受的字符集。
Accept-Language:瀏覽器所希望的語言種類,當伺服器能夠提供一種以上的語言版本時要用到。
Authorization:請求報頭域主要用於證明客戶端有權檢視某個資源。當瀏覽器訪問一個頁面時,如果收到伺服器的響應程式碼為401(未授權),可以傳送一個包含Authorization請求報頭域的請求,要求伺服器對其進行驗證。
Pragma:指定“no-cache”值表示伺服器必須返回一個重新整理後的文件,即使它是代理伺服器而且已經有了頁面的本地拷貝;在HTTP/1.1版本中,它和Cache-Control:no-cache作用一模一樣。Pargma只有一個用法, 例如: Pragma: no-cache