在平常的开发中,常常使用 Nginx 做为反向代理服务器进行 http 请求代理。既然 Nginx 能够进行请求反向代理 NodeJs 确定也是能够的。开源社区上,比较出名的项目就是 node-http-proxy 了,尝试对该项目进行一次源码解读,藉此机会了解一下 NodeJs 代理过程当中的一些细节javascript
1.17.0java
http-proxy:node
定义关键方法 createRightProxy,该方法功能以下:web
声明 ProxyServer 类,ProxyServer 类继承 eventemitter3 的功能,并自定义方法:正则表达式
constructor:初始化配置,经过 createRightProxy 方法 分别为 web 和 ws 两种类型请求初始化处理器(pass),默认的处理器由 web-incoming,ws-incoming 两个文件提供;服务器
onError:当事件监听器获取错误事件(error)时,向外抛出错误;websocket
listen:经过原生模块 http 和 https 开启 server 监听特定端口,当接收的请求方法名为 upgrade 时,转为 websocket 处理请求;并发
close:关闭 server;socket
before:针对 处理器(pass)添加处理器运行前的回调函数函数
after:针对 处理器(pass)添加处理器运行后的回调函数
关键流程:
web-incoming、web-outgoing:
web-incoming 关键方法为 stream,功能概述为 获取原始请求、响应对象 req 和 res 后,建立对应的 proxyReq 对象,经过 proxyReq 对象转发请求达到代理的效果,并在 proxyReq 的请求响应 proxyRes 返回时,经过 web-outgoing 封装方法更新 res 的 header,最后将 proxyRes 的结果内容经过 pipe 方法更新到 res 上
关键流程以下:
请求响应关键流程:
关键代码:
proxyReq.on('response', function(proxyRes) {
if(!res.headersSent && !options.selfHandleResponse) {
// 复制 res header
}
if (!res.finished) {
proxyRes.pipe(res);
}
});
复制代码
ws-incoming:
该文件主要负责对 web-socket 进行代理
关键方法为 stream,经过 req 建立 proxyReq,proxyReq 向目标服务发出 http 请求(GET),等待 upgrade 响应,获得 upgrade 响应后,便可得到代理套接字 proxySocket,原套接字写回转换协议响应给客户端,最后经过 pipe 方法将 socket 和 proxySocket 的内容绑定起来
关键代码:
socket.write(createHttpHeader('HTTP/1.1 101 Switching Protocols', proxyRes.headers));
复制代码
proxySocket.pipe(socket).pipe(proxySocket);
复制代码
checkMethodAndHeader
checkMethodAndHeader : function checkMethodAndHeader(req, socket) {
if (req.method !== 'GET' || !req.headers.upgrade) {
socket.destroy();
return true;
}
if (req.headers.upgrade.toLowerCase() !== 'websocket') {
socket.destroy();
return true;
}
}
复制代码
XHeaders
XHeaders: function XHeaders(req, res, options) {
if(!options.xfwd) return;
var encrypted = req.isSpdy || common.hasEncryptedConnection(req);
var values = {
for : req.connection.remoteAddress || req.socket.remoteAddress,
port : common.getPort(req),
proto: encrypted ? 'https' : 'http'
};
['for', 'port', 'proto'].forEach(function(header) {
req.headers['x-forwarded-' + header] =
(req.headers['x-forwarded-' + header] || '') +
(req.headers['x-forwarded-' + header] ? ',' : '') +
values[header];
});
req.headers['x-forwarded-host'] = req.headers['x-forwarded-host'] || req.headers['host'] || '';
}
复制代码
req.connection.encrypted
req.connection.pair
req.isSpdy
复制代码
上述三个条件任一成立,判断为 https 协议
createHttpHeaders
var createHttpHeader = function(line, headers) {
return Object.keys(headers).reduce(function (head, key) {
var value = headers[key];
if (!Array.isArray(value)) {
head.push(key + ': ' + value);
return head;
}
for (var i = 0; i < value.length; i++) {
head.push(key + ': ' + value[i]);
}
return head;
}, [line])
.join('\r\n') + '\r\n\r\n';
}
复制代码