前面的話
Web是一種強大的內容發布工具。人們已經從只在網上發送靜態的在線文檔,發展到共享更復雜的資源,比如數據庫內容或動態生成的HTML頁面。Web瀏覽器為用戶提供了一種統一的方式來訪問因特網上的內容
HTTP也已成為應用程序開發者的一種基本構造模塊,開發者們可以在HTTP上梢回其他的協議內容。比如,可以將其他協議的流量包裹在HTTP中,用HTTP通過隧道或中繼方式將這些流量傳過公司的防火牆。Web上所有的資源都可以使用HTTP協議,而且其他應用程序和應用程序協議也可以利用HTTP來完成它們的任務
本文將詳細介紹網關、隧道和中繼
網關
HTTP擴展和接口的發展是由用戶需求驅動的。要在Web上發布更復雜資源的需求出現時,人們很快就明確了一點:單個應用程序無法處理所有這些能想到的資源
為了解決這個問題,開發者提出了網關(gateway)的概念,網關可以作為某種翻譯器使用,它抽象出了一種能夠到達資源的方法。網關是資源和應用程序之間的粘合劑。應用程序可以通過HTTP或其他已定義的接口請求網關來處理某條請求,網關可以提供一條響應。網關可以向數據庫發送査詢語句,或者生成動態的內容,就像一個門一樣:進去一條請求,出來一個響應
下圖顯示的是一種資源網關。在這里,Joe的服務器就是作為連接數據庫內容的網關使用的——注意,客戶端只是在通過HTTP請求資源,而Joe的服務器在與網關進行交互以獲取資源

有些網關會自動將HTTP流量轉換為其他協議,這樣HTTP客戶端無需了解其他協議,就可以與其他應用程序進行交互

在圖a中,網關收到了對FTP URL的HTTP請求。然后網關打開FTP連接,並向FTP服務器發布適當的命令。然后將文檔和正確的HTTP首部通過HTTP回送
在圖b中,網關通過SSL收到了一條加密的Web請求,網關會對請求進行解
密,然后向目標服務器轉發一條普通的HTTP請求。可以將這些安全加速器直接放在(通常處於同一場所的)Web服務器前面,以便為原始服務器提供髙性能的加密機制
在圖c中,網關通過應用程序服務器網關API,將HTTP客戶端連接到服務器端的應用程序上去。在網上的電子商店購物,査看天氣預報,或者獲取股票報價時,訪問的就是應用程序服務器網關
Web網關在一側使用HTTP協議,在另一側使用另一種協議。可以用一個斜杠來分隔客戶端和服務器端協議,並以此對網關進行描述:
<客戶端協議>/<服務器端協議>
[注意]在不同HTTP版本之間進行轉換的Web代理就像網關一樣,它們會執行復雜的邏輯,以便在各個端點之間進行溝通。但因為它們在兩側使用的都是HTTP,所以從技術上來講,它們還是代理
因此,將HTTP客戶端連接到NNTP新聞服務器的網關就是一個HTTP/NNTP網關。我們用術語服務器端網關和客戶端網關來說明對話是在網關的哪一側進行的
服務器端網關(server-side gateway)通過HTTP與客戶端對話,通過其他協議與服務器通信(HTTP/*);客戶端網關(client-side gateway)通過其他協議與客戶端對話,通過HTTP與服務器通信<*/HTTP>
【協議網關】
將HTTP流量導向網關時所使用的方式與將流量導向代理的方式相同。最常見的方式是,顯式地配置瀏覽器使用網關,對流量進行透明的攔截,或者將網關配置為替代者(反向代理)

1、 HTTP/*:服務器端Web網關
請求流入原始服務器時,服務器端Web網關會將客戶端HTTP請求轉換為其他協議
在下圖中,網關收到了一條對FTP資源的HTTP請求:
ftp://ftp.irs.gov/pub/00-index.txt

網關會打開一條到原始服務器FTP端口(端口 21)的FTP連接,通過FTP協議獲取對象。網關會發送USER和PASS命令登錄到服務器上去;發布CWD命令,轉移到服務器上合適的目錄中去;將下載類型設置為ASCII;用MDTM獲取文檔的最后修改時間;用PASV告訴服務器將有被動數據獲取請求到達;用RETR請求進行對象獲取;打開到FTP服務器的數據連接,服務器端口由控制信道返回,一旦數據信道打開了,就將對象內容回送給網關
完成獲取之后,會將對象放在一條HTTP響應中回送給客戶端
2、HTTP/HTTPS:服務器端安全網關
一個組織可以通過網關對所有的輸入Web請求加密,以提供額外的隱私和安全性保護。客戶端可以用普通的HTTP瀏覽Web內容,但網關會自動加密用戶的對話

3、HTTPS/HTTP:客戶端安全加速器網關
可以將HTTPS/HTTP網關作為安全加速器使用,這些HTTPS/HTTP網關位於Web服務器之前,通常作為不可見的攔截網關或反向代理使用。它們接收安全的HTTPS流量,對安全流量進行解密,並向Web服務器發送普通的HTTP請求

這些網關中通常都包含專用的解密硬件,以比原始服務器有效得多的方式來解密安全流量,以減輕原始服務器的負荷。這些網關在網關和原始服務器之間發送的是未加密的流量。所以,要謹慎使用,確保網關和原始服務器之間的網絡是安全的
【資源網關】
最常見的網關——應用程序服務器,會將目標服務器與網關結合在一個服務器中實現。應用程序服務器是服務器端網關,與客戶端通過HTTP進行通信,並與服務器端的應用程序相連

在上圖中,兩個客戶端是通過HTTP連接到應用程序服務器的。但應用程序服務器並沒有回送文件,而是將請求通過一個網關應用編程接口(Application Programming Interface, API)發送給運行在服務器上的應用程序
收到客戶端A的請求,根據URI將其通過API發送給一個數碼相機應用程序。 將得到的圖片綁定到一條HTTP響應報文中,再回送給客戶端,在客戶端的瀏覽器中顯示
客戶端B的URI請求的是一個電子商務應用程序。客戶端B的請求是通過服務器網關API發送給電子商務軟件的,結果會被回送給瀏覽器。電子商務軟件與客戶端進行交互,引導用戶通過一系列HTML頁面來完成購物
第一個流行的應用程序網關API就是通用網關接口(Common Gateway Interface, CGI)。CGI是一個標准接口集,Web服務器可以用它來裝載程序以響應對特定URL的HTTP請求,並收集程序的輸出數據,將其放在HTTP響應中回送
早期的Web服務器是相當簡單的,請求需要使用網關的資源時,服務器會請輔助應用程序來處理請求。服務器會將輔助應用程序所需的數據傳送給它。通常就是整條請求,或者用戶想在數據庫上運行的請求(來自URL的請求字符串)之類的東西
然后,它會向服務器返回一條響應或響應數據,服務器則會將其轉發給客戶端。服務器和網關是相互獨立的應用程序,因此,它們的責任是分得很清楚
下圖顯示了服務器與網關應用程序之間交互的基本運行機制

這個簡單的協議(輸入請求,轉交,響應)就是最古老,也最常用的服務器擴展接口CGI的本質
【CGI】
CGI在Web上廣泛用於動態HTML,信用卡處理以及數據庫査詢等任務。CGI應用程序獨立於服務器,所以,幾乎可以用任意語言來實現,包括Perl、Tel、C和各種shell語言。CGI很簡單,幾乎所有的HTTP服務器都支持它
CGI的處理對用戶來說是不可見的。從客戶端的角度來看,就像發起一個普通請求一樣。它完全不清楚服務器和CGI應用程序之間的轉接過程。URL中出現字符cgi和可能出現的“?”是客戶端發現使用了CGI應用程序的唯一線索
CGI在服務器和眾多的資源類型之間提供了一種簡單的、函數形式的粘合方式,用來處理各種需要的轉換。這個接口還能很好地保護服務器,防止一些糟糕的擴展對它造成的破壞(如果這些擴展直接與服務器相連,造成的錯誤可能會引發服務器崩潰)
但是,這種分離會造成性能的耗費。為每條CGI請求引發一個新進程的開銷是很高的,會限制那些使用CGI的服務器的性能,並且會加重服務端機器資源的負擔。為了解決這個問題,人們開發了一種新型CGI——並將其恰當地稱為快速CGI。這個接口模擬了CGI,但它是作為持久守護進程運行的,消除了為每個請求建立或拆除新進程所帶來的性能損耗
CGI協議為外部翻譯器與現有的HTTP服務器提供了一種簡潔的接口方式,但如果想要改變服務器自身的行為,或者只是想盡可能地提升能從服務器上獲得的性能呢?服務器開發者為這兩種需求提供了幾種服務器擴展API,為Web開發者提供了強大的接口,以便他們將自己的模塊與HTTP服務器直接相連。擴展API允許程序員將自己的代碼嫁接到服務器上,或者用自己的代碼將服務器的一個組件完整地替換出來
大多數流行的服務器都會為開發者提供一個或多個擴展API。這些擴展通常都會綁定在服務器自身的結構上,所以,大多數都是某種服務器類型特有的。Apache和其他服務器都提供了一些API接口,允許開發者通過這些接口改變服務器的行為,或者為不同的資源提供一些定制的接口。這些定制接口為開發者提供了強大的接口方式
隨着Web應用程序提供的服務類型越來越多,HTTP可以作為一種連接應用程序的基礎軟件來使用。在將應用程序連接起來的過程中,一個更為棘手的問題是在兩個應用程序之間進行協議接口的協商,以便這些應用程序可以進行數據的交換——這通常都是針對具體應用程序的個案進行的
應用程序之間要配合工作,所要交互的信息比HTTP首部所能表達的信息要復雜得多。因特網委員會開發了一組允許Web應用程序之間相互通信的標准和協議。盡管Web服務(Web service)可以用來表示獨立的Web應用程序(構造模塊),這里我們還是寬松地用這個術語來表示這些標准。Web服務的引入並不新鮮,但這是應用程序共享信息的一種新機制。Web服務是構建在標准的Web技術(比如HTTP)之上的
Web服務可以用XML通過SOAP來交換信息。XML(Extensible Markup Language, 擴展標記語言)提供了一種創建數據對象的定制信息,並對其進行解釋的方法。SOAP(Simple Object Access Protocol,簡單對象訪問協議)是向HTTP報文中添加XML信息的標准方式
隧道
Web隧道(Web tunnel)是HTTP的另一種用法,可以通過HTTP應用程序訪問使用非HTTP協議的應用程序。Web隧道允許用戶通過HTTP連接發送非HTTP流量,這樣就可以在HTTP上捎帶其他協議數據了。使用Web隧道最常見的原因就是要在HTTP連接中嵌入非HTTP流量,這樣,這類流量就可以穿過只允許Web流量通過的防火牆了
【建立隧道】
Web隧道是用HTTP的CONNECT方法建立起來的。CONNECT方法請求隧道網關創建一條到達任意目的服務器和端口的TCP連接,並對客戶端和服務器之間的后繼數據進行盲轉發
下圖顯示了CONNECT方法如何建立起一條到達網關的隧道

在圖a中,客戶端發送了一條CONNECT請求給隧道網關。客戶端的CONNECT方法請求隧道網關打開一條TCP連接(在這里,打開的是到主機orders.joes-hardware.com的標准SSL端口443的連接);在圖b和圖c中創建了TCP連接;一旦建立了TCP連接,網關就會發送一條HTTP 200 Connection Established響應來通知客戶端;此時,隧道就建立起來了。客戶端通過HTTP隧道發送的所有數據都會被直接轉發給輸出TCP連接,服務器發送的所有數據都會通過HTTP隧道轉發給客戶端
上圖中的例子描述了一條SSL隧道,其中的SSL流量是在一條HTTP連接上發送的,但是通過CONNECT方法可以與使用任意協議的任意服務器建立TCP連接
1、CONNECT請求
除了起始行之外,CONNECT的語法與其他HTTP方法類似。一個后面跟着冒號和端口號的主機名取代了請求URI。主機和端口都必須指定:
CONNECT home.netscape.com:443 HTTP/1.0 User-agent: Mozilla/4.0
和其他HTTP報文一樣,起始行之后,有零個或多個HTTP請求首部字段。這些行照例以CRLF結尾,首部列表以一個空的CRLF結束
2、CONNECT響應
發送了請求之后,客戶端會等待來自網關的響應。和普通HTTP報文一樣,響應碼200表示成功。按照慣例,響應中的原因短語通常被設置為“Connection Established”
HTTP/1.0 200 Connection Established Proxy-agent: Netscape-Proxy/1.1
與普通HTTP響應不同,這個響應並不需要包含Content-Type首部。此時連接只是對原始字節進行轉接,不再是報文的承載者,所以不需要使用內容類型
管道化數據對網關是不透明的,所以網關不能對分組的順序和分組流作任何假設。一旦隧道建立起來了,數據就可以在任意時間流向任意方向了
作為一種性能優化方法,允許客戶端在發送了CONNECT請求之后,接收響應之前,發送隧道數據。這樣可以更快地將數據發送給服務器,但這就意味着網關必須能夠正確處理跟在請求之后的數據。尤其是,網關不能假設網絡I/O請求只會返回首部數據,網關必須確保在連接准備就緒時,將與首部一同讀進來的數據發送給服務器。在請求之后以管道方式發送數據的客戶端,如果發現回送的響應是認證請求,或者其他非200但不致命的錯誤狀態,就必須做好重發請求數據的准備。如果在任意時刻,隧道的任意一個端點斷開了連接,那個端點發出的所有未傳輸數據都會被傳送給另一個端點,之后,到另一個端點的連接也會被代理終止。如果還有數據要傳輸給關閉連接的端點,數據會被丟棄
【SSL隧道】
最初開發Web隧道是為了通過防火牆來傳輸加密的SSL流量。很多組織都會將所有流量通過分組過濾路由器和代理服務器以隧道方式傳輸,以提升安全性。但有些協議,比如加密SSL,其信息是加密的,無法通過傳統的代理服務器轉發。隧道會通過一條HTTP連接來傳輸SSL流置,以穿過端口80的HTTP防火牆

為了讓SSL流量經現存的代理防火牆進行傳輸,HTTP中添加了一項隧道特性,在此特性中,可以將原始的加密數據放在HTTP報文中,通過普通的HTTP信道傳送

在圖a中,SSL流量被直接發送給了一個(SSL端口443上的)安全Web服務器。在圖b中,SSL流量被封裝到一條HTTP報文中,並通過HTTP端口80上的連接發送,最后被解封裝為普通的SSL連接
通常會用隧道將非HTTP流量傳過端口過濾防火牆。這一點可以得到很好的利用。比如,通過防火牆傳輸安全SSL流量。但是,這項特性可能會被濫用,使得惡意協議通過HTTP隧道流入某個組織內部
可以像其他協議一樣,對HTTPS協議(SSL上的HTTP)進行網關操作:由網關(而不是客戶端)初始化與遠端HTTPS服務器的SSL會話,然后代表客戶端執行 HTTPS事務。響應會由代理接收並解密,然后通過(不安全的)HTTP傳送給客戶端。這是網關處理FTP的方式。但這種方式有幾個缺點:客戶端到網關之間的連接是普通的非安全HTTP;盡管代理是已認證主體,但客戶端無法對遠端服務器執行SSL客戶端認證(基於X509證書的認證);網關要支持完整的SSL實現
對於SSL隧道機制來說,無需在代理中實現SSL。SSL會話是建立在產生請求的客戶端和目的(安全的)Web服務器之間的,中間的代理服務器只是將加密數據經過隧道傳輸,並不會在安全事務中扮演其他的角色
在適當的情況下,也可以將HTTP的其他特性與隧道配合使用。尤其是,可以將代理的認證支持與隧道配合使用,對客戶端使用隧道的權利進行認證

總的來說,隧道網關無法驗證目前使用的協議是否就是它原本打算經過隧道傳輸的協議。因此,比如說,一些喜歡搗亂的用戶可能會通過本打算用於SSL的隧道,越過公司防火牆傳遞因特網游戲流量,而惡意用戶可能會用隧道打開Telnet會話,或用隧道繞過公司的E-mail掃描器來發送E-mail。為了降低對隧道的濫用,網關應該只為特定的知名端口,比如HTTPS的端口443打開隧道
中繼
中繼(relay)是沒有完全遵循HTTP規范的簡單HTTP代理。中繼負責處理HTTP中建立連接的部分,然后對字節進行盲轉發
HTTP很復雜,所以實現基本的代理功能並對流量進行盲轉發,而且不執行任何首部和方法邏輯,有時是很有用的。盲中繼很容易實現,所以有時會提供簡單的過濾、診斷或內容轉換功能。但這種方式可能潛在嚴重的互操作問題,所以部署的時候要特別小心
某些簡單盲中繼實現中存在的一個更常見(也更聲名狼藉的)問題是,由於它們無法正確處理Connection首部,所以有潛在的掛起keep-alive連接的可能

在圖a中,Web客戶端向中繼發送了一條包含Connection: Keep-Alive首部的報文,如果可能的話要求建立一條keep-alive連接。客戶端等待響應,以確定它要求建立keep-alive信道的請求是否被認可了
中繼收到了這條HTTP請求,但它並不理解Connection首部,因此會將報文一字不漏地沿着鏈路傳遞給服務器。但Connection首部是個逐跳首部,只適用於單條傳輸鏈路,是不應該沿着鏈路傳送下去的
在圖b中,經過中繼轉發的HTTP請求抵達Web服務器。當Web服務器收到經過代理轉發的Connection: Keep-Alive首部時,會錯誤地認為中繼(對服務器來說,它看起來就和其他客戶端一樣)要求進行keep-alive的對話。這對Web服務器來說沒什么問題——它同意進行keep-alive對話,並在圖c中回送了一個Connection: Keep-Alive響應首部。此時,Web服務器就認為它是在與中繼進行keep-alive對話,會遵循keep-alive對話的規則。但中繼對keep-alive會話根本就一無所知
在圖d中,中繼將Web服務器的響應報文,以及來自Web服務器的Connection: Keep-Alive首部一起發回給客戶端。客戶端看到這個首部,認為中繼同意進行keep-alive對話。此時,客戶端和服務器都認為它們是在進行keep-alive對話,但與它們進行對話的中繼卻根本不知道什么keep-alive對話
中繼對持久對話一無所知,所以它會將收到的所有數據都轉發給客戶端,等待原始服務器關閉連接。但原始服務器認為中繼要求服務器將連接保持在活躍狀態,所以是不會關閉連接的。這樣,中繼就會掛起,等待連接的關閉
在圖d中,當客戶端收到回送的響應報文時,它會直接轉向第二條請求,在 keep-alive連接上向中繼發送另一條請求。簡單中繼通常不會期待同一條連接上還會有另一條請求到達。瀏覽器上的圈會不停地轉,但沒有任何進展
有一些方法可以使中繼稍微智能一些,以消除這些風險,但所有簡化的代理都存在着出現互操作性問題的風險。要為某個特定目標構建簡單的HTTP中繼,一定要特別注意其使用方法。對任何大規模部署來說,都要非常認真地考慮使用真正的、完全遵循HTTP的代理服務器