一、URL與資源
URI是一類更通用的概念,用來表示某一個互聯網資源的“位置”。
URI有兩個重要的子集:URL和URN。
URL語法
URL由三個部分組成:
第一部分:URL方案,告知客戶端通過怎樣的方式訪問資源(如http://,說明要使用HTTP協議)
第二部分:指的是服務器所在的位置(如ip地址或域名,一般也包含了端口號)
第三部分:資源的路徑。路徑說明了請求的是服務器上的那個資源。
大多數URL語法都建立在以下由9個部分構成的通用格式上:
<scheme>://<user>:<password>@<host>:<port>/<path>;<params>?<query>#<frag>
組件 |
描述 |
默認值 |
<scheme>方案 |
訪問服務器獲取資源時是用那種協議(不分大小寫) |
無 |
<user>用戶 |
某些方案訪問資源時需要的用戶名(如ftp協議) |
匿名 |
<password>密碼 |
用戶名后面可能要包含的密碼 |
E-mail地址 |
<host>主機地址 |
資源所在服務器的域名或ip地址 |
無 |
<port>端口號 |
資源所在服務器正在監聽的端口號 |
每個方案都有不同的默認端口號,http方案的默認端口號是80 |
<path>資源路徑 |
服務器上資源的本地名。路徑組件的語法與服務器和方案有關 |
無 |
<params>參數 |
某些方案會用這個組件指定輸入參數,多個參數之間也是用“;”分隔。 |
無 |
<query>查詢 |
用於給動態網頁傳遞參數,可以傳遞多個參數,用“&”隔開,每個參數的名與值用“=”隔開 |
無 |
<frag>片段 |
“#”后面的片段是用來“指導瀏覽器動作的”,對服務端無用,farag也不會被傳送到服務器。 |
無 |
下面提一下某些組件的注意事項:
參數:
參數由“;”將其與URL其他部分分隔開來(此處的其他部分指的是“參數組件”之前的URL部分,因為其后的部分的分隔符由后面的組件決定)
比如像FTP協議有兩種傳輸模式(二進制和文本形式)。此時就可以通過參數來指定傳輸形式。
如:ftp://xxx.xx.xx.xx/pub;type=d
其中有一個參數type=d,參數名是type,對應的值時d。
而路徑組件可以分為多個路徑段,每一個路徑段都能有自己的參數組件。如:
http://xxx.xx.xx.xx/hammers;sale=false/index.html;graphics=true
片段:
為了應用部分資源內容,可以使用“片段組件”表示資源內部的一個片段。
http://xxx.xx.xx.xx/tools.html#drills如:
瀏覽器並不會發送#drills,當從服務器拿到html文檔並解析后,瀏覽器會根據片段內容(也就是drills)將頁面向下滾動到名為drills的片段處。
URL快捷方式
相對URL:
URL有兩種方式:相對的和絕對的。
絕對URL包含有訪問資源所需的全部信息。
而相對URL則是不完整的,他所缺失的信息要從另一個“基礎URL”中推導出來。
基礎URL來自以下幾個地方:
1、在資源中顯示提供
有些資源會顯式的指定“基礎URL”,如,HTML文檔可以包含一個定義了基礎URL的HTML標記<BASE>
2、封裝資源的基礎URL
如果一個資源中沒有定義顯式的基礎URL,則相對URL會將“所在資源的URL作為基礎URL”
將相對URL轉換成絕對URL:
保留相對引用中定義的“組件”,然后繼承“基礎URL”中的剩下的“組件。
字符
有些協議(如SMTP協議)在傳輸的時候,會“剝去”URL中的一些特定的字符,
所以,在設計URL的時候,為了安全起見,我們需要一個“轉義機制”,將特殊字符們轉義成安全字符。
這種轉義方法為:將一個“特殊字符”用一個百分號(%),后面跟着兩個表示該字符ASCII碼的十六進制數來替換它。
如:字符“~”,ASCII碼為126,其十六進制數為0x7E。 所以應該轉義成:%7E
http://xxx.xx.xx.xx/~abc即:將URL:
轉義為:http://xxx.xx.xx.xx/%7Eabc
所有的字符在URL中使用時都應該被轉義。(但這並不是強制的,對某些傳輸協議來說,不轉義也不會產生什么問題)。
但某些字符“在作為某些用途時”不用轉義,即被保留下來(不過在將其用於保留用途之外的場合時,還是要進行轉義)。這些有特殊用途的字符為:
字符 |
保留用途(該字符用於此用途時,無需轉義) |
% |
作為編碼字符的轉義標志 |
/ |
作為<path>組件的定界符 |
. |
在路徑組件中使用 |
.. |
在路徑組件中使用 |
# |
作為<frag>組件定界符 |
? |
作為<query>組件定界符 |
; |
作為<params>組件定界符 |
: |
作為方案、用戶、密碼、主機、端口組件的定界符 |
$+ |
任何時候都可保留 |
@&= |
在某些方案的上下文中有特殊含義 |
方案
常見的方案格式:
http
超文本傳輸協議,沒有用戶組件和密碼組件,默認端口為80.
基本格式:
http://<host>:<port>/<path>?<query>#<frag>
https
與http方案唯一的區別在於,https方案使用了SSL,SSL為http連接提供了端到端的加密機制。https語法與http相同。默認端口為443.
基本格式:
https://<host>:<port>/<path>?<query>#<frag>
mailto
該URL指向的是E-mail地址。mailto URL的格式與標准URL格式不同。具體語法記錄在RFC822中。
基本格式:
mailto:<RFC-822-addr-spec>
ftp
文本傳輸協議。用來從FTP服務器上下載或向其上傳文件。
基本格式:
ftp://<user>:<password>@<host>:<port>/<path>;<params>
rtsp,rtspu
音/視頻媒體資源標識符。
rtspu中的u表示它使用的是UDP協議獲取資源。
基本格式:
rtsp://<user>:<password>@<host>:<port>/<path>
rtspu://<user>:<password>@<host>:<port>/<path>
file
方案file表示一台指定主機上課直接訪問的文件。如果省略了主機名,就默認為正在使用URL的本地主機。
基本格式:
file://<host>/<path>
news
用來訪問一些特定的文章或新聞組。它有一個獨特的性質:news URL自身包含的信息不足以對資源進行定位。
new URL中沒有提供主機名或機器名。而是需要從用戶那里獲取此類信息。如 可以在瀏覽器的選項里指定服務器。
新聞資源可以從多台服務器中獲取。被稱為位置無關的。
基本格式:
news:<newsgroup>
telnet
用於訪問交互式業務。他表示的不是資源本身。而是可通過telnet協議訪問的交互式應用程序。
基本格式:
telnet://<user>:<password>@<host>:<port>/
二、HTTP報文
如果說HTTP是信使,那么HTTP報文就是用來搬東西的包裹了。
HTTP報文由一行行的簡單字符串組成。
HTTP報文都是純文本的,不是二進制代碼,讀寫方便。
HTTP報文只有兩種:客戶端發送的“請求報文”。服務端發送的“響應報文”。
HTTP報文由三個部分組成:起始行、首部字段、主體。
報文流
HTTP使用術語“流入”、“流出”、“上游”、“下游”來描述報文的方向。
如:報文流入服務器后,又會流出回用戶的瀏覽器。
所有的HTTP報文都會向“下游”游動,所有的發送者都在接收者的“上游”。
報文的組成
之前提到了,每條報文由三個部分組成:
起始行和首部是由“行”分隔的ASCII文本。
每行以一個“由兩個字符組成的行終止序列”作為結束,即:一個回車符(ASCII碼13)和一個換行符(ASCII碼10)。
(雖然規范中要求用這兩種字符表示行終止,但穩健的應用也應該接受單個“換行符”作為行終止。因為有些老HTTP程序可能只會發送個換行符)。
報文的語法
所有的報文都能分為兩類:請求報文和響應報文。
請求報文的格式:
<method> <request-URL> <version>
<headers>
<entity-body>
響應報文的格式:(只有起始行的語法有所不同)
<version> <status> <reason-phrase>
<headers>
<entity-body>
對各部分的簡要描述(稍后會詳細介紹):
方法(method)
客戶端希望服務端對資源執行的動作。如:GET、POST等
請求URL(request-URL):
完整的URL。
版本(version):
報文所使用的HTTP版本,格式為:HTTP/<major>.<minor>
其中主要版本號(major)和次要版本號(minor)都是整數。
狀態碼(status):
這三位數字用於描述請求過程中所發生的情況(如成功、出錯等)。
原因短語(reason-phrase)
狀態碼(status)的可讀版本,原因短語只對人類有意義,機器不會處理它。
首部(headers):
可以有零個或多個首部。每個首部占一行。
每個首部的格式為:先是一個首部名,后面跟一個冒號(:),然后是一個可選的空格,接着是一個值。
在整個首部組結束時,跟一個空行(CR+LF,即回車符+換行符)結束該首部。
主體(entity-body):
該部分包含一個由“任意數據組成的數據塊”。
並不是所有的報文都有實體部分。
注:雖然規定里首部必須以空行結束。但很多客戶端和服務端在沒有實體時,(錯誤的)省略了首部最后的空行。
起始行
所有的HTTP報文都是以一個起始行作為開始。
請求報文的起始行說明了要做什么。或稱為請求行,包含了一個方法、一個請求URL、HTTP的版本。這些字段都是由空格符分隔。
響應報文的起始行說明了發生了什么。或稱為響應行。包含了HTTP的版本、數字狀態碼、原因短語。這些字段都是由空格符分隔。
方法
常用的HTTP方法:
方法 |
描述 |
是否包含主體(entity-body) |
GET |
通常用於請求服務器發送某個資源。 |
否 |
HEAD |
只從服務器獲取文檔的首部。不會返回主體部分。可以用來:1、在不獲取資源的情況下了解資源情況。2、通過查看響應中的狀態碼,查看對象是否存在。3、通過查看首部,測試資源是否被修改。 |
否 |
POST |
向服務器發送需要處理的數據。通常用它來支持HTML的表單。 |
是 |
PUT |
讓服務器通過請求的主體部分創建一個由所請求的URL命名的新文檔,或者如果該URL所指資源已經存在,則用這個主體來替換它。由於PUT允許修改內容,所以一般都需要用賬戶密碼登錄。 |
是 |
TRACE |
客戶端發起一個請求可能要穿過防火牆、代理等多個應用。每個中間節點都可能會修改原始HTTP請求。TRACE方法允許客戶端看到該請求最終變成了什么樣子。即:行程最后一站的服務器會返回一條TRACE響應。 |
否 |
OPTIONS |
OPTION方法可以請求服務器告知其支持哪些方法。 |
否 |
DELETE |
請求服務器刪除URL所指定的資源。但此方法並不一定會被執行。因為HTTP協議允許服務端在不通知客戶端的情況下撤銷請求。 |
否 |
由上表可知,有些方法的請求報文中並不存在主體。
服務器還可以實現一些自己定義的方法。這些方法被稱為擴展方法。
常見的擴展方法有:
方法 |
描述 |
LOCK |
允許用戶“鎖定”資源。如,可以在編輯某個資源時將其鎖定,防止別人同時對其進行修改。 |
MKCOL |
允許用戶創建資源 |
COPY |
在服務器上復制資源 |
MOVE |
在服務器上移動資源 |
並不是每個服務器都實現了所有的方法。如HTTP1.1只要求服務器必須實現GET和HEAD方法。
狀態碼
狀態碼存在於響應報文的起始行中,作用是告訴客戶端,發生了什么。
狀態碼分類:
整體范圍 |
目前已定義的范圍 |
分類 |
100~199 |
100~101 |
信息提示 |
200~299 |
200~206 |
成功 |
300~399 |
300~305 |
重定向 |
400~499 |
400~415 |
客戶端錯誤 |
500~599 |
500~505 |
服務端錯誤 |
100~199:信息性狀態碼
狀態碼 |
原因短語 |
含義 |
100 |
Continue |
說明收到了請求的初始部分,請客戶端繼續。 |
101 |
Switching Protocols |
說明服務器正在根據客戶端的指定,將協議updata首部所列的協議。 |
200~299:成功狀態碼
狀態碼 |
原因短語 |
含義 |
200 |
OK |
請求沒問題,響應報文的主體部分包含了所請求的資源。 |
201 |
Created |
用於在服務器上創建對象的請求(如PUT)。由於需要創建的對象已經創建好了。所以返回響應報文的時候應該在實體中包含已創建的資源的URL。 |
202 |
Accepted |
請求已被接受,但服務器還未執行相關動作。不保證服務器會完成這個請求。 |
203 |
Non-Authoritative Information |
首部中包含的信息不是來自原端服務器,而是來自資源的一份副本。 |
204 |
No Content |
響應報文中不包含主體部分。用於瀏覽器不轉為顯示新文檔的情況下,對其進行更新。 |
205 |
ResetContent |
負責告訴瀏覽器,清除當前頁面中所有HTML表單元素。 |
206 |
Partial Content |
成功執行了一個“范圍請求”。如客戶端通過特殊的首部,來獲取某個范圍內的文檔。 |
300~399:重定向狀態碼
一般情況下重定向的流程為:
客戶端發送請求報文。
服務端返回一個含有“3xx”重定向碼的響應報文。
然后客戶端根據響應報文的內容,再發送一個請求報文到重定向的服務器。
然后該服務器再返回給客戶端響應報文。
狀態碼 |
原因短語 |
含義 |
300 |
Multiple |
客戶端請求一個“實際指向多個資源”的URL時會返回該狀態碼。返回該狀態碼時會附帶一個選項列表,用戶可以選擇發希望使用那一項。然后再發送請求進行訪問。 |
301 |
Moved |
客戶端請求的URL已被移除,所以該響應報文的Location首部中會含有該資源現在所處的URL。 |
302 |
Found |
臨時重定向。但客戶端只是臨時使用Location中的重定向URL,將來的請求還是用的是老的URL |
303 |
See Other |
HTTP/1.1中使用303來實現與301同樣的行為。 |
304 |
Not Modified |
客戶端可以通過首部,將請求變成帶有條件的,當客戶端發送這種有條件的GET請求獲取資源時,而服務器中該資源近期又未被修改,則可以用該狀態碼返回,表示該資源沒被修改,客戶端應該展示該資源的本地緩存。也就是說帶有該狀態碼的響應不應該包含主體部分。 |
305 |
Use Proxy |
用來說明必須通過代理來訪問該資源,代理中資源的URL由Location首部給出 |
307 |
Temporary Redirect |
HTTP/1.1中用307代替了302 |
400~499:客戶端錯誤狀態碼
狀態碼 |
原因短語 |
含義 |
400 |
Bad Request |
告知客戶端它發送了一個錯誤的請求 |
401 |
Unauthorized |
與適當的首部一同返回,要求客戶端在獲取資源之前先認證。 |
402 |
Payment Required |
目前未使用,以后可能會使用 |
403 |
Forbidden |
請求被服務器拒絕了,可以在主體部分對拒絕原因進行描述。 |
404 |
Not Found |
服務器無法找到被請求的URL。 |
405 |
Method Not Allowed |
請求報文的首部中的方法服務器不允許。返回次響應報文時,首部里應該包含Allow首部,一遍告知客戶端可以使用那些方法。 |
406 |
Not Acceptable |
客戶端可以在URL中指定它願意接受“什么類型”的實體。而當服務器沒有與之相匹配的實體時,可使用此代碼 |
407 |
Proxy Authentiction Required |
與401類似,但用於需要認證的“代理服務器” |
408 |
Request Timeout |
為了完成客戶端的請求,花費了大量的時間,超時了。 |
409 |
Conflict |
請求可能會在資源上引發沖突,響應中應該包含描述沖突的主體。 |
410 |
Gone |
與404類似,現在找不到請求的資源,但以前曾經擁有過。 |
411 |
Length Required |
服務器要求在請求報文中包含Content-Length首部。 |
412 |
Precondition Failed |
客戶端發起條件請求,且其中一個條件失敗了的時候使用。 |
413 |
Request Entity Too Large |
客戶端發送的請求報文中的實體太大了。 |
414 |
Request URI Too Long |
客戶端發送的URL請求過長 |
415 |
Unsupported Media Type |
服務端無法理解客戶端發送的請求報文中實體的內容類型。 |
416 |
Request Range Not Satisfiable |
請求報文請求的是指定資源的某個范圍,而此范圍無效時,返回此狀態碼。 |
417 |
Expectation Failed |
請求報文的Expect首部中有一個期望,但服務器沒法滿足該期望時,使用此碼。 |
500~599:服務端錯誤狀態碼
狀態碼 |
原因短語 |
含義 |
500 |
Internal Server Error |
服務器遇到一個妨礙它為請求提供服務的錯誤。 |
501 |
Not Implemented |
客戶端發送的請求超出服務器的能力范圍。 |
502 |
Bad GateWay |
作為代理或網關來使用的服務器無法連接到其父網關。 |
503 |
Service Unavailable |
用來說明服務器現在無法為請求提供服務,但將來可以。如果服務器知道何時資源可用,則可以在響應報文的首部中包含一個Rety-After首部。 |
504 |
Gateway Timeout |
此響應來自代理或網關,表示他們在等待其他服務器響應時超時了。 |
505 |
HTTP Version Not Supported |
服務端收到的請求使用了它不支持的協議版本。 |
以上這些狀態碼其實不用刻意的去背,只要多留意一下“原因短語”,還是很容易辨識的。
原因短語
原因短語與狀態碼是成對出現的,
HTTP並沒有硬性規定什么狀態碼必須對應什么原因短語,但提倡使用上面提到的原因短語。
版本號
版本號會以HTTP/<major>.<minor>的形式出現。目的是以便互相了解對方的能合報文格式。
版本號不會被當作小數來處理。版本中<major>和<minor>會被當做單獨的數字來比較與處理。
首部
首部和方法配合着工作。共同決定了客戶端和服務器能做什么。
一個報文中可以有零個或多個首部。且“同一種首部可以有多個(如一個響應報文首部里可以有多個Set-Cookie首部)”
首部主要分為以下五類:
1、通用首部
這些首部客戶端和服務端都可以使用。提供一些最基本的信息。
首部 |
描述 |
Connection |
允許客戶端和服務器指定與連接有關的選項。 |
Date |
提供日期和時間標志,說明報文是什么時間創建的。 |
MIME-Version |
給出發送端使用的MIME版本 |
Trailer |
如果報文采用了分塊傳輸編碼方式,可用這個首部列出位於報文拖掛部分的首部集合。 |
Transfer-Encoding |
告知接收端,報文采用了什么編碼方式。 |
Update |
給出發送端想要“升級”使用的新協議(如http的升級協議websocket) |
Via |
顯示了報文經過的中間節點(代理、網關) |
Cache-Control |
隨報文傳送緩存指示 |
Pragma |
另一種隨報文傳送指示的方式,但並不專用於傳送緩存。 |
2、請求首部
是請求報文特有的首部,服務器可以根據請求首部中的信息,為客戶端提供更好的響應。
請求首部又可以細分為以下幾種請求首部:
1、請求的信息性首部
首部 |
描述 |
Client-IP |
提供運行客戶端的機器的IP地址 |
From |
客戶端用戶的E-mail地址 |
Host |
接收該請求的服務器的主機名和端口號 |
Referer |
請求的URL |
UA-Color |
客戶端顯示器的顯示顏色有關的信息 |
UA-CPU |
客戶端CPU的類型或制造商 |
UA-Disp |
客戶端顯示器能力有關的消息 |
UA-OS |
客戶端的操作系統的名稱及版本 |
UA-Pixels |
客戶端顯示器的像素信息 |
User-Agent |
將發起請求的應用程序名稱告知服務器(如具體的瀏覽器,可以根據這個對不同的瀏覽器進行適配。) |
2、Accept首部
該首部為客戶端提供了一種將其喜好和能力告知服務器的方式,此首部對兩方都受益,
客戶端會得到它想要的,而服務器也不會浪費寬帶發送客戶端無法使用的東西。
首部 |
描述 |
Accept |
告訴服務器能夠發送那些媒體類型 |
Accept-Charset |
告訴服務器能夠發送那些字符集 |
Accept-Encoding |
告訴服務器能夠發送那些編碼方式 |
Accept-Language |
告訴服務器能夠發送那些語言 |
TE |
告訴服務器可以使用那些擴展傳輸編碼 |
3、條件請求首部
客戶端為請求加上某些限制,要求服務端在對請求進行響應之前,確保條件為真。
首部 |
描述 |
Expect |
允許客戶端列出某些所要求的服務器行為 |
If-Match |
如果實體標記與文檔當前的實體標記相匹配, 就獲取該文檔。 |
If-Modified-Since |
除非某個指定日期之后該資源被修改過,否則限制該請求。 |
If-None-Match |
如果提供的實體標記與當前文檔的實體標記不相符,就獲取文檔。 |
If-Range |
允許對文檔的某個范圍進行條件請求 |
If-Unmodified-Since |
除非某個指定日期之后資源沒被修改過,否則限制該請求 |
Range |
如果服務器支持范圍請求,就請求資源的指定范圍。 |
4、安全請求首部
首部 |
描述 |
Authorization |
包含了客戶端提供給服務器,以便對其自身進行認證的數據 |
Cookie |
客戶端想服務端發送的一個令牌(后面會詳細的講cookie) |
Cookie2 |
用來說明請求端支持的cookie版本 |
5、代理請求首部
首部 |
描述 |
Max-Forward |
在通往源端服務器上的路徑上,將請求轉發給其他代理或網關的最大次數。 |
Proxy-Authorization |
與Authorization首部相同,但該首部是在代理進行認證時使用。 |
Proxy-Connection |
與Connection首部相同,但該首部是在與代理進行連接時使用。 |
3、響應首部
1、響應的信息性首部
首部 |
描述 |
Age |
從最初創建開始,響應的持續時間 |
Public |
服務器為其資源支持的請求方法列表 |
Retry-After |
如果資源不可用,則可在此時間重試 |
Server |
服務器應用程序的名稱和版本 |
Title |
對HTML文檔來說,就是標題 |
Warning |
比原因短語更詳細的警告報文 |
2、協商首部
首部 |
描述 |
Accept-Ranges |
對此資源來說,服務器可接受的范圍類型 |
Vary |
這是一個首部列表,服務器會根據該列表挑選最合適資源發送 |
3、安全響應首部
首部 |
描述 |
Proxy-Authenticate |
來自代理的堆客戶端的質詢列表 |
Set-Cookie |
可在客戶端設置一個令牌,一遍進行標識 |
Set-Cookie2 |
與Set-Cookie類似(之后有詳解) |
WWW-Authenticate |
來自服務器的堆客戶端的質詢列表 |
4、實體首部
實體首部提供了實體及其內容的信息,可以告知報文接收者它在對什么進行處理。
1、實體的信息性首部
首部 |
描述 |
Allow |
列出了可以對此實體執行的請求方法 |
Location |
告訴客戶端該實體“真正的URL”。 |
2、內容首部
首部 |
描述 |
Content-Base |
基礎URL |
Content-Encoding |
對主體執行的編碼方式 |
Content-Language |
理解主體時,最合適的自然語言 |
Content-Length |
主體的長度或尺寸 |
Content-Location |
資源實際所處的位置 |
Content-MD5 |
主體的MD5校驗和 |
Content-Range |
在整個資源中此實體表示的字節范圍 |
Content-Type |
主體的對象類型 |
3、實體緩存首部
提供與被緩存實體有關的信息(之后還會詳講)
首部 |
描述 |
ETag |
與此實體相關的實體標記 |
Expires |
實體不再有效。需要從原始端源再次獲取該實體日期與時間 |
Last-Modified |
該實體最后一次唄修改的日期和時間。 |
5、擴展首部
擴展首部是非標准的首部,可以由開發者自己創建。即使不知道含義,HTTP程序也應該對其進行轉發。
實體
該部分為可選的。
實體的主體是HTTP要傳輸的內容。
該內容可以是很多類型的數字數據,如:圖片、視頻、HTML文檔、軟件應用程序、電子郵件等。
三、cookie機制
http是一種無狀態協議。所以一般情況下服務端很難根據http協議來判斷訪問者是哪一位。
而cookie可以很好的解決這個問題。cookie是當前“識別用戶,實現持久會話的最好方式”。
cookie的類型
可以籠統的分為兩類:會話cookie和持久cookie。
會話cookie:是一種臨時cookie,當用戶退出瀏覽器時,會話cookie就刪除了
持久cookie:存儲在硬盤上,瀏覽器退出,計算機重啟后它任然存在。
它們之間的唯一區別就是過期時間。
如果設置了Discard參數,或沒有設置Expires或Max-Age參數來擴展過期時間,該cookie就是一個會話cookie
cookie的工作原理
某個客戶端首次訪問服務器時,服務器通過響應首部Set-Cookie或Set-Cookie2,給該客戶端“貼上”一個“該客戶端獨有的cookie”。
這樣服務端以后就能根據該cookie來識別客戶端了。
瀏覽器會記住從服務器返回的響應首部中的cookie內容,並將cookie內容存入瀏覽器的“cookie數據庫中”。將來如果用戶使用該瀏覽器訪問同一服務器時,瀏覽器就會挑出“該服務器貼到用戶上的那些cookie”,然后通過請求首部Cookie將cookie內容發送給服務器。
客戶端側狀態
不同的瀏覽器會以不同的方式存儲cookie。
1、IE瀏覽器
IE將每個cookie都存儲在緩存目錄里的獨立文件中(即每個瀏覽器“貼的”cookie都對應一個cookie)。
2、谷歌瀏覽器
將所有的cookie都保存在同一的一個文件里。該文件的格式為SQLite3數據庫格式的文件。文件名為Cookies
3、火狐瀏覽器
將所有的cookie都保存在同一的一個文件里。該文件的格式為SQLite3數據庫格式的文件。文件名為cookies.sqlite
cookie成分
cookie規范定義在報文的首部里,同一種首部可以重復
如服務器可以在響應報文中設置多個Set-Cookie首部,一次性給瀏覽器“貼”多個cookie
目前的cookie規范有兩個版本。
cookies版本0
http://home.netscape.com/newsref/std/cookie_spec.html描述文檔地址:
版本0的響應首部Set-Cookie
格式:
Set-Cookie:name=value [; expires=date] [; path=path] [; domain=domain] [; secure]
其中:Set-Cookie首部有一個強制的cookie值和名。后面的屬性都是可選的,由分號隔開。
Set-Cookie屬性 |
描述及實例 |
name=value |
強制的。name和value都是字符序列。除非包含在雙引號里,否則不包含分號、逗號、等號、空格。在瀏覽器后續對服務器的訪問中會將其送回Web服務器 |
expires=date |
可選的。該屬性會指定一個日期字符串,用來定義cookie的實際生存期。一旦超過日期,瀏覽器就不再存儲cookie了。如果沒有指定Expires,cookie會在瀏覽器關閉時過期。date格式為:Weekday(如Wednesday), DD-Mon-YY HH:MM:SS GMT |
path=path |
可選的。該屬性可以為服務器上的特定文檔分配cookie。(路徑“/”與域名中所有內容相匹配)。默認為“產生Set-Cookie響應的URL路徑” |
domain=domain |
可選的。瀏覽器只向指定域中的服務器主機名發送cookie。這樣服務器就將cookie限制在了特定域中。如:acmc.com域與an.acmc.com相匹配。.com與acmc.com相匹配。如果沒有指定域,就默認域為產生Set-Cookie響應的服務器的主機名。 |
secure |
可選的,如果包含此屬性,就只有在使用在HTTP使用SSL安全連接時才會發送cookie |
版本0的請求首部Cookie
客戶端發送請求時,會將所有與域、路徑、過濾器相匹配且未過期的cookie鍵值對都發送給該站點。
格式為:
Cookie: name1=value1 [; name2=value2]·····
cookies版本1
http://www.ietf.org/rfc/rfc2965.txt描述文檔地址:
cookies版本1是cookie的一個擴展版本。雖然該版本引入了新首部,但也能與版本0交互。
主要改動如下:
1、為每個cookie關聯上解釋性文本,對其目的進行解釋。
2、允許在瀏覽器退出時,不考慮過期時間,將cookie銷毀。
3、Max-Age使用相對秒數。
4、在域和路徑的基礎上還增加了端口號
5、通過cookie首部回送“域、端口、路徑過濾器”(如果有的話)
6、為實現互操作性使用的版本號。
7、在Cookie首部從名字中區分出附加關鍵字的$前綴。
版本1的Cookie2首部:
版本1增加了一個cookie2首部,cookie可以告訴服務器:客戶端所支持的新的cookie標准版本。
如:Cookie2: $Version="1"
四、HTTPS
https方案
https是最常見的http安全版本。
HTTPS就是在安全的傳輸層上發送HTTP。在將http發送給TCP之前,現將其發送給一個安全層對其進行加密,然后再發送給TCP傳輸層。
其中安全層使用的協議是SSL或TLS協議。
如果URL的方案為https,客戶端就會打開一條到服務器端口443(默認情況下)的連接,然后與服務器“握手”,以二進制格式與服務器交換一些SSL安全參數,附上加密的HTTP命令。
在未加密HTTP中:
客戶端會打開一條到web服務器端口80的TCP連接,發送一條請求報文,接收一條響應報文,關閉連接。
在HTTPS中:
1、客戶端首先打開一條到Web服務器端口443的連接。
2、然后建立端口443的TCP連接。之后客戶端與服務端進行SSL握手,即對加密參數進行溝通,並交換秘鑰。
3、然后SSL初始化就完成了,客戶端就可以將請求報文發送給“安全層讓其進行加密”。
4、之后在將已加密的請求發送給TCP。
5、然后服務端再將響應發送給SSL層進行加密,加密后再發給TCP,然后發給客戶端。
6、SSL關閉,TCP連接關閉。
SSL握手
在上面的第二步中,客戶端和服務端進行了一次SSL握手。該握手主要完成以下工作:
交換協議版本號。
選擇一個兩端都了解的密碼。
對兩端的身份進行驗證。
生成臨時的會話秘鑰,以便加密信道。
OpenSSL
SSL是個復雜的二進制協議。我們可以借助一些商業或開源庫,來編寫SSL客戶端和服務端。
OpenSSL是一個實現了SSL和TLS協議以及以及一個全功能的通用加密庫。
(之前我的“深入理解Connector”博客中就提到了tomcat native技術中就集成了OpenSSL)
可以從http://www.openssl.org上獲取OpenSSL的相關信息,並下載。
五、實體和編碼
各種媒體對象都可以通過http傳送,如圖像、文本、影音、軟件等。HTTP還會確保它的報文被正確的傳送、識別、以及適當的處理。
如果說HTTP報文是箱子,那么實體就是貨物。
實體包含兩個部分:實體首部和實體主體。
實體首部
在之前講解首部的時候列出過HTTP實體首部。在此就不一一列出了。
下面詳細的講一下其中幾個實體首部:
Content-Length首部詳解
該首部指出實體主體的字節大小。
除非使用了“分塊編碼”,否則必須使用該首部。
因為該首部會被用來“檢測報文結尾”,即用來識別是否傳送了完整的報文實體主體。
而且Content-Length在持久連接中也是必不可少的。因為如果是持久連接傳送,客戶端可通過該首部知道該報文何時結束,也就能知道嚇一跳報文何時開始。
Content-Length指的長度一定是“實體主體內容編碼后的字節長度”。(而不是原始的字節長度)
Content-MD5首部詳解
該首部發送的是“編碼前”的實體主體運行md5算法的結果。
作用是驗證端到端實體的完整性,即兩邊都做一下md5算法,比對一下結果。
另一個作用是充當散列表的關鍵字,用來快速定位文檔。
Content-Type首部詳解
該首部字段說明了實體主體的基本媒體類型,常用的媒體類型如下表:
媒體類型 |
描述 |
text/html |
實體主體是HTML文檔 |
text/plain |
實體主體是純文本文檔 |
image/gif |
主體是GIF圖像 |
image/jpeg |
主體是JPEG格式圖像 |
audio/x-wav |
主體包含WAV格式聲音數據 |
model/vrml |
主體是三維的VRML模型 |
application/vnd.ms-powerpoint |
主體ppt文檔 |
multipart/byteranges |
主體有若干部分,每部分都包含了完整文檔中不同的字節范圍。 |
message/http |
實體主體包含完整的HTTP報文。 |
注:該首部說明的是原始實體主體的類型,即主體編碼之前的。
Content-Encoding首部詳解
http應用有時在發送報文前,要對報文中的實體主體進行編碼。目的可能是壓縮或加密等。
Content-Encoding首部是用來說明編碼時使用的算法
內容編碼類型:
Content-Encoding值 |
描述 |
gzip |
實體采用GNUzip編碼(效率最高,使用最廣) |
compress |
實體采用Unix的文件壓縮程序 |
deflate |
實體采用zlib格式壓縮 |
identity |
沒有對實體進行編碼(默認情況) |
Accept-Encoding首部詳解
毫無疑問,我們不希望服務器用一種客戶端無法解析的方法來進行編碼。
所以客戶端可以通過該首部將自己支持的實體主體編碼格式發給服務端。
客戶端可以為每種編碼附帶Q值說明編碼優先級。范圍是0.0~1.0,1.0表示最希望使用的編碼。
“*”表示任何其他方法。
傳輸編碼
之前討論的都是內容編碼,主要是對實體主體進行編碼。
而傳輸編碼主要是為了改變數據在網絡中的傳輸方式,下面主要說一下傳輸編碼中的分塊編碼。
分塊編碼把“報文”分割陳若干個大小的塊。塊之間是緊挨着發送的,這樣就不需要使用Content-Length首部了(因為每塊的大小都是固定的,只需要知道塊數即可)。
注意:分塊編碼是對整個傳輸進行編碼,而不僅僅是對實體主體進行編碼。
分塊編碼有起始的HTTP響應首部開始組成一塊,隨后就是一系列分塊。
每個分塊包含一個“長度值”和一個“該分塊的數據”。長度值是16進制,數據大小是以字節計算。
長度值與分塊之間由“<CR><LF>”分割(之前提到過,這個分割符就是一個回車符(ASCII碼13)和一個換行符(ASCII碼10))。
最后一個塊比較特別,其長度為0,表示主體結束。
傳輸編碼的規則:
1、傳輸編碼集合中必須包含“分塊”。唯一例外是使用關閉連接來結束報文。
2、當使用分塊傳輸編碼時,它必須是最后一個作用在主體之上的(如報文既有內容主體編碼,又有分塊編碼。那么就必須先對實體主體進行內容編碼,然后再對整個報文進行分塊編碼)。
3、分塊編碼不能多次作用在一個報文主體上。
所有的HTTP/1.1程序都支持分塊編碼。
范圍請求
http允許客戶端只請求文檔的某一范圍。
使用的自然就是我們之前提到的Range響應首部。
如:Range: bytes=4000-
這就表示,客戶端只想請求文檔開頭4000個字節之后的部分。
另外還能用該首部來加速下載。
如客戶端從多台服務器上下載同一個文檔,那么就能“在一條請求報文中”同時從不同服務器上下載該文檔的不同范圍。返回的響應也是單個實體。