一 為何要學習HTTP協議
http協議就是通訊的雙方共同遵守的標準,就好比要合夥辦事的兩家公司簽署的合同。
openstack中各元件是基於restful api通訊的,restful api可以單純的理解為一個url地址:http://www.egon.com/index.html
因而不管研究openstack內的任何元件,都離不開http協議,要想成為一名合格的架構師,這些必須搞明白
二 使用者上網過程
使用者從開啟瀏覽器輸入網址到看到頁面經歷了什麼?
1.首先必須web伺服器執行
2.客戶端執行瀏覽器軟體
3.使用者輸入http://www.sina.com.cn/
4.客戶端瀏覽器處理http://www.sina.com.cn/,發起查詢本地DNS操作,將www.sina.com.cn->202.103.0.33
5.客戶端瀏覽器傳送http請求http://202.103.0.33:80/index.html (注意:80是web伺服器的預設埠,index.html是預設的請求的資源)
6.服務端web服務收到該http的request請求頭,從請求頭中獲取客戶端的方法GET/POST.../index.html這個路徑,及客戶端請求的其他相關資訊
7.服務端web服務根據取得的資訊,回覆respone響應頭
響應頭中包含:
響應程式碼:200表示成功,3xx表示重定向,4xx表示客戶端傳送的請求有錯誤,5xx表示伺服器端處理時發生了錯誤;
響應型別:由Content-Type指定;
以及其他相關的Header;
通常伺服器的HTTP響應會攜帶內容,也就是有一個Body,包含響應的內容,網頁的HTML原始碼就在Body中,壓縮後返回給客戶端。
8.客戶端瀏覽器收到服務端發來的資料,解壓後解析html內容,使用者就看到網頁內容了
9.html內可能巢狀其他的連結,比方說圖片、視訊、javascript指令碼,flash等,客戶端瀏覽器會繼續發起http請求來獲取它們。這樣來自圖片和視訊的壓力就被分散到各個伺服器,一個站點由無數個站點相互連線起來,就形成了World Wide Web,簡稱WWW。
綜上,其實就是一次http請求-響應的流程
三 HTTP協議
web伺服器即socket服務端,瀏覽器即socket客戶端,這叫B/S架構,B與S之間通訊的標準是HTTP協議(目前版本1.1,比1.0好在可以允許多個http請求複用一個TCP連線)
part1 http協議概述
HTTP(hypertext transport protocol),即超文字傳輸協議。這個協議詳細規定了瀏覽器和全球資訊網伺服器之間互相通訊的規則。
HTTP就是一個通訊規則,通訊規則規定了客戶端傳送給伺服器的內容格式,也規定了伺服器傳送給客戶端的內容格式。其實我們要學習的就是這個兩個格式!客戶端傳送給伺服器的格式叫“請求協議”;伺服器傳送給客戶端的格式叫“響應協議”。
特點:
- HTTP叫超文字傳輸協議,基於請求/響應模式的!
- HTTP是無狀態協議,FTP是有狀態。
URL:統一資源定位符,就是一個網址:協議名://域名:埠/路徑,例如:http://www.oldboy.cn:80/index.html
part2 請求協議
請求協議的格式如下:
請求首行; // 請求方式 請求路徑 協議和版本,例如:GET /index.html HTTP/1.1 請求頭資訊;// 請求頭名稱:請求頭內容,即為key:value格式,例如:Host:localhost 空行; // 用來與請求體分隔開 請求體。 // GET沒有請求體,只有POST有請求體。
瀏覽器傳送給伺服器的內容就這個格式的,如果不是這個格式伺服器將無法解讀!在HTTP協議中,請求有很多請求方法,其中最為常用的就是GET和POST。不同的請求方法之間的區別,後面會一點一點的介紹。
GET請求
HTTP預設的請求方法就是GET
* 沒有請求體
* 資料必須在1K之內!
* GET請求資料會暴露在瀏覽器的位址列中
GET請求常用的操作:
1. 在瀏覽器的位址列中直接給出URL,那麼就一定是GET請求
2. 點選頁面上的超連結也一定是GET請求
3. 提交表單時,表單預設使用GET請求,但可以設定為POST
GET / HTTP/1.1 Host: www.sina.com.cn Connection: keep-alive Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 Accept-Encoding: gzip, deflate, sdch Accept-Language: zh-CN,zh;q=0.8 Cookie: SINAGLOBAL=223.71.229.3_1484011627.259786; Apache=223.71.229.3_1484011627.259788
- GET / HTTP/1.1:GET請求,請求伺服器路徑為 http://www.sina.com.cn/ ,協議為1.1;
- Host:www.sina.com.cn 請求的主機名為www.sina.com.cn;
- Connection: keep-alive:客戶端支援的連結方式,保持一段時間連結,預設為3000ms;
- User-Agent:.......:與瀏覽器和OS相關的資訊。有些網站會顯示使用者的系統版本和瀏覽器版本資訊,這都是通過獲取User-Agent頭資訊而來的;
- Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8:告訴伺服器,當前客戶端可以接收的文件型別,其實這裡包含了*/*,就表示什麼都可以接收;
- Accept-Encoding: gzip, deflate,sdch:支援的壓縮格式。資料在網路上傳遞時,可能伺服器會把資料壓縮後再傳送;
- Accept-Language: zh-cn,zh;q=0.5:當前客戶端支援的語言,可以在瀏覽器的工具選項中找到語言相關資訊;
-
Cookie:SINAGLOBAL=223.71.229.3_1484011627.259786; Apache=223.71.229.3_1484011627.259788
因為不是第一次訪問這個地址,所以會在請求中把上一次伺服器響應中傳送過來的Cookie在請求中一併傳送去過;這個Cookie的名字為SINAGLOBAL
POST請求
(1). 資料不會出現在位址列中
(2). 資料的大小沒有上限
(3). 有請求體
(4). 請求體中如果存在中文,會使用URL編碼!
<!DOCTYPE html> <html> <body> <form method="post"> 使用者名稱:<br> <input type="text" name="username"> <br> 密碼:<br> <input type="text" name="password"> <input type="submit" value="登入"> </form> </body> </html>
我們都知道Http協議中引數的傳輸是"key=value"這種簡直對形式的,如果要傳多個引數就需要用“&”符號對鍵值對進行分割。如"?name1=value1&name2=value2",這樣在服務端在收到這種字串的時候,會用“&”分割出每一個引數,然後再用“=”來分割出引數值。 針對“name1=value1&name2=value2”我們來說一下客戶端到服務端的概念上解析過程: 上述字串在計算機中用ASCII嗎表示為: 6E616D6531 3D 76616C756531 26 6E616D6532 3D 76616C756532。 6E616D6531:name1 3D:= 76616C756531:value1 26:& 6E616D6532:name2 3D:= 76616C756532:value2 服務端在接收到該資料後就可以遍歷該位元組流,首先一個位元組一個位元組的吃,當吃到3D這位元組後,服務端就知道前面吃得位元組表示一個key,再想後吃,如果遇到26,說明從剛才吃的3D到26子節之間的是上一個key的value,以此類推就可以解析出客戶端傳過來的引數。 現在有這樣一個問題,如果我的引數值中就包含=或&這種特殊字元的時候該怎麼辦。 比如說“name1=value1”,其中value1的值是“va&lu=e1”字串,那麼實際在傳輸過程中就會變成這樣“name1=va&lu=e1”。我們的本意是就只有一個鍵值對,但是服務端會解析成兩個鍵值對,這樣就產生了奇異。 如何解決上述問題帶來的歧義呢?解決的辦法就是對引數進行URL編碼 URL編碼只是簡單的在特殊字元的各個位元組前加上%,例如,我們對上述會產生奇異的字元進行URL編碼後結果:“name1=va%26lu%3D”,這樣服務端會把緊跟在“%”後的位元組當成普通的位元組,就是不會把它當成各個引數或鍵值對的分隔符。 為什麼要進行URL編碼
POST請求是可以有體的,而GET請求不能有請求體。
- Referer: http://localhost:63342/test/test.html:請求來自哪個頁面,例如你在百度上點選連結到了這裡,那麼Referer:http://www.baidu.com;如果你是在瀏覽器的位址列中直接輸入的地址,那麼就沒有Referer這個請求頭了;
- Content-Type: application/x-www-form-urlencoded:表單的資料型別,說明會使用url格式編碼資料;url編碼的資料都是以“%”為字首,後面跟隨兩位的16進位制。
- Content-Length:49:請求體的長度,這裡表示13個位元組。
- username=‘林海峰’:請求體內容!hello是在表單中輸入的資料,keyword是表單欄位的名字
Referer請求頭是比較有用的一個請求頭,它可以用來做統計工作,也可以用來做防盜鏈。
統計工作:我公司網站在百度上做了廣告,但不知道在百度上做廣告對我們網站的訪問量是否有影響,那麼可以對每個請求中的Referer進行分析,如果Referer為百度的很多,那麼說明使用者都是通過百度找到我們公司網站的。
防盜鏈:我公司網站上有一個下載連結,而其他網站盜鏈了這個地址,例如在我網站上的index.html頁面中有一個連結,點選即可下載JDK7.0,但有某個人的微博中盜鏈了這個資源,它也有一個連結指向我們網站的JDK7.0,也就是說登入它的微博,點選連結就可以從我網站上下載JDK7.0,這導致我們網站的廣告沒有看,但下載的卻是我網站的資源。這時可以使用Referer進行防盜鏈,在資源被下載之前,我們對Referer進行判斷,如果請求來自本網站,那麼允許下載,如果非本網站,先跳轉到本網站看廣告,然後再允許下載。
part3 響應協議
3.1 響應內容
響應協議的格式如下:
響應首行; 響應頭資訊; 空行; 響應體。
響應內容是由伺服器傳送給瀏覽器的內容,瀏覽器會根據響應內容來顯示。遇到<img src=''>會開一個新的執行緒載入,所以有時圖片多的話,內容會先顯示出來,然後圖片才一張張載入出來。
#_*_coding:utf-8_*_ #!/usr/bin/env python from wsgiref.simple_server import make_server # 定義application函式: def application(environ, start_response): print(environ) start_response('200 OK', [('Content-Type', 'text/html')]) f=open('test.html','rb') return [f.read()] # 建立一個伺服器,IP地址為空,埠是8000,處理函式是application: httpd = make_server('', 8000, application) print('Serving HTTP on port 8000...') # 開始監聽HTTP請求: httpd.serve_forever()
<!DOCTYPE html> <html> <body> <form> 使用者名稱:<br> <input type="text" name="username"> <br> 密碼:<br> <input type="text" name="password"> <input type="submit" value="登入"> </form> </body> </html>
- HTTP/1.1 200 OK:響應協議為HTTP1.1,狀態碼為200,表示請求成功,OK是對狀態碼的解釋;
- Server:WSGIServer/0.2 CPython/3.6.0:伺服器的版本資訊;
- Content-Type: text/html;charset=UTF-8:響應體使用的編碼為UTF-8;
- Content-Length: 217:響應體為217位元組;
- Set-Cookie: JSESSIONID=C97E2B4C55553EAB46079A4F263435A4; Path=/hello:響應給客戶端的Cookie;
- Date: Wed, 25 Sep 2012 04:15:03 GMT:響應的時間,這可能會有8小時的時區差;
3.2 狀態碼
響應頭對瀏覽器來說很重要,它說明了響應的真正含義。例如200表示響應成功了,302表示重定向,這說明瀏覽器需要再發一個新的請求。
- 200:請求成功,瀏覽器會把響應體內容(通常是html)顯示在瀏覽器中;
- 404:請求的資源沒有找到,說明客戶端錯誤的請求了不存在的資源;
- 500:請求資源找到了,但伺服器內部出現了錯誤;
- 302:重定向,當響應碼為302時,表示伺服器要求瀏覽器重新再發一個請求,伺服器會傳送一個響應頭Location,它指定了新請求的URL地址;
- 304:
當使用者第一次請求index.html時,伺服器會新增一個名為Last-Modified響應頭,這個頭說明了 index.html的最後修改時間,瀏覽器會把index.html內容,以及最後響應時間快取下來。當使用者第 二次請求index.html時,在請求中包含一個名為If-Modified-Since請求頭,它的值就是第一次請 求時伺服器通過Last-Modified響應頭髮送給瀏覽器的值,即index.html最後的修改時間, If-Modified-Since請求頭就是在告訴伺服器,我這裡瀏覽器快取的index.html最後修改時間是這個, 您看看現在的index.html最後修改時間是不是這個,如果還是,那麼您就不用再響應這個index.html 內容了,我會把快取的內容直接顯示出來。而伺服器端會獲取If-Modified-Since值,與index.html 的當前最後修改時間比對,如果相同,伺服器會發響應碼304,表示index.html與瀏覽器上次快取的相 同,無需再次傳送,瀏覽器可以顯示自己的快取頁面,如果比對不同,那麼說明index.html已經做了修 改,伺服器會響應200。
3.3 其他響應頭
告訴瀏覽器不要快取的響應頭:
- Expires: -1;
- Cache-Control: no-cache;
- Pragma: no-cache;
自動重新整理響應頭,瀏覽器會在3秒之後請求http://www.baidu.com:
- Refresh: 3;url=http://www.baidu.com
3.4 HTML中指定響應頭
在HTMl頁面中可以使用<meta http-equiv="" content="">來指定響應頭,例如在index.html頁面中給出<meta http-equiv="Refresh" content="3;url=http://www.baidu.com">,表示瀏覽器只會顯示index.html頁面3秒,然後自動跳轉到http://www.baidu.com.
四 抓包分析HTTP協議
我們可以開啟瀏覽器在選單中選擇”檢視“,“開發者”,"開發者工具",選擇Network來監控瀏覽器與web伺服器之間做的事情。
web伺服器收到的客戶端發來的請求頭
web伺服器發給客戶端的響應頭(返回body就是html了)