<br/>
<br/><strong>HTTP協議</strong>
<br/>
<br/>HTTP協議簡單說來就是瀏覽器把壹串字符串發送到目標服務器,然後把目標服務器返回回來的壹串字符串顯示給用戶。
<br/>
<br/>瀏覽器發送的這串字符主要分為兩個部分,壹部分是頭,裏面包含目標服務器域名,當前請求的文件路徑等信息。另壹部分是正文,壹般的GET請求沒有正文。
<br/>
<br/>服務器返回來的字符串也分為頭和正文。
<br/>
<br/><strong>HTTP代理原理</strong>
<br/>
<br/>HTTP代理需要做的事情就是接收瀏覽器發來的請求字符串,再從請求字符串的頭部分找出瀏覽器請求的目標主機,然後直接把這串請求字符串發給目標主機,再把目標主機返回的數據發給瀏覽器。 “什麽?就這麽簡單?” “呃。。是啊,但這還沒完。。”
<br/>
<br/>現代瀏覽器壹般都是默認采用HTTP/1.1版本,並且默認會發送Connection: keep-alive請求。 這些信息是寫在請求的頭部的,意思是通知目標服務器采用keep-alive技術繼續處理後續的請求。 但是我們做的代理程序要想支持keep-alive是比較麻煩的。所以幹脆就把這個篡改成Connection: close。 這樣就可以保證瀏覽器請求的每個文件都會單獨發送壹個HTTP請求。
<br/>
<br/><strong>下面是NodeJS代碼實現</strong>
<br/><pre escaped=“true” lang=“javascript”>var net = require(‘net’);
<br/>var local_port = 8893;
<br/>
<br/>//在本地創建壹個server監聽本地local_port端口
<br/>net.createServer(function (client)
<br/>{
<br/>
<br/> //首先監聽瀏覽器的數據發送事件,直到收到的數據包含完整的http請求頭
<br/> var buffer = new Buffer(0);
<br/> client.on(‘data’,function(data)
<br/> {
<br/> buffer = buffer_add(buffer,data);
<br/> if (buffer_find_body(buffer) == -1) return;
<br/> var req = parse_request(buffer);
<br/> if (req === false) return;
<br/> client.removeAllListeners(‘data’);
<br/> relay_connection(req);
<br/> });
<br/>
<br/> //從http請求頭部取得請求信息後,繼續監聽瀏覽器發送數據,同時連接目標服務器,並把目標服務器的數據傳給瀏覽器代理的出現是因為瀏覽器同源策略的存在
服務端實現代理的例子和方法很多 比如nginx 反向代理解決生產環境的跨域問題
再有http-server等壹些第三方的包幫我處理 基本達到了開箱即用的體驗
通常我們所說的代理來源於http1.1的定義,代理扮演的是「中間人」角色,對於連接到它的客戶端來說,它是服務端;對於要連接的服務端來說,它是客戶端。它就負責在兩端之間來回傳送 HTTP 報文
假如我通過代理訪問A網站,對於A來說,它會把代理當做客戶端,完全察覺不到真正客戶端的存在,這實現了隱藏客戶端IP的目的。
但是他們到底是如何實現的 ,值得壹探究竟,下面是用原生nodejs 寫個以後個代理
const http = require("http");
const url = require("url");
//首先啟動本地服務器
http.createServer(function(req, res) {
//客戶端請求有兩種方式,可以是對象,也可以是url字符串
//1.這裏采取的是對象形式,包括url對象以及headers
var options = url.parse(req.url);
options.headers = req.headers;
//2.如果采取字符串形式,就傳入壹個完整的url字符串,node會自動解析成url對象
//通過客戶端請求新建壹個代理服務器
//代理請求仿照本地請求頭的數據
var proxyRequest = http.request(options, function(proxyResponse) { //代理請求獲取的數據再返回給本地res
proxyResponse.on('data', function(chunk) {
console.log('proxyResponse length:', chunk.length);
res.write(chunk, 'binary');
});
//當代理請求不再收到新的數據,告知本地res數據寫入完畢。
proxyResponse.on('end', function() {
console.log('proxied request ended');
res.end();
});
res.writeHead(proxyResponse.statusCode, proxyResponse.headers);
});
//data只有當請求體數據進來時才會觸發
//盡管沒有請求體數據進來,data還是要寫,否則不會觸發end事件
req.on('data', function(chunk) {
console.log('in request length:', chunk.length);
proxyRequest.write(chunk, 'binary');
});
req.on('end', function() {
//向proxy發送求情,這裏end方法必須被調用才能發起代理請求
//所有的客戶端請求都需要通過end來發起
proxyRequest.end();
});
}).listen(8080);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
以上代碼的核心思想就是用http.request充當了中間人的角色 幫我們去目標地址取數據然後在把得到的數據傳回去。可以看作是設計模式中代理模式的壹種實踐