成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

HTTP 代理原理及實(shí)現(xiàn)(一)

網(wǎng)絡(luò) 網(wǎng)絡(luò)管理
Web 代理是一種存在于網(wǎng)絡(luò)中間的實(shí)體,提供各式各樣的功能。現(xiàn)代網(wǎng)絡(luò)系統(tǒng)中,Web 代理無處不在。我之前有關(guān) HTTP 的博文中,多次提到了代理對 HTTP 請求及響應(yīng)的影響。今天這篇文章,我打算談?wù)?HTTP 代理本身的一些原理,以及如何用 Node.js 快速實(shí)現(xiàn)代理。

Web 代理是一種存在于網(wǎng)絡(luò)中間的實(shí)體,提供各式各樣的功能?,F(xiàn)代網(wǎng)絡(luò)系統(tǒng)中,Web 代理無處不在。我之前有關(guān) HTTP 的博文中,多次提到了代理對 HTTP 請求及響應(yīng)的影響。今天這篇文章,我打算談?wù)?HTTP 代理本身的一些原理,以及如何用 Node.js 快速實(shí)現(xiàn)代理。

HTTP 代理存在兩種形式,分別簡單介紹如下:

***種是 RFC 7230 - HTTP/1.1: Message Syntax and Routing(即修訂后的 RFC 2616,HTTP/1.1 協(xié)議的***部分)描述的普通代理。這種代理扮演的是「中間人」角色,對于連接到它的客戶端來說,它是服務(wù)端;對于要連接的服務(wù)端來說,它是客戶端。它就負(fù)責(zé)在兩端之間來回傳送 HTTP 報(bào)文。

第二種是 Tunneling TCP based protocols through Web proxy servers(通過 Web 代理服務(wù)器用隧道方式傳輸基于 TCP 的協(xié)議)描述的隧道代理。它通過 HTTP 協(xié)議正文部分(Body)完成通訊,以 HTTP 的方式實(shí)現(xiàn)任意基于 TCP 的應(yīng)用層協(xié)議代理。這種代理使用 HTTP 的 CONNECT 方法建立連接,但 CONNECT 最開始并不是 RFC 2616 - HTTP/1.1 的一部分,直到 2014 年發(fā)布的 HTTP/1.1 修訂版中,才增加了對 CONNECT 及隧道代理的描述,詳見 RFC 7231 - HTTP/1.1: Semantics and Content。實(shí)際上這種代理早就被廣泛實(shí)現(xiàn)。

本文描述的***種代理,對應(yīng)《HTTP 權(quán)威指南》一書中第六章「代理」;第二種代理,對應(yīng)第八章「集成點(diǎn):網(wǎng)關(guān)、隧道及中繼」中的 8.5 小節(jié)「隧道」。

普通代理

***種 Web 代理原理特別簡單:

HTTP 客戶端向代理發(fā)送請求報(bào)文,代理服務(wù)器需要正確地處理請求和連接(例如正確處理 Connection: keep-alive),同時(shí)向服務(wù)器發(fā)送請求,并將收到的響應(yīng)轉(zhuǎn)發(fā)給客戶端。

下面這張圖片來自于《HTTP 權(quán)威指南》,直觀地展示了上述行為:

 [[157909]]

假如我通過代理訪問 A 網(wǎng)站,對于 A 來說,它會把代理當(dāng)做客戶端,完全察覺不到真正客戶端的存在,這實(shí)現(xiàn)了隱藏客戶端 IP 的目的。當(dāng)然代理也可以修改 HTTP 請求頭部,通過 X-Forwarded-IP 這樣的自定義頭部告訴服務(wù)端真正的客戶端 IP。但服務(wù)器無法驗(yàn)證這個(gè)自定義頭部真的是由代理添加,還是客戶端修改了請求頭,所以從 HTTP 頭部字段獲取 IP 時(shí),需要格外小心。

給瀏覽器顯式的指定代理,需要手動修改瀏覽器或操作系統(tǒng)相關(guān)設(shè)置,或者指定 PAC(Proxy Auto-Configuration,自動配置代理)文件自動設(shè)置,還有些瀏覽器支持 WPAD(Web Proxy Autodiscovery Protocol,Web 代理自動發(fā)現(xiàn)協(xié)議)。顯式指定瀏覽器代理這種方式一般稱之為正向代理,瀏覽器啟用正向代理后,會對 HTTP 請求報(bào)文做一些修改,來規(guī)避老舊代理服務(wù)器的一些問題。

還有一種情況是訪問 A 網(wǎng)站時(shí),實(shí)際上訪問的是代理,代理收到請求報(bào)文后,再向真正提供服務(wù)的服務(wù)器發(fā)起請求,并將響應(yīng)轉(zhuǎn)發(fā)給瀏覽器。這種情況一般被稱之為反向代理,它可以用來隱藏服務(wù)器 IP 及端口。一般使用反向代理后,需要通過修改 DNS 讓域名解析到代理服務(wù)器 IP,這時(shí)瀏覽器無法察覺到真正服務(wù)器的存在,當(dāng)然也就不需要修改配置了。反向代理是 Web 系統(tǒng)最為常見的一種部署方式,例如本博客就是使用 Nginx 的proxy_pass 功能將瀏覽器請求轉(zhuǎn)發(fā)到背后的 Node.js 服務(wù)。

了解完***種代理的基本原理后,我們用 Node.js 實(shí)現(xiàn)一下它。只包含核心邏輯的代碼如下:

  1. var http = require('http'); 
  2. var net = require('net'); 
  3. var url = require('url'); 
  4.  
  5. function request(cReq, cRes) { 
  6.     var u = url.parse(cReq.url); 
  7.  
  8.     var options = { 
  9.         hostname : u.hostname,  
  10.         port     : u.port || 80, 
  11.         path     : u.path,        
  12.         method     : cReq.method, 
  13.         headers     : cReq.headers 
  14.     }; 
  15.  
  16.     var pReq = http.request(options, function(pRes) { 
  17.         cRes.writeHead(pRes.statusCode, pRes.headers); 
  18.         pRes.pipe(cRes); 
  19.     }).on('error'function(e) { 
  20.         cRes.end(); 
  21.     }); 
  22.  
  23.     cReq.pipe(pReq); 
  24.  
  25. http.createServer().on('request', request).listen(8888, '0.0.0.0'); 

以上代碼運(yùn)行后,會在本地 8888 端口開啟 HTTP 代理服務(wù),這個(gè)服務(wù)從請求報(bào)文中解析出請求 URL 和其他必要參數(shù),新建到服務(wù)端的請求,并把代理收到的請求轉(zhuǎn)發(fā)給新建的請求,***再把服務(wù)端響應(yīng)返回給瀏覽器。修改瀏覽器的 HTTP 代理為 127.0.0.1:8888 后再訪問 HTTP 網(wǎng)站,代理可以正常工作。

但是,使用我們這個(gè)代理服務(wù)后,HTTPS 網(wǎng)站完全無法訪問,這是為什么呢?答案很簡單,這個(gè)代理提供的是 HTTP 服務(wù),根本沒辦法承載 HTTPS 服務(wù)。那么是否把這個(gè)代理改為 HTTPS 就可以了呢?顯然也不可以,因?yàn)檫@種代理的本質(zhì)是中間人,而 HTTPS 網(wǎng)站的證書認(rèn)證機(jī)制是中間人劫持的克星。普通的 HTTPS 服務(wù)中,服務(wù)端不驗(yàn)證客戶端的證書,中間人可以作為客戶端與服務(wù)端成功完成 TLS 握手;但是中間人沒有證書私鑰,無論如何也無法偽造成服務(wù)端跟客戶端建立 TLS 連接。當(dāng)然如果你擁有證書私鑰,代理證書對應(yīng)的 HTTPS 網(wǎng)站當(dāng)然就沒問題了。

HTTP 抓包神器 Fiddler 的工作原理也是在本地開啟 HTTP 代理服務(wù),通過讓瀏覽器流量走這個(gè)代理,從而實(shí)現(xiàn)顯示和修改 HTTP 包的功能。如果要讓 Fiddler 解密 HTTPS 包的內(nèi)容,需要先將它自帶的根證書導(dǎo)入到系統(tǒng)受信任的根證書列表中。一旦完成這一步,瀏覽器就會信任 Fiddler 后續(xù)的「偽造證書」,從而在瀏覽器和 Fiddler、Fiddler 和服務(wù)端之間都能成功建立 TLS 連接。而對于 Fiddler 這個(gè)節(jié)點(diǎn)來說,兩端的 TLS 流量都是可以解密的。

如果我們不導(dǎo)入根證書,F(xiàn)iddler 的 HTTP 代理還能代理 HTTPS 流量么?實(shí)踐證明,不導(dǎo)入根證書,F(xiàn)iddler 只是無法解密 HTTPS 流量,HTTPS 網(wǎng)站還是可以正常訪問。這是如何做到的,這些 HTTPS 流量是否安全呢?這些問題將在下一節(jié)揭曉。

隧道代理

第二種 Web 代理的原理也很簡單:

HTTP 客戶端通過 CONNECT 方法請求隧道代理創(chuàng)建一條到達(dá)任意目的服務(wù)器和端口的 TCP 連接,并對客戶端和服務(wù)器之間的后繼數(shù)據(jù)進(jìn)行盲轉(zhuǎn)發(fā)。

下面這張圖片同樣來自于《HTTP 權(quán)威指南》,直觀地展示了上述行為:

 [[157910]]

假如我通過代理訪問 A 網(wǎng)站,瀏覽器首先通過 CONNECT 請求,讓代理創(chuàng)建一條到 A 網(wǎng)站的 TCP 連接;一旦 TCP 連接建好,代理無腦轉(zhuǎn)發(fā)后續(xù)流量即可。所以這種代理,理論上適用于任意基于 TCP 的應(yīng)用層協(xié)議,HTTPS 網(wǎng)站使用的 TLS 協(xié)議當(dāng)然也可以。這也是這種代理為什么被稱為隧道的原因。對于 HTTPS 來說,客戶端透過代理直接跟服務(wù)端進(jìn)行 TLS 握手協(xié)商密鑰,所以依然是安全的,下圖中的抓包信息顯示了這種場景:

 [[157911]]

可以看到,瀏覽器與代理進(jìn)行 TCP 握手之后,發(fā)起了 CONNECT 請求,報(bào)文起始行如下:

  1. CONNECT imququ.com:443 HTTP/1.1 

對于 CONNECT 請求來說,只是用來讓代理創(chuàng)建 TCP 連接,所以只需要提供服務(wù)器域名及端口即可,并不需要具體的資源路徑。代理收到這樣的請求后,需要與服務(wù)端建立 TCP 連接,并響應(yīng)給瀏覽器這樣一個(gè) HTTP 報(bào)文:

  1. HTTP/1.1 200 Connection Established 

瀏覽器收到了這個(gè)響應(yīng)報(bào)文,就可以認(rèn)為到服務(wù)端的 TCP 連接已經(jīng)打通,后續(xù)直接往這個(gè) TCP 連接寫協(xié)議數(shù)據(jù)即可。通過 Wireshark 的 Follow TCP Steam 功能,可以清楚地看到瀏覽器和代理之間的數(shù)據(jù)傳遞:

 [[157912]]

可以看到,瀏覽器建立到服務(wù)端 TCP 連接產(chǎn)生的 HTTP 往返,完全是明文,這也是為什么 CONNECT 請求只需要提供域名和端口:如果發(fā)送了完整 URL、Cookie 等信息,會被中間人一覽無余,降低了 HTTPS 的安全性。HTTP 代理承載的 HTTPS 流量,應(yīng)用數(shù)據(jù)要等到 TLS 握手成功之后通過 Application Data 協(xié)議傳輸,中間節(jié)點(diǎn)無法得知用于流量加密的 master-secret,無法解密數(shù)據(jù)。而 CONNECT 暴露的域名和端口,對于普通的 HTTPS 請求來說,中間人一樣可以拿到(IP 和端口很容易拿到,請求的域名可以通過 DNS Query 或者 TLS Client Hello 中的 Server Name Indication 拿到),所以這種方式并沒有增加不安全性。

#p#

了解完原理后,再用 Node.js 實(shí)現(xiàn)一個(gè)支持 CONNECT 的代理也很簡單。核心代碼如下:

  1. JSvar http = require('http'); 
  2.  
  3. var net = require('net'); 
  4.  
  5. var url = require('url'); 
  6.  
  7. function connect(cReq, cSock) { 
  8.  
  9. var u = url.parse('http://' + cReq.url); 
  10.  
  11. var pSock = net.connect(u.port, u.hostname, function() { 
  12.  
  13. cSock.write('HTTP/1.1 200 Connection Established\r\n\r\n'); 
  14.  
  15. pSock.pipe(cSock); 
  16.  
  17. }).on('error'function(e) { 
  18.  
  19. cSock.end(); 
  20.  
  21. }); 
  22.  
  23. cSock.pipe(pSock); 
  24.  
  25.  
  26. http.createServer().on('connect', connect).listen(8888, '0.0.0.0'); 

以上代碼運(yùn)行后,會在本地 8888 端口開啟 HTTP 代理服務(wù),這個(gè)服務(wù)從 CONNECT 請求報(bào)文中解析出域名和端口,創(chuàng)建到服務(wù)端的 TCP 連接,并和 CONNECT 請求中的 TCP 連接串起來,***再響應(yīng)一個(gè) Connection Established 響應(yīng)。修改瀏覽器的 HTTP 代理為 127.0.0.1:8888 后再訪問 HTTPS 網(wǎng)站,代理可以正常工作。

***,將兩種代理的實(shí)現(xiàn)代碼合二為一,就可以得到全功能的 Proxy 程序了,全部代碼在 50 行以內(nèi)(當(dāng)然異常什么的基本沒考慮,這是我博客代碼的一貫風(fēng)格):

  1. JSvar http = require('http'); 
  2.  
  3. var net = require('net'); 
  4.  
  5. var url = require('url'); 
  6.  
  7. function request(cReq, cRes) { 
  8.  
  9. var u = url.parse(cReq.url); 
  10.  
  11. var options = { 
  12.  
  13. hostname : u.hostname, 
  14.  
  15. port : u.port || 80, 
  16.  
  17. path : u.path, 
  18.  
  19. method : cReq.method, 
  20.  
  21. headers : cReq.headers 
  22.  
  23. }; 
  24.  
  25. var pReq = http.request(options, function(pRes) { 
  26.  
  27. cRes.writeHead(pRes.statusCode, pRes.headers); 
  28.  
  29. pRes.pipe(cRes); 
  30.  
  31. }).on('error'function(e) { 
  32.  
  33. cRes.end(); 
  34.  
  35. }); 
  36.  
  37. cReq.pipe(pReq); 
  38.  
  39.  
  40. function connect(cReq, cSock) { 
  41.  
  42. var u = url.parse('http://' + cReq.url); 
  43.  
  44. var pSock = net.connect(u.port, u.hostname, function() { 
  45.  
  46. cSock.write('HTTP/1.1 200 Connection Established\r\n\r\n'); 
  47.  
  48. pSock.pipe(cSock); 
  49.  
  50. }).on('error'function(e) { 
  51.  
  52. cSock.end(); 
  53.  
  54. }); 
  55.  
  56. cSock.pipe(pSock); 
  57.  
  58.  
  59. http.createServer() 
  60.  
  61. .on('request', request) 
  62.  
  63. .on('connect', connect) 
  64.  
  65. .listen(8888, '0.0.0.0'); 

需要注意的是,大部分瀏覽器顯式配置了代理之后,只會讓 HTTPS 網(wǎng)站走隧道代理,這是因?yàn)榻⑺淼佬枰馁M(fèi)一次往返,能不用就盡量不用。但這并不代表 HTTP 請求不能走隧道代理,我們用 Node.js 寫段程序驗(yàn)證下(先運(yùn)行前面的代理服務(wù)):

  1. JSvar http = require('http'); 
  2.  
  3. var options = { 
  4.  
  5. hostname : '127.0.0.1'
  6.  
  7. port : 8888, 
  8.  
  9. path : 'imququ.com:80'
  10.  
  11. method : 'CONNECT' 
  12.  
  13. }; 
  14.  
  15. var req = http.request(options); 
  16.  
  17. req.on('connect'function(res, socket) { 
  18.  
  19. socket.write('GET / HTTP/1.1\r\n' + 
  20.  
  21. 'Host: imququ.com\r\n' + 
  22.  
  23. 'Connection: Close\r\n' + 
  24.  
  25. '\r\n'); 
  26.  
  27. socket.on('data'function(chunk) { 
  28.  
  29. console.log(chunk.toString()); 
  30.  
  31. }); 
  32.  
  33. socket.on('end'function() { 
  34.  
  35. console.log('socket end.'); 
  36.  
  37. }); 
  38.  
  39. }); 
  40.  
  41. req.end(); 

這段代碼運(yùn)行完,結(jié)果如下:

  1. HTTP/1.1 301 Moved Permanently 
  2. Server: nginx 
  3. Date: Thu, 19 Nov 2015 15:57:47 GMT 
  4. Content-Type: text/html 
  5. Content-Length: 178 
  6. Connection: close 
  7. Location: https://imququ.com/ 
  8.  
  9. <html> 
  10. <head><title>301 Moved Permanently</title></head> 
  11. <body bgcolor="white"
  12. <center><h1>301 Moved Permanently</h1></center> 
  13. <hr><center>nginx</center> 
  14. </body> 
  15. </html> 
  16.  
  17. socket end. 

可以看到,通過 CONNECT 讓代理打開到目標(biāo)服務(wù)器的 TCP 連接,用來承載 HTTP 流量也是完全沒問題的。

***,HTTP 的認(rèn)證機(jī)制可以跟代理配合使用,使得必須輸入正確的用戶名和密碼才能使用代理,這部分內(nèi)容比較簡單,這里略過。在本文第二部分,我打算談?wù)勅绾伟呀裉鞂?shí)現(xiàn)的代理改造為 HTTPS 代理,也就是如何讓瀏覽器與代理之間的流量走 HTTPS 安全機(jī)制。

責(zé)任編輯:何妍 來源: Jerry Qu的小站
相關(guān)推薦

2015-12-02 15:29:32

HTTP網(wǎng)絡(luò)協(xié)議代理原理

2021-07-20 10:30:46

Golanghttp語言

2024-04-26 09:04:13

2020-07-10 09:04:55

HTTPS瀏覽器網(wǎng)絡(luò)協(xié)議

2014-10-22 09:36:41

TCPIP

2024-01-08 08:36:29

HTTPGo代理服務(wù)器

2020-05-15 08:10:14

HTTP3應(yīng)用協(xié)議

2015-03-17 09:44:08

2009-06-04 10:41:52

Struts工作原理

2021-12-14 09:00:42

Swift HTTP 代理服務(wù)器

2018-11-30 09:03:55

HTTP緩存Web

2013-07-09 14:36:24

2022-03-17 08:55:43

本地線程變量共享全局變量

2021-06-10 08:29:15

Rollup工具前端

2025-05-27 01:00:00

2023-12-18 09:39:13

PreactHooks狀態(tài)管理

2012-02-08 10:37:42

Java反射

2009-11-23 11:55:43

Linux雙網(wǎng)卡IP原理

2024-09-12 10:06:21

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 精品免费国产 | 91久久久精品国产一区二区蜜臀 | 国产女人精品视频 | 蜜桃特黄a∨片免费观看 | 一区二区高清 | 精品国产乱码久久久久久丨区2区 | 超碰免费在线 | 精产国产伦理一二三区 | 欧美一区二区在线观看 | 亚洲精品色 | www.4hu影院 | 国产精品伦一区二区三级视频 | 日韩欧美国产精品一区二区 | 视频一区中文字幕 | 亚洲精品在线看 | 成人精品一区二区 | 亚洲日日夜夜 | 国产精品免费一区二区三区四区 | 五月槐花香 | 黄色片在线观看网址 | 欧美福利久久 | 国内精品久久久久久久影视简单 | 国产欧美日韩一区 | 日韩精品一区二区三区在线观看 | 国产97视频在线观看 | 国产精品a久久久久 | 91视频精选 | 羞羞视频在线观看免费观看 | 免费视频一区二区三区在线观看 | 一级中国毛片 | 免费一级欧美在线观看视频 | www.久久.com| 一级片av| 国产欧美在线 | 中文字幕一级 | 黄色a视频 | 欧美日高清 | 国产精品久久久久久久久久久久久 | 99reav | 91久久精品国产免费一区 | 中文字幕欧美日韩一区 |