HTTP 簡史

2019-03-15 11:31:00

譯註:本文來源於 2013 年出版的《High Performance Browser Networking》的第九章,因此有些資訊略有過時。事實上,現在 HTTP/2 已經有相當的不是,而新的 HTTP/3 也在設計和標準制定當中。

介紹

超文字傳輸協定Hypertext Transfer Protocol(HTTP)是網際網路上最普遍和廣泛採用的應用程式協定之一。它是用戶端和伺服器之間的通用語言,支援現代 Web。從最初作為單個的關鍵字和文件路徑開始,它已成為不僅僅是瀏覽器的首選協定,而且幾乎是所有連線網際網路硬體和軟體應用程式的首選協定。

在本文中,我們將簡要回顧 HTTP 協定的發展歷史。對 HTTP 不同語意的完整討論超出了本文的範圍,但理解 HTTP 的關鍵設計變更以及每個變更背後的動機將為我們討論 HTTP 效能提供必要的背景,特別是在 HTTP/2 中即將進行的許多改進。

HTTP 0.9: 單行協定

蒂姆·伯納斯·李Tim Berners-Lee 最初的 HTTP 提案在設計時考慮到了簡單性,以幫助他採用他的另一個新想法:全球資訊網World Wide Web。這個策略看起來奏效了:注意,他是一個有抱負的協定設計者。

1991 年,伯納斯·李概述了這個新協定的動機,並列出了幾個高階設計目標:檔案傳輸功能、請求超文件存檔索引搜尋的能力,格式協商以及將用戶端參照到另一個伺服器的能力。為了證明該理論的實際應用,構建了一個簡單原型,它實現了所提議功能的一小部分。

  • 用戶端請求是一個 ASCII 字串。
  • 用戶端請求以回車符(CRLF)終止。
  • 伺服器響應是 ASCII 字元流。
  • 伺服器響應是一種超文件標示語言(HTML)。
  • 文件傳輸完成後連線終止。

然而,即使這聽起來也比實際複雜得多。這些規則支援的是一種非常簡單的,對 Telnet 友好的協定,一些 Web 伺服器至今仍然支援這種協定:

$> telnet google.com 80Connected to 74.125.xxx.xxxGET /about/(hypertext response)(connection closed)

請求包含這樣一行:GET 方法和請求文件的路徑。響應是一個超文字文件,沒有標題或任何其他後設資料,只有 HTML。真的是再簡單不過了。此外,由於之前的互動是預期協定的子集,因此它獲得了一個非官方的 HTTP 0.9 標籤。其餘的,就像他們所說的,都是歷史。

從 1991 年這些不起眼的開始,HTTP 就有了自己的生命,並在接下來幾年裡迅速發展。讓我們快速回顧一下 HTTP 0.9 的特性:

  • 採用用戶端-伺服器架構,是一種請求-響應協定。
  • 採用 ASCII 協定,執行在 TCP/IP 鏈路上。
  • 旨在傳輸超文字文件(HTML)。
  • 每次請求後,伺服器和用戶端之間的連線都將關閉。

流行的 Web 伺服器,如 Apache 和 Nginx,仍然支援 HTTP 0.9 協定,部分原因是因為它沒有太多功能!如果你感興趣,開啟 Telnet 對談並嘗試通過 HTTP 0.9 存取 google.com 或你最喜歡的網站,並檢查早期協定的行為和限制。

HTTP/1.0: 快速增長和 Informational RFC

1991 年至 1995 年期間,HTML 規範和一種稱為 “web 瀏覽器”的新型軟體快速發展,面向消費者的公共網際網路基礎設施也開始出現並快速增長。

完美風暴:1990 年代初的網際網路熱潮

基於蒂姆·伯納斯·李最初的瀏覽器原型,美國國家超級計算機應用中心(NCSA)的一個團隊決定實現他們自己的版本。就這樣,第一個流行的瀏覽器誕生了:NCSA Mosaic。1994 年 10 月,NCSA 團隊的一名程式設計師 Marc Andreessen 與 Jim Clark 合作建立了 Mosaic Communications,該公司後來改名為 Netscape(網景),並於 1994 年 12 月發布了 Netscape Navigator 1.0。從這一點來說,已經很清楚了,全球資訊網已經不僅僅是學術上的好奇心了。

實際上,同年在瑞士日內瓦組織了第一次全球資訊網會議,這導致全球資訊網聯盟World Wide Web Consortium(W3C)的成立,以幫助指導 HTML 的發展。同樣,在 IETF 內部建立了一個並行的HTTP 工作組HTTP Working Group(HTTP-WG),專注於改進 HTTP 協定。後來這兩個團體一直對 Web 的發展起著重要作用。

最後,完美風暴來臨,CompuServe,AOL 和 Prodigy 在 1994-1995 年的同一時間開始向公眾提供撥號上網服務。憑藉這股迅速的浪潮,Netscape 在 1995 年 8 月 9 日憑藉其成功的 IPO 創造了歷史。這預示著網際網路熱潮已經到來,人人都想分一杯羹!

不斷增長的新 Web 所需功能及其在公共網站上的應用場景很快暴露了 HTTP 0.9 的許多基礎限制:我們需要一種能夠提供超文字文件、提供關於請求和響應的更豐富的後設資料,支援內容協商等等的協定。相應地,新興的 Web 開發人員社群通過一個特殊的過程生成了大量實驗性的 HTTP 伺服器和用戶端實現來回應:實現,部署,並檢視其他人是否採用它。

從這些急速增長的實驗開始,一系列最佳實踐和常見模式開始出現。1996 年 5 月,HTTP 工作組HTTP Working Group(HTTP-WG)發布了 RFC 1945,它記錄了許多被廣泛使用的 HTTP/1.0 實現的“常見用法”。請注意,這只是一個資訊性 RFC:HTTP/1.0,如你所知的,它不是一個正式規範或 Internet 標準!

話雖如此,HTTP/1.0 請求看起來應該是:

$> telnet website.org 80Connected to xxx.xxx.xxx.xxxGET /rfc/rfc1945.txt HTTP/1.0 ?User-Agent: CERN-LineMode/2.15 libwww/2.17b3Accept: */*HTTP/1.0 200 OK ?Content-Type: text/plainContent-Length: 137582Expires: Thu, 01 Dec 1997 16:00:00 GMTLast-Modified: Wed, 1 May 1996 12:45:26 GMTServer: Apache 0.84(plain-text response)(connection closed)
  • ? 請求行有 HTTP 版本號,後面跟請求頭
  • ? 響應狀態,後跟響應頭

前面的互動並不是 HTTP/1.0 功能的詳盡列表,但它確實說明了一些關鍵的協定更改:

  • 請求可能多個由換行符分隔的請求頭欄位組成。
  • 響應物件的字首是響應狀態行。
  • 響應物件有自己的一組由換行符分隔的響應頭欄位。
  • 響應物件不限於超文字。
  • 每次請求後,伺服器和用戶端之間的連線都將關閉。

請求頭和響應頭都保留為 ASCII 編碼,但響應物件本身可以是任何型別:HTML 檔案、純文字檔案、影象或任何其他內容型別。因此,HTTP 的“超文字傳輸”部分在引入後不久就變成了用詞不當。實際上,HTTP 已經迅速發展成為一種超媒體傳輸,但最初的名稱沒有改變。

除了媒體型別協商之外,RFC 還記錄了許多其他常用功能:內容編碼、字元集支援、多部分型別、授權、快取、代理行為、日期格式等。

今天,幾乎所有 Web 上的伺服器都可以並且仍將使用 HTTP/1.0。不過,現在你應該更加清楚了!每個請求都需要一個新的 TCP 連線,這會對 HTTP/1.0 造成嚴重的效能損失。參見三次握手,接著會慢啟動

HTTP/1.1: Internet 標準

將 HTTP 轉變為官方 IETF 網際網路標準的工作與圍繞 HTTP/1.0 的文件工作並行進行,並計劃從 1995 年至 1999 年完成。事實上,第一個正式的 HTTP/1.1 標準定義於 RFC 2068,它在 HTTP/1.0 發布大約六個月後,即 1997 年 1 月正式發布。兩年半後,即 1999 年 6 月,一些新的改進和更新被納入標準,並作為 RFC 2616 發布。

HTTP/1.1 標準解決了早期版本中發現的許多協定歧義,並引入了一些關鍵的效能優化:保持連線,分塊編碼傳輸,位元組範圍請求,附加快取機制,傳輸編碼和請求管道。

有了這些功能,我們現在可以審視一下由任何現代 HTTP 瀏覽器和用戶端執行的典型 HTTP/1.1 對談:

$> telnet website.org 80Connected to xxx.xxx.xxx.xxxGET /index.html HTTP/1.1 ?Host: website.orgUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_4)... (snip)Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8Accept-Encoding: gzip,deflate,sdchAccept-Language: en-US,en;q=0.8Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3Cookie: __qca=P0-800083390... (snip)HTTP/1.1 200 OK ?Server: nginx/1.0.11Connection: keep-aliveContent-Type: text/html; charset=utf-8Via: HTTP/1.1 GWADate: Wed, 25 Jul 2012 20:23:35 GMTExpires: Wed, 25 Jul 2012 20:23:35 GMTCache-Control: max-age=0, no-cacheTransfer-Encoding: chunked100 ?<!doctype html>(snip)100(snip)0 ?GET /favicon.ico HTTP/1.1 ?Host: www.website.orgUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_4)... (snip)Accept: */*Referer: http://website.org/Connection: close ?Accept-Encoding: gzip,deflate,sdchAccept-Language: en-US,en;q=0.8Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3Cookie: __qca=P0-800083390... (snip)HTTP/1.1 200 OK ?Server: nginx/1.0.11Content-Type: image/x-iconContent-Length: 3638Connection: closeLast-Modified: Thu, 19 Jul 2012 17:51:44 GMTCache-Control: max-age=315360000Accept-Ranges: bytesVia: HTTP/1.1 GWADate: Sat, 21 Jul 2012 21:35:22 GMTExpires: Thu, 31 Dec 2037 23:55:55 GMTEtag: W/PSA-GAu26oXbDi(icon data)(connection closed)
  • ? 請求的 HTML 檔案,包括編、字元集和 cookie 後設資料
  • ? 原始 HTML 請求的分塊響應
  • ? 以 ASCII 十六進位制數位(256 位元組)表示塊中的八位元的數量
  • ? 分塊流響應結束
  • ? 在相同的 TCP 連線上請求一個圖示檔案
  • ? 通知伺服器不再重用連線
  • ? 圖示響應後,然後關閉連線

哇,這裡發生了很多事情!第一個也是最明顯的區別是我們有兩個物件請求,一個用於 HTML 頁面,另一個用於影象,它們都通過一個連線完成。這就是保持連線的實際應用,它允許我們重用現有的 TCP 連線到同一個主機的多個請求,提供一個更快的終端使用者體驗。參見TCP 優化

要終止持久連線,注意第二個用戶端請求通過 Connection 請求頭向伺服器傳送顯示的 close。類似地,一旦傳輸響應,伺服器就可以通知用戶端關閉當前 TCP 連線。從技術上講,任何一方都可以在沒有此類信號的情況下終止 TCP 連線,但用戶端和伺服器應盡可能提供此類信號,以便雙方都啟用更好的連線重用策略。

HTTP/1.1 改變了 HTTP 協定的語意,預設情況下使用保持連線。這意味著,除非另有說明(通過 Connection:close 頭),否則伺服器應預設保持連線開啟。

但是,同樣的功能也被反向移植到 HTTP/1.0 上,通過 Connection:keep-Alive 頭啟用。因此,如果你使用 HTTP/1.1,從技術上講,你不需要 Connection:keep-Alive 頭,但許多用戶端仍然選擇提供它。

此外,HTTP/1.1 協定還新增了內容、編碼、字元集,甚至語言協商、傳輸編碼、快取指令、用戶端 cookie,以及可以針對每個請求協商的十幾個其他功能。

我們不打算詳細討論每個 HTTP/1.1 特性的語意。這個主題可以寫一本專門的書了,已經有了很多很棒的書。相反,前面的範例很好地說明了 HTTP 的快速進展和演變,以及每個用戶端-伺服器交換的錯綜複雜的過程,裡面發生了很多事情!

要了解 HTTP 協定所有內部工作原理,參考 David Gourley 和 Brian Totty 共同撰寫的權威指南: The Definitive Guide。

HTTP/2: 提高傳輸效能

RFC 2616 自發布以來,已經成為網際網路空前增長的基礎:數十億各種形狀和大小的裝置,從台式電腦到我們口袋裡的小型網路裝置,每天都在使用 HTTP 來傳送新聞,視訊,在我們生活中的數百萬的其他網路應用程式都在依靠它。

一開始是一個簡單的,用於檢索超文字的簡單協定,很快演變成了一種通用的超媒體傳輸,現在十年過去了,它幾乎可以為你所能想象到的任何用例提供支援。可以使用協定的伺服器無處不在,用戶端也可以使用協定,這意味著現在許多應用程式都是專門在 HTTP 之上設計和部署的。

需要一個協定來控制你的咖啡壺?RFC 2324 已經涵蓋了超文字咖啡壺控制協定(HTCPCP/1.0)- 它原本是 IETF 在愚人節開的一個玩笑,但在我們這個超連結的新世界中,它不僅僅意味著一個玩笑。

超文字傳輸協定(HTTP)是一個應用程式級的協定,用於分散式、共同作業、超媒體資訊系統。它是一種通用的、無狀態的協定,可以通過擴充套件請求方法、錯誤碼和頭,用於超出超文字之外的許多工,比如名稱伺服器和分散式物件管理系統。HTTP 的一個特性是資料表示的型別和協商,允許獨立於傳輸的資料構建系統。

RFC 2616: HTTP/1.1, June 1999

HTTP 協定的簡單性是它最初被採用和快速增長的原因。事實上,現在使用 HTTP 作為主要控制和資料協定的嵌入式裝置(感測器,執行器和咖啡壺)並不罕見。但在其自身成功的重壓下,隨著我們越來越多地繼續將日常互動轉移到網路 —— 社交、電子郵件、新聞和視訊,以及越來越多的個人和工作空間,它也開始顯示出壓力的跡象。使用者和 Web 開發人員現在都要求 HTTP/1.1 提供近乎實時的響應能力和協定 效能,如果不進行一些修改,就無法滿足這些要求。

為了應對這些新挑戰,HTTP 必須繼續發展,因此 HTTPbis 工作組在 2012 年初宣布了一項針對 HTTP/2 的新計劃:

已經有一個協定中出現了新的實現經驗和興趣,該協定保留了 HTTP 的語意,但是沒有保留 HTTP/1.x 的訊息方塊架和語法,這些問題已經被確定為妨礙效能和鼓勵濫用底層傳輸。

工作組將使用有序的雙向流中生成 HTTP 當前語意的新表示式的規範。與 HTTP/1.x 一樣,主要傳輸目標是 TCP,但是應該可以使用其他方式傳輸。

HTTP/2 charter, January 2012

HTTP/2 的主要重點是提高傳輸效能並支援更低的延遲和更高的吞吐量。主要的版本增量聽起來像是一個很大的步驟,但就效能而言,它將是一個重大的步驟,但重要的是要注意,沒有任何高階協定語意收到影響:所有的 HTTP 頭,值和用例是相同的。

任何現有的網站或應用程式都可以並且將通過 HTTP/2 傳送而無需修改。你無需修改應用程式標記來利用 HTTP/2。HTTP 伺服器將來一定會使用 HTTP/2,但這對大多數使用者來說應該是透明的升級。如果工作組實現目標,唯一的區別應該是我們的應用程式以更低的延遲和更好的網路連線利用率來傳送資料。

話雖如此,但我們不要走的太遠了。在討論新的 HTTP/2 協定功能之前,有必要回顧一下我們現有的 HTTP/1.1 部署和效能最佳實踐。HTTP/2 工作組正在新規範上取得快速的進展,但即使最終標準已經完成並準備就緒,在可預見的未來,我們仍然必須支援舊的 HTTP/1.1 用戶端,實際上,這得十年或更長時間。