现在使用的代理大部分为HTTP和Socket代理。 Socket代理更底层,需要本地解析域名,而HTTP代理则是基于HTTP协议之上的,不需要本地解析域名。下面我讲讲HTTP(S)代理的设计思路以及NodeJS代码实现。<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);1234567891011121314151617181920212223242526272829303132333435363738394012345678910111213141516171819202122232425262728293031323334353637383940以上代码的核心思想就是用http.request充当了中间人的角色 帮我们去目标地址取数据然后在把得到的数据传回去。可以看作是设计模式中代理模式的一种实践