前端开发时,经常需要用代理工具Charles或者Fiddler来抓包,或修改接口或替换文件或查看提交数据等,都是非常方便。尤其是做移动端开发时,是不是经常得配置host,或者查看页面请求的数据是否正确,相信我,此时使用Charles的远程代理是一件很幸福的事情。

当我们打开浏览器请求一个页面时候,为什么Charles就能捕获到请求?然而如果chrome使用了SwitchyOmega(配合shadowsocks,翻墙神器)或者类似的代理插件,此时Charles时捕获不到我们发的请求。

一开始我也很困惑,直到有一次Charles崩溃了,使用装有SwitchyOmega插件的chrome浏览器可以正常联网,而其他浏览器却不行了。在查看网络设置(网络->代理)中,一不小发现:

代理设置

我明明没有设置过这个东西啊,看着8888这个端口号好熟悉啊,额,这个不就是Charles代理设置里面默认的端口号嘛。于是好像突然明白了,Charles是通过这个来监听网页请求,然后抓包,然后就可以对请求进行修改请求参数,断点调试等。

然后再次正常打开Charles,然后正常关闭,果然网页代理设置没有,非chrome浏览器也可以正常联网了。如果打开Charles,手动把代理设置取消,Charles也捕获不了请求了。

既然Charles可以通过设置网络代理来捕获请求,那是否可以自己写一个服务器也实现同样的功能呢?

首先创建一个简单的node服务器:

1
2
3
4
5
6
7
8
9
const http = require('http');
http.createServer((req, res) => {
console.log(req.url);
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('it work');
}).listen(1338, '127.0.0.1', () => {
console.log('server start at 127.0.0.1:1338');
});

然后启动服务器,顺便设置网络的网页代理为服务器的地址127.0.0.1:1338;接下来访问一个http地址(csdn),因为设置网页代理,当然也可以设置安全网页代理。页面如期待的那样出现了it work

代理服务器已经搭建好,但怎么像Charles一样能正常返回请求的页面?

万事开头难,开头了,其实后面确实也就简单了很多。有疑问,但知道具体想做什么,百度一下,不就什么都知道了嘛。参考HTTP 代理原理及实现(一)修改一下源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const http = require('http');
const url = require('url');
http.createServer((req, res) => {
console.log(req.url);
const u = url.parse(req.url);
var options = {
hostname : u.hostname,
port : u.port || 80,
path : u.path,
method : req.method,
headers : req.headers
};
var pReq = http.request(options, function(pRes) {
res.writeHead(pRes.statusCode, pRes.headers);
pRes.pipe(res);
}).on('error', function(e) {
res.end();
});
req.pipe(pReq);
}).listen(1338, '127.0.0.1', () => {
console.log('server start at 127.0.0.1:1338');
});

顺利完成http代理,不信你再访问csdn试试,没有出现页面就是csdn挂了。至于https的可以参考HTTP 代理原理及实现(一)里面有详细的介绍Http代理相关的知识。

嗯嗯,http代理完成了,但是还有个问题啊,为什么上面通过解析req.url就能获取请求的目标地址,而自己想写一个脚本访问CSDN:

1
2
3
4
5
6
7
8
9
10
var http = require("http");
var options = {
host: "localhost",
port: 1338,
method: 'GET'
};
http.request(options, function(res) {
res.pipe(process.stdout);
}).end();

遍观nodejs官网http.request的可选参数, 都没有提到如何设置。不知道又想不明白,能咋办,上网找呗。

在stackoverflow上找到了一个解决方案,传送–>How can I use an http proxy with node.js http.Client给的方案是设置path:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var http = require("http");
var options = {
host: "localhost",
port: 1338,
method: 'GET',
path: "http://www.csdn.net",
headers: {
Host: "www.csdn.net"
}
};
http.request(options, function(res) {
res.pipe(process.stdout);
}).end();

仔细想像好像真的是这么回事啊,如果不设置path,服务器默端的req.url就是个/,这个不就是默认的path的值嘛。至于设置headers里面的Host, 如果不设置会默认为localhost:1338,也就是请求的服务器的host,这样获得的是一个403页面,如下:

1
2
3
4
5
6
7
<html>
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx</center>
</body>
</html>

参考:

  1. HTTP 代理原理及实现(一)
  2. How can I use an http proxy with node.js http.Client
Contents