内容简介:本来源代码版本之前我有一篇很多时候导致这种的原因就是不了解我们使用的东西的本质.
本来源代码版本
- http-proxy: 1.17.0
- http-proxy-middleware 0.19.0
之前我有一篇 Koa下http代理 , 主要是说在开发的时候遇到的一个坑和排查过程. 很多时间结果很简单, 过程却是异常的痛苦.
很多时候导致这种的原因就是不了解我们使用的东西的本质.
所以这一篇就是简单的分析nodejs下的http代理, 当然不会太深入, 因为能力不足.
在express下大家最熟悉的是 http-proxy-middleware
http-proxy-middleware其是基于 http-proxy 封装的.
http-proxy 本身又是基于 http 和 https 封装的
HTTPS 是 HTTP 基于 TLS/SSL 的版本
http里面有个方法叫 http.request , http-proxy主要就是基于这个实现的代理.
http与net模块还有一定关系, net核心方法和类关系如下
net.Server:这个类用于创建 TCP 或 IPC server
net.Socket: 使用它来与server或者客户端通信
举个例子,它是通过net.createConnection()返回的,所以用户可以使用它来与server通信。
当一个连接被接收时,它也能被Node.js创建并传递给用户。比如,它是通过监听在一个net.Server上的’connection’事件触发而获得的,那么用户可以使用它来与客户端通信。
回归正轨
http-proxy-middleware
http-proxy-middleware lib文件下index.js文件
var proxy = httpProxy.createProxyServer({})
logger.info('[HPM] Proxy created:', config.context, ' -> ', proxyOptions.target)
var pathRewriter = PathRewriter.create(proxyOptions.pathRewrite) // returns undefined when "pathRewrite" is not provided
// attach handler to http-proxy events
handlers.init(proxy, proxyOptions)
// log errors for debug purpose
proxy.on('error', logError)
// https://github.com/chimurai/http-proxy-middleware/issues/19
// expose function to upgrade externally
middleware.upgrade = wsUpgradeDebounced
return middleware
function middleware(req, res, next) {
if (shouldProxy(config.context, req)) {
var activeProxyOptions = prepareProxyRequest(req)
proxy.web(req, res, activeProxyOptions)
} else {
next()
}
if (proxyOptions.ws === true) {
// use initial request to access the server object to subscribe to http upgrade event
catchUpgradeRequest(req.connection.server)
}
}
httpProxy就是http-proxy, 上面大致就是创建了一个proxy, 各种预处理.
最后返回一个中间件.
中间件, 就是检查需不需要代理, 需要的话就进行代理.
proxy.web(req, res, activeProxyOptions)这句就是进行请求, 然后被代理
http-proxy
createProxyServer方法就是http-proxy下面的ProxyServer方法,
ProxyServer是继承eventemitter3的
function ProxyServer(options) {
EE3.call(this);
options = options || {};
options.prependPath = options.prependPath === false ? false : true;
this.web = this.proxyRequest = createRightProxy('web')(options);
this.ws = this.proxyWebsocketRequest = createRightProxy('ws')(options);
this.options = options;
this.webPasses = Object.keys(web).map(function (pass) {
return web[pass];
});
this.wsPasses = Object.keys(ws).map(function (pass) {
return ws[pass];
});
this.on('error', this.onError, this);
}
我们看到了this.web, 我们再追溯createRightProxy, 三层嵌套的函数,执行两次后, 返回的就是function(req, res,…)格式的方法, 刚好对应到http-proxy-middleware 里面的proxy.web的参数.
注意passes这个值,http的话对应上面代码的11行, 是 web = require(‘./passes/web-incoming’)
function createRightProxy(type) {
return function (options) {
return function (req, res /*, [head], [opts] */) {
var passes = (type === 'ws') ? this.wsPasses : this.webPasses,
args = [].slice.call(arguments),
cntr = args.length - 1,
head, cbl;
/* optional args parse begin */
if (typeof args[cntr] === 'function') {
cbl = args[cntr];
cntr--;
}
var requestOptions = options;
if (
!(args[cntr] instanceof Buffer) &&
args[cntr] !== res
) {
//Copy global options
requestOptions = extend({}, options);
//Overwrite with request options
extend(requestOptions, args[cntr]);
cntr--;
}
if (args[cntr] instanceof Buffer) {
head = args[cntr];
}
/* optional args parse end */
['target', 'forward'].forEach(function (e) {
if (typeof requestOptions[e] === 'string')
requestOptions[e] = parse_url(requestOptions[e]);
});
if (!requestOptions.target && !requestOptions.forward) {
return this.emit('error', new Error('Must provide a proper URL as target'));
}
for (var i = 0; i < passes.length; i++) {
/**
* Call of passes functions
* pass(req, res, options, head)
*
* In WebSockets case the `res` variable
* refer to the connection socket
* pass(req, socket, options, head)
*/
if (passes[i](req, res, requestOptions, head, this, cbl)) { // passes can return a truthy value to halt the loop
break;
}
}
};
};
}
上面的核心就是遍历passes, 这个passes又是什么呢,
是 web = require(‘./passes/web-incoming’) , 我们再进入看一看
- deleteLength: function deleteLength(req, res, options)
- timeout: function timeout(req, res, options)
- XHeaders: function XHeaders(req, res, options)
- stream: function stream(req, res, options, _, server, clb)
最终发送请求的就是这个stream, passes就是对应这几个东西.
我们再进入steam 方法, 直接看代码27行,实际就是根据protocold调用http或者https的request方法.
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);
}
});
}
这里简单分析的是http(htttps)的代理走向, socket就靠你们了, 没电了,拜.
以上所述就是小编给大家介绍的《浅浅的说http-proxy-middleware》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
数据结构与算法:Python语言描述
裘宗燕 / 机械工业出版社 / 2016-1 / CNY 45.00
本书基于Python语言介绍了数据结构与算法的基本知识,主要内容包括抽象数据类型和Python面向对象程序设计、线性表、字符串、栈和队列、二叉树和树、集合、排序以及算法的基本知识。本书延续问题求解的思路,从解决问题的目标来组织教学内容,注重理论与实践的并用。一起来看看 《数据结构与算法:Python语言描述》 这本书的介绍吧!