一直都沒有深入研究過 Http Proxy,最近在使用libcurl的過程中,發現在有Proxy的情況下,使用CURL請求一個https的資源,會有返回2個response。經過一番抓包和研究之後,發現另有原因。
根據RFC2817的講解發現,在使用Proxy請求https的時候,首先會使用HTTP的CONNECT Method向Proxy發起請求。
另外,更具RFC2816中關於CONNECT Method的講解,HTTP的CONNECT方法是用跟Proxy一起動態的建立HTTP隧道的。
Alt Text
如圖所示,通過Proxy,Client和Content Server之間建立起了隧道。這種隧道使得客戶端感覺不到代理的存在,在客戶端開來,它是直接跟要請求的資源服務器在通信。
Http隧道分為兩種:
1.不使用CONNECT的隧道
2.使用CONNECT的隧道
不使用CONNECT的隧道,實現了數據包的重組和轉發。也就是在我們使用Proxy的時候,然後發起Http請求,使用的就是非CONNECT的隧道。在這種情況下,在Proxy收到來自客戶端的Http請求之後,會重新創建Request請求,並發送到目標服務器,也就是圖中的Content Server。當目標服務器返回Response給Proxy之後,Proxy會對Response進行解析,然後重新組裝Response,發送給客戶端。所以,在不使用CONNECT方式建立的隧道,Proxy有機會對客戶端與目標服務器之間的通信數據進行窺探,而且有機會對數據進行串改。
而對於使用CONNECT的隧道則不同。當客戶端向Proxy發起Http CONNECT Method的時候,就是告訴Proxy,先在Proxy和目標服務器之間先建立起連接,在這個連接建立起來之後,目標服務器會返回一個回復給Proxy,Proxy將這個回復轉發給客戶端,這個Response是Proxy跟目標服務器連接建立的狀態回復,而不是請求數據的Response。在此之後,客戶端跟目標服務器的所有通信都將使用之前建立起來的建立。這種情況下的Http隧道,Proxy僅僅實現轉發,而不會關心轉發的數據。
這也是為什麼在使用Proxy的時候,Https請求必須首先使用Http CONNECT建立隧道。因為Https的數據都是經過加密的,Proxy是無法對Https的數據進行解密的,所以只能使用CONNECT,僅僅對通信數據進行轉發。
所以,實際上所有的網絡庫(例如libcurl, chromium_net)在實現的時候,如果使用了Proxy,在請求Https協議的資源是,首先是使用CONNECT方法建立Http隧道,等收到目標服務器建立成功的回復之後,開始做SSL/TLS握手,然後進行數據傳輸。
雖然Http CONNECT隧道安全,能夠使得客戶端的數據實現"翻牆", 但是Http CONNECT容易失控,由於數據都是加密的,Proxy跟目標服務器建立TCP連接之後,之後的所有數據都服用這個連接。但是Http CONNECT隧道不僅僅是可以建立Https 443端口的連接,實際上可以建立任何端口的連接,這也使得Proxy變得非常危險,所以,一般的服務器都會對Http CONNECT隧道進行控制,只能實現Https 443端口的隧道通信。