内容简介:我们上线一个网站,往往需要代理来完成需求,不然的话就只能使用最简单的做法是拿已经写好的包直接使用上面:point_up_2:代码监听
我们上线一个网站,往往需要代理来完成需求,不然的话就只能使用 IP
加端口的方式来访问应用,目前基本上都会使用 Nginx
来完成反向代理,那么我们开发 Node
应用一定需要 Nginx
吗?肯定不是,我们完全可以通过 Node
来做,但是大多是不建议你用 Node
去处理,因为 Nginx
功能更强大,比如负载均衡,本文主要是帮你了解如何通过 Node
实现代理
使用http-proxy
最简单的做法是拿已经写好的包直接使用
$ cnpm i http-proxy
const proxy = require("http-proxy").createProxyServer({});
server = require("http").createServer(function(req, res) {
const host = req.headers.host;
switch (host) {
case "your domain":
proxy.web(req, res, { target: "http://localhost:8000" });
break;
default:
res.writeHead(200, {
"Content-Type": "text/plain"
});
res.end("Welcome to my server!");
}
});
console.log("listening on port 80");
server.listen(80);
上面:point_up_2:代码监听 80
端口(如果你的服务器目前使用来 Nginx
,暂用 80
端口的话,需要先暂停 Nginx
服务),然后我们通过访问域名(前提是域名做好了解析),然后使用 proxy.web
方法反向代理到当前服务下的 8000
端口,到此一个简单的服务完成了
原生实现
const http = require("http");
const url = require("url");
function request(req, res) {
const reqUrl = url.parse(req.url);
const target = url.parse("http://localhost:3000");
const options = {
hostname: target.hostname,
port: target.port,
path: reqUrl.path,
method: req.method,
headers: req.headers
};
const proxyReq = http.request(options);
proxyReq.on("response", proxyRes => {
res.writeHead(proxyRes.statusCode, proxyRes.headers);
proxyRes.pipe(res);
});
req.pipe(proxyReq);
}
http
.createServer()
.on("request", request)
.listen(8003);
是不是很简单?通过访问 8003
端口,我们将请求转发到 3000
端口,可以复制当前代码尝试一下,前提是 3000
端口可以正常访问。当访问 8003
端口的时候,内部重新请求我们需要代理的地址,然后通过 pipe
返回转发后的数据
http-proxy源码实现原理
执行 proxy.web
function ProxyServer(options) {
...
this.web = this.proxyRequest = createRightProxy('web')(options);
...
}
内部关键代码执行了一下这段, passes
是一个数组方法,包含 deleteLength
、 timeout
、 XHeaders
、 stream
,关键点在 stream
,其他基本是辅助作用, XHeaders
功能是设置 x-forwarded-*
这种 header
,不过前提是 option
配置了 xfwd
才行, timeout
是设置超时时间的, deleteLength
只有请求方法是 OPTIONS
和 DELETE
才会执行
...
for(var i=0; i < passes.length; i++) {
if(passes[i](req, res, requestOptions, head, this, cbl)) { // passes can return a truthy value to halt the loop
break;
}
}
stream
方法
module.exports = {
deleteLength: ....,
timeout: ...,
XHeaders: ...,
stream: function stream(req, res, options, _, server, clb) {
// And we begin!
server.emit('start', req, res, options.target || options.forward);
var agents = options.followRedirects ? followRedirects : nativeAgents;
var http = agents.http;
var https = agents.https;
if(options.forward) {
// If forward enable, so just pipe the request
var forwardReq = (options.forward.protocol === 'https:' ? https : http).request(
common.setupOutgoing(options.ssl || {}, options, req, 'forward')
);
// error handler (e.g. ECONNRESET, ECONNREFUSED)
// Handle errors on incoming request as well as it makes sense to
var forwardError = createErrorHandler(forwardReq, options.forward);
req.on('error', forwardError);
forwardReq.on('error', forwardError);
(options.buffer || req).pipe(forwardReq);
if(!options.target) { return res.end(); }
}
// Request initalization
var proxyReq = (options.target.protocol === 'https:' ? https : http).request(
common.setupOutgoing(options.ssl || {}, options, req)
);
// Enable developers to modify the proxyReq before headers are sent
proxyReq.on('socket', function(socket) {
if(server) { server.emit('proxyReq', proxyReq, req, res, options); }
});
// allow outgoing socket to timeout so that we could
// show an error page at the initial request
if(options.proxyTimeout) {
proxyReq.setTimeout(options.proxyTimeout, function() {
proxyReq.abort();
});
}
// Ensure we abort proxy if request is aborted
req.on('aborted', function () {
proxyReq.abort();
});
// handle errors in proxy and incoming request, just like for forward proxy
var proxyError = createErrorHandler(proxyReq, options.target);
req.on('error', proxyError);
proxyReq.on('error', proxyError);
function createErrorHandler(proxyReq, url) {
return function proxyError(err) {
if (req.socket.destroyed && err.code === 'ECONNRESET') {
server.emit('econnreset', err, req, res, url);
return proxyReq.abort();
}
if (clb) {
clb(err, req, res, url);
} else {
server.emit('error', err, req, res, url);
}
}
}
(options.buffer || req).pipe(proxyReq);
proxyReq.on('response', function(proxyRes) {
if(server) { server.emit('proxyRes', proxyRes, req, res); }
if(!res.headersSent && !options.selfHandleResponse) {
for(var i=0; i < web_o.length; i++) {
if(web_o[i](req, res, proxyRes, options)) { break; }
}
}
if (!res.finished) {
// Allow us to listen when the proxy has completed
proxyRes.on('end', function () {
if (server) server.emit('end', req, res, proxyRes);
});
// We pipe to the response unless its expected to be handled by the user
if (!options.selfHandleResponse) proxyRes.pipe(res);
} else {
if (server) server.emit('end', req, res, proxyRes);
}
});
}
}
关键代码
// Request initalization
var proxyReq = (options.target.protocol === 'https:' ? https : http).request(
common.setupOutgoing(options.ssl || {}, options, req)
);
(options.buffer || req).pipe(proxyReq);
proxyReq.on('response', function(proxyRes) {
if(!res.headersSent && !options.selfHandleResponse) {
for(var i=0; i < web_o.length; i++) {
if(web_o[i](req, res, proxyRes, options)) { break; }
}
}
if (!res.finished) {
// We pipe to the response unless its expected to be handled by the user
if (!options.selfHandleResponse) proxyRes.pipe(res);
}
});
实现大致和我们之前写得差不多,但是他考虑得更多,支持 https
,错误处理也做得很好,已经很成熟了
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 动态代理三部曲(一):动态代理模式及实现原理
- 使用 Javassist 实现动态代理
- Javassist实现JDK动态代理
- 代理模式-go/java实现
- 中通安全访问代理设计与实现
- vue配置文件实现代理v2版本
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。