在平常的開發中,常常使用 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';
}
複製代碼