天天看點

HTTP 1.1學習筆記

前言

由于HTTP 1自身的局限性,它不能很好的為使用者提供性能良好的WEB服務。于1999年6月正式釋出了HTTP1.1标準REC2616,它厘清了之前版本中很多有歧義的地方,而且還新增了很多重要的優化,如持久連接配接、分塊編碼傳輸、狀态碼擴充、增強的緩存機制、傳輸編碼及請求管道等。本文是個人在學習《WEB性能權威指南》後,又查閱了一些文檔資料寫的一篇随筆,僅供參考和個人以後查閱。下面将對比http1.0講述一些在新版本中的重要改進。(本文最初釋出于公司内網,外網原文位址:騰雲閣--HTTP 1.1學習筆記)

1.持久連接配接

每個TCP連接配接在建立初期都需要進行三次握手,需要經曆一次用戶端與伺服器間的完整往返,如果進行資料傳輸的話,至少還需要引發另外一次往返。再加上伺服器端的處理請求的時間,就是可以得到每次請求的總時間。如果每次發送請求都是由建立的TCP連接配接發送的話,它至少需要兩次完整的網絡往返時間,因為每個TCP連接配接的建立都需要重新進行三次握手。

那麼,能不能對TCP連接配接進行重用呢?

答案無疑是肯定的,HTTP1.1裡添加了持久連接配接的支援,再次發送請求時,可以直接使用上次已經建立完成的TCP連接配接,這樣就避免了第二次TCP連接配接時的三次握手、消除另一次TCP慢啟動的往返,極大了減小了網絡延時。如果重用CTP連接配接發送HTTP請求的次數越多,帶來的性能提升越可觀,因為在第一次經過三次握手建立連接配接之後,無需再花費多餘的時間再建立連接配接。

注:目前,所有現代浏覽器都嘗試持久化HTTP連接配接,如果伺服器支援的話。使用HTTP1.1的話,預設的就是持久連接配接;如果使用HTTP 1.0,可以明确使用connection:keep-alive首部聲明使用持久連接配接;還應注意一些HTTP庫和架構的預設行為,它們有時候并不是預設使用持久連接配接的。

2.HTTP管道

持久HTTP可以讓我們重用已有的連接配接來完成多次請求,但這些請求需要滿足先進先出的隊列順序:發送請求--等待響應,再發送下一個請求。HTTP/1.1允許多個http請求通過一個套接字同時被輸出 ,而不用等待相應的響應。然後請求者就會等待各自的響應,這些響應是按照之前請求的順序依次到達。(所有請求保持一個FIFO的隊列,一個請求發送完之後,不必等待這個請求的響應被接受到,下一個請求就可以被再次發出;同時,伺服器端傳回這些請求的響應時也是按照FIFO的順序)。

在高延遲和多請求的場景下,通過HTTP管道進行資料傳輸會有更大的性能提升,會節省更多的時間。經過仔細的觀察,可以發現在HTTP1.1中存在一些局限,它嚴格串行的傳回響應,它不允許多個資料交錯到達(多路複用),隻能等待一個響應完全傳回後,下一個響應才能發送,無論下一個響應是否早于前一個響應完成處理,這也叫做隊首阻塞。這就帶來一個非常糟糕的體驗,如果第一個請求需要處理的時間非常長,那麼後續的請求即使被伺服器已經處理完成,響應也不能立即傳回,而是存儲在服務端的緩存區中,等待第一個響應的完成,才能按照FIFO順序傳回。

由于TCP嚴格按照按順序傳遞,丢失一個TCP分組就會阻塞所有高序号的分組,除非重傳丢失的分組,這也會帶來額外的延遲。由于HTTP1.1中不允許多路複用,HTTP管道也會帶來一些不容忽視的問題:

  • 一個慢響應會阻塞所有後續請求;
  • 并行處理請求時,伺服器需要緩存處理結果,會占用伺服器資源,如果某個響應很大,很容易形成伺服器的受攻擊面;
  • 響應失敗可能終止TCP連接配接,會強迫用戶端重新發送對後續資源的請求,導緻重複處理;
  • 網絡環境中存在中間代理,檢測管道相容性十分必要;
  • 如果中間代理不支援管道,那它可能中斷連接配接,也可能把所有請求串聯起來。

正是由于存在這樣或那樣的問題,HTTP管道技術的應用比較有限,并沒有大面積推廣開來,即使一些支援它的浏覽器也僅僅把它作為一個進階選項。如果你對用戶端和服務端都有很強的控制力,依然可以使用它,會帶來不錯的性能提升。如果要在采用這種技術,你需要注意以下事項:

  • 確定HTTP用戶端支援管道;
  • 確定HTTP伺服器支援管道;
  • 應用必須進行中斷的連接配接并恢複;
  • 應用必須進行中斷的請求的幂等問題;
  • 應用必須保持自身不受出問題的代理的影響。

實踐中部署HTTP管道的最佳途徑,就是在用戶端和服務端使用HTTPS,這樣就可以免除不支援管道的中間代理的幹擾。

3.增強的緩存機制

在HTTP/1.0中,使用Expire頭域來判斷資源的fresh或stale,并使用條件請求(conditional request)來判斷資源是否仍有效。

相關字段:

  • Date: 伺服器響應的時間;
  • Expires: 資源過期時間;
  • Last-Modified: 資源最後修改時間;
  • If-Modified-Since: 用來驗證資源是否過期;

大緻政策為:

如果Expires設定的時間在Date之後,則浏覽器在Expires标記的時間之前都不會通路伺服器了,而是使用浏覽器緩存;如果Expires設定的時間在Date之前,或者浏覽器時間已經在Expires之後,那麼再次通路資源時, 浏覽器就要向伺服器發送請求,但不是重新拉取資料,而是詢問伺服器該資源是否過期,方法時,把上次response中Last-Modified的時間作為If-Modified-Since的時間,發送請求,伺服器對比該時間和資源目前的更改時間,如果未更改,則傳回304,否則傳輸新檔案。此外,HTTP/1.0中還定義了Pragma:no-cache頭域,用戶端使用該頭域說明請求資源不能從cache中擷取,而必須回源擷取。

HTTP/1.0中,If-Modified-Since頭域使用的是絕對時間戳,精确到秒,但使用絕對時間會帶來不同機器上的時鐘同步問題,文檔的 更新周期小于1s, 都會出現問題。

為了提供更好的緩存機制,HTTP1.1對以往的機制進行了一個漸進性的改進。HTTP/1.1提倡的緩存機制是,對比文檔的hash值,文檔内容變,則hash變,用相對時間代替絕對時間,這樣就可以解決使用絕對時間戳帶來一些問題。

HTTP/1.1 繼承 HTTP/1.0 是以HTTP/1.0的相關字段仍然有效,保留的這些字段就是為了相容那些僅支援HTTP/1.0的用戶端。 HTTP/1.1伺服器不應該設定與1.0沖突的過期政策, 1.1的伺服器在沒有文檔hash值時,也可以使用If-Modified-Since進行判斷文檔過期。

HTTP1.1中新增了以下字段:

  • Cache-Control: 用來控制浏覽器的緩存行為;
  • ETag: 文檔的Hash值;
  • If-None-Match: 用來驗證資源是否過期,即文檔Hash值是否變化。

而HTTP/1.1中引入了一個ETag頭域用于重激活機制,它的值ETag可以用來唯一的描述一個資源。請求消息中可以使用If-None-Match頭域來比對資源的entitytag是否有變化。

為了使caching機制更加靈活,HTTP/1.1增加了Cache-Control頭域(請求消息和響應消息都可使用),它支援一個可擴充的指令子集:例如max-age指令支援相對時間戳;private和no-store指令禁止對象被緩存;no-transform阻止Proxy進行任何改變響應的行為;no-cache浏覽器緩存,但是認為是過期緩存;no-store浏覽器不緩存;max-age:緩存有效時間段等等。

如果想要浏覽器每次發送請求,還啟用緩存,那就使用Cache-Control: no-cache, 每次通路圖檔,浏覽器都會去驗證Etag。

Cache使用關鍵字索引在磁盤中緩存的對象,在HTTP/1.0中使用資源的URL作為關鍵字。但可能存在不同的資源基于同一個URL的情況,要差別它們還需要用戶端提供更多的資訊,如Accept-Language和Accept-Charset頭域。為了支援這種内容協商機制(content negotiation mechanism),HTTP/1.1在響應消息中引入了Vary頭域,該頭域列出了請求消息中需要包含哪些頭域用于内容協商。

4.分塊編碼傳輸

HTTP消息中可以包含任意長度的實體,通常它們使用Content-Length來給出消息結束标志。但是,對于很多動态産生的響應,隻能通過緩沖完整的消息來判斷消息的大小,但這樣做會加大延遲。如果不使用長連接配接,還可以通過連接配接關閉的信号來判定一個消息的結束。

HTTP/1.1中引入了Chunke dtransfer-coding來解決上面這個問題,發送方将消息分割成若幹個任意大小的資料塊,每個資料塊在發送時都會附上塊的長度,最後用一個零長度的塊作為消息結束的标志。這種方法允許發送方隻緩沖消息的一個片段,避免緩沖整個消息帶來的過載。

在HTTP/1.0中,有一個Content-MD5的頭域,要計算這個頭域需要發送方緩沖完整個消息後才能進行。而HTTP/1.1中,采用chunked分塊傳遞的消息在最後一個塊(零長度)結束之後會再傳遞一個拖尾(trailer),它包含一個或多個頭域,這些頭域是發送方在傳遞完所有塊之後再計算出值的。發送方會在消息中包含一個Trailer頭域告訴接收方這個拖尾的存在。

5.狀态碼擴充

狀态碼是試圖了解和滿足請求的三位數字的整數位。HTTP/1.0中隻定義了16個狀态響應碼,對錯誤或警告的提示不夠具體。HTTP/1.1引入了一個Warning頭域,增加對錯誤或警告資訊的描述。

截止到HTTP1.1中包含的狀态碼大緻如下:

狀态代碼 狀态資訊 含義

消息(1**)

100 Continue 初始的請求已經接受,客戶應當繼續發送請求的其餘部分。(HTTP 1.1新)

101 Switching Protocols 伺服器将遵從客戶的請求轉換到另外一種協定(HTTP 1.1新)

成功(2**)

200 OK 一切正常,對GET和POST請求的應答文檔跟在後面。

201 Created 伺服器已經建立了文檔,Location頭給出了它的URL。

202 Accepted 已經接受請求,但處理尚未完成。

203 Non-Authoritative Information 文檔已經正常地傳回,但一些應答頭可能不正确,因為使用的是文檔的拷貝(HTTP 1.1新)。

204 No Content 沒有新文檔,浏覽器應該繼續顯示原來的文檔。如果使用者定期地重新整理頁面,而Servlet可以确定使用者文檔足夠新,這個狀态代碼是很有用的。

205 Reset Content 沒有新的内容,但浏覽器應該重置它所顯示的内容。用來強制浏覽器清除表單輸入内容(HTTP 1.1新)。

206 Partial Content 客戶發送了一個帶有Range頭的GET請求,伺服器完成了它(HTTP 1.1新)。

重定向(3**)

300 Multiple Choices 客戶請求的文檔可以在多個位置找到,這些位置已經在傳回的文檔内列出。如果伺服器要提出優先選擇,則應該在Location應答頭指明。

301 Moved Permanently 客戶請求的文檔在其他地方,新的URL在Location頭中給出,浏覽器應該自動地通路新的URL。

302 Found 類似于301,但新的URL應該被視為臨時性的替代,而不是永久性的。注意,在HTTP1.0中對應的狀态資訊是“Moved Temporatily”。

出現該狀态代碼時,浏覽器能夠自動通路新的URL,是以它是一個很有用的狀态代碼。

注意這個狀态代碼有時候可以和301替換使用。例如,如果浏覽器錯誤地請求 http://host/~user (缺少了後面的斜杠), 有的伺服器傳回301,有的則傳回302。

嚴格地說,我們隻能假定隻有當原來的請求是GET時浏覽器才會自動重定向。請參見307。

303 See Other 類似于301/302,不同之處在于,如果原來的請求是POST,Location頭指定的重定向目标文檔應該通過GET提取(HTTP 1.1新)。

304 Not Modified 用戶端有緩沖的文檔并發出了一個條件性的請求(一般是提供If-Modified-Since頭表示客戶隻想比指定日期更新的文檔)。伺服器告訴客戶,原來緩沖的文檔還可以繼續使用。

305 Use Proxy 客戶請求的文檔應該通過Location頭所指明的代理伺服器提取(HTTP 1.1新)。

307 Temporary Redirect 和302(Found)相同。許多浏覽器會錯誤地響應302應答進行重定向,即使原來的請求是POST,即使它實際上隻能在POST請求的應答是303時 才能重定向。由于這個原因,HTTP 1.1新增了307,以便更加清除地區分幾個狀态代碼:當出現303應答時,浏覽器可以跟随重定向的GET和POST請求;如果是307應答,則浏覽器隻能跟随對GET請求的重定向。(HTTP 1.1新)

請求錯誤(4**)

400 Bad Request 請求出現文法錯誤。

401 Unauthorized 客戶試圖未經授權通路受密碼保護的頁面。應答中會包含一個WWW-Authenticate頭,浏覽器據此顯示使用者名字/密碼對話框,然後在填寫合适的Authorization頭後再次送出請求。

403 Forbidden 資源不可用。伺服器了解客戶的請求,但拒絕處理它。通常由于伺服器上檔案或目錄的權限設定導緻。

404 Not Found 無法找到指定位置的資源。這也是一個常用的應答。

405 Method Not Allowed 請求方法(GET、POST、HEAD、DELETE、PUT、TRACE等)對指定的資源不适用。(HTTP 1.1新)

406 Not Acceptable 指定的資源已經找到,但它的MIME類型和客戶在Accpet頭中所指定的不相容(HTTP 1.1新)。

407 Proxy Authentication Required 類似于401,表示客戶必須先經過代理伺服器的授權。(HTTP 1.1新)

408 Request Timeout 在伺服器許可的等待時間内,客戶一直沒有發出任何請求。客戶可以在以後重複同一請求。(HTTP 1.1新)

409 Conflict 通常和PUT請求有關。由于請求和資源的目前狀态相沖突,是以請求不能成功。(HTTP 1.1新)

410 Gone 所請求的文檔已經不再可用,而且伺服器不知道應該重定向到哪一個位址。它和404的不同在于,傳回407表示文檔永久地離開了指定的位置,而404表示由于未知的原因文檔不可用。(HTTP 1.1新)

411 Length Required 伺服器不能處理請求,除非客戶發送一個Content-Length頭。(HTTP 1.1新)

412 Precondition Failed 請求頭中指定的一些前提條件失敗(HTTP 1.1新)。

413 Request Entity Too Large 目标文檔的大小超過伺服器目前願意處理的大小。如果伺服器認為自己能夠稍後再處理該請求,則應該提供一個Retry-After頭(HTTP 1.1新)。

414 Request URI Too Long URI太長(HTTP 1.1新)。

416 Requested Range Not Satisfiable 伺服器不能滿足客戶在請求中指定的Range頭。(HTTP 1.1新)

伺服器錯誤(5**)

500 Internal Server Error 伺服器遇到了意料不到的情況,不能完成客戶的請求。

501 Not Implemented 伺服器不支援實作請求所需要的功能。例如,客戶發出了一個伺服器不支援的PUT請求。

502 Bad Gateway 伺服器作為網關或者代理時,為了完成請求通路下一個伺服器,但該伺服器傳回了非法的應答。

503 Service Unavailable 伺服器由于維護或者負載過重未能應答。例如,Servlet可能在資料庫連接配接池已滿的情況下傳回503。伺服器傳回503時可以提供一個Retry-After頭。

504 Gateway Timeout 由作為代理或網關的伺服器使用,表示不能及時地從遠端伺服器獲得應答。(HTTP 1.1新)

505 HTTP Version Not Supported 伺服器不支援請求中所指明的HTTP版本。(HTTP 1.1新)

6.Host頭域

在HTTP1.0中認為每台伺服器都綁定一個唯一的IP位址,是以,請求消息中的URL并沒有傳遞主機名(hostname)。但随着虛拟主機技術的發展,在一台實體伺服器上可以存在多個虛拟主機(Multi-homed Web Servers),并且它們共享一個IP位址。由于HTTP 1.0不支援Host請求頭字段,WEB浏覽器無法使用主機頭名來明确表示要通路伺服器上的哪個WEB站點,這樣就無法使用WEB伺服器在同一個IP位址和端口号上配置多個虛拟WEB站點。在HTTP 1.1中增加Host請求頭字段後,WEB浏覽器可以使用主機頭名來明确表示要通路伺服器上的哪個WEB站點,這才實作了在一台WEB伺服器上可以在同一個IP位址和端口号上使用不同的主機名來建立多個虛拟WEB站點。

7.請求方式新增

客戶程式向伺服器發送的請求可以有不同的類型,這樣伺服器可以根據不同的請求類型進行不同的處理。在HTTP1.0中,定義了三種最基本的請求類 型,GET、POST和HEAD,當使用GET和POST方法時,伺服器最後都将結果文檔傳回給客戶程式,浏覽器将重新整理顯示;而HEAD請求則不同,HEAD請求在客戶程式和伺服器之間進行交流,而不會傳回具體的文檔,它僅僅交流一些内部資料,這些資料不會影響浏覽的過程。是以HEAD方法通常不單獨使用,而是和其他的請求方法一起起到輔助作用。一些搜尋引擎使用的自動搜尋機器人使用這個方法來獲得網頁的标志資訊,或者進行安全認證時,使用這個方法來傳遞認證資訊。

除了上述三種最常見的通路方法之外,在HTTP1.1中還定義了更多的通路方法類型,具體如下:

  • PUT:向指定資源位置上傳其最新内容;
  • DELETE:請求伺服器删除Request-URI所辨別的資源;
  • OPTIONS:傳回伺服器針對特定資源所支援的HTTP請求方法。也可以利用向Web伺服器發送'*'的請求來測試伺服器的功能性;
  • TRACE:回顯伺服器收到的請求,主要用于測試或診斷;
  • CONTENT:HTTP/1.1協定中預留給能夠将連接配接改為管道方式的代理伺服器。

這些方法并不常用,因而大部分Web伺服器軟體并沒有實作他們。然而對于特定場合他們還是非常有用的,如果伺服器不支援客戶發送的請求方法,伺服器将傳回錯誤并立即關閉連接配接。

8.帶寬優化

HTTP/1.0中,存在一些浪費帶寬的現象,例如用戶端隻是需要某個對象的一部分,而伺服器卻将整個對象送過來了,又比如下載下傳大檔案時需要支援斷點續傳功能,而不是在發生斷連後不得不重新下載下傳完整的包。HTTP/1.1中在請求消息中引入了range頭域,它允許隻請求資源的某個部分。在響應消息中Content-Range頭域聲明了傳回的這部分對象的偏移值和長度。如果伺服器相應地傳回了對象所請求範圍的内容,則響應碼為206(Partial Content),它可以防止Cache将響應誤以為是完整的一個對象。

另外一種情況是請求消息中如果包含比較大的實體内容,但不确定伺服器是否能夠接收該請求(如是否有權限),此時若貿然發出帶實體的請求,如果被拒絕也會浪費帶寬。HTTP/1.1加入了一個新的狀态碼100(Continue)。用戶端事先發送一個隻帶頭域的請求,如果伺服器因為權限拒絕了請求,就回送響應碼401(Unauthorized);如果伺服器接收此請求就回送響應碼100,用戶端就可以繼續發送帶實體的完整請求了。注意,HTTP/1.0的用戶端不支援100響應碼。但可以讓用戶端在請求消息中加入Expect頭域,并将它的值設定為100-continue。

節省帶寬資源的一個非常有效的做法就是壓縮要傳送的資料。Content-Encoding是對消息進行端到端(end-to-end)的編碼,它可能是資源在伺服器上儲存的固有格式(如jpeg圖檔格式);在請求消息中加入Accept-Encoding頭域,它可以告訴伺服器用戶端能夠解碼的編碼方式。而Transfer-Encoding是逐段式(hop-by-hop)的編碼,如Chunked編碼。在請求消息中加入TE頭域用來告訴伺服器能夠接收的transfer-coding方式。

9.性能優化

9.1使用多個TCP連接配接

上文已經說明,HTTP1.X并不支援多路複用,請求需要在用戶端排隊等待發送,而且容易遇到隊首阻塞的問題,并不能很好的提高資料傳輸速率。那麼,既然不能對單一連接配接進行多路複用,那是不是可以同時打開多個連接配接進行資料傳輸呢?答案是肯定的,浏覽器開發商為了解決這個問題,使浏覽器支援用戶端最多打開六個連接配接,這樣我們就可以更快速的進行通信。任何事情都兩面性,同時打開多個連接配接勢必帶來一些優化和問題,具體如下:

優點:

  • 用戶端可以并行發起多個請求;
  • 伺服器可以并行處理多個請求;
  • 第一次往返可以發送的累計分組數量是原來的6倍;

缺點:

  • 更多的套接字會占用更多的資源;
  • 并行TCP流之間競争共享的帶寬;
  • 處理多個套接字,實作更為複雜;
  • 即使并行TCP流,應用的并發能力也受限制。

這種打開多個連接配接的方式,也帶來了一些壞處,那為什麼現在還使用的如此廣泛呢?主要由以下三個原因:

  • 作為繞過HTTP限制的一個權宜之計;
  • 作為繞過TCP中低起始擁塞視窗的一個權宜之計;
  • 作為用戶端繞過不能使用TCP視窗縮放的一個權益之計。

9.2域名分區

由于HTTP1.1協定不支援多路複用,迫使浏覽器開發商為了提高通信效率,引入并維護着連接配接池,每個主機可以有6個TCP流。根據HTTP Archive統計,目前平均每個頁面要包含90個左右的資源,如果這些資源都來自于同一個主機,即使可以同時打開6個TCP流,依然會導緻明顯的排隊情形。我們并不需要把所有的資源都放在同一個主機上,可以分開放置到不同的域名下,這樣就可以增加可以同時打開的TCP流總數,可以突破浏覽器的連接配接限制,實作更高的并發能力。

理論上來說,使用的域名越多,并行能力也就越強。但是,在發送請求之前都需要進行DNS解析,不同的域名需要分别進行解析,都需要進行額外的DNS查詢,如果域名數量過多,會導緻大量的額外解析;在TCP連接配接中存在的慢啟動機制,有時候也會降低性能;而且每多一個套接字都需要用戶端和服務端消耗資源進行維護;更糟糕的是,開發者需要手動的把這些資源進行分區,部署到不同的域名下。域名分區的數量太大或太小都會影響性能,但如何确定最優的分區數量并是個很好回答的問題,因為頁面中資源的數量、用戶端連接配接的可用帶寬及延遲等都會影響分區數量的合理性。

要想确定合适的域名分區數量,隻能用最原始的方式從最小分區開始不斷的測試,觀察不同分區數目對應用的影響,然後選擇最優的一個值作為固定分區數目。

9.3連接配接與合并

最快的請求就是不用請求,不管什麼協定或什麼類型的應用,減少請求次數總是最好的優化手段。如果每個資源都是必不可少的,那你可以考慮把這些資源打包到一塊,通過一次網絡請求擷取。連接配接和拼合技術屬于以内容為中心的應用層優化,通過減少網絡往返開銷,可以明顯的提升性能。可是這些技術也需要額外的處理、部署和編碼,也會帶來額外的複雜性。把多個資源打包到一起,也可能給緩存帶來負擔,一些細微的更新都需要重新請求資源并緩存,有時候目前頁面并不需要檔案中的其他一些資源,這都會影響頁面的執行速度。

在采用連接配接與合并技術時,雖然會減少網絡往返開銷和提升性能,但也會增加應用的複雜度,以緻緩存、更新、執行速度、渲染頁面等問題。是以,采用這種優化時,應綜合考慮,尋求一種最佳的檔案打包粒度。

9.4嵌入資源

嵌入資源也是一種很常見的優化方法,把資源嵌入文檔可以減少請求的次數。嵌入頁面的資源适合特别小的,使用次數很少,最好是一次的資源。實踐中,一個經驗規則是隻考慮嵌入1-2KB以下的資源,因為小于這個标準的資源會導緻比它自身更高的HTTP開銷。然而,如果嵌入資源頻繁變更,也會導緻宿主文檔的無效緩存率升高。如果應用中要使用很小的、個别的檔案,在考慮是否嵌入時,可以參考以下建議:

  • 如果檔案很小,而且隻有個别頁面使用,可以考慮嵌入;
  • 如果檔案很小,但需要在多個頁面中使用,應該考慮集中打包;
  • 如果檔案經常需要更新,就不要嵌入了;
  • 通過減少HTTP cookie的大小将協定開銷最小化。

參考資料:

《WEB性能權威指南》

《HTTP/1.1與HTTP/1.0的差別》

《HTTP 1.1與HTTP 1.0的比較》

http://www.faqs.org/rfcs/rfc1945.html

http://www.faqs.org/rfcs/rfc2616.html

資料冰冷的,但我們要讓資料溫暖起來,改變我們的生活!