浅浅的说http-proxy-middleware

栏目: 后端 · 前端 · 发布时间: 7年前

内容简介:本来源代码版本之前我有一篇很多时候导致这种的原因就是不了解我们使用的东西的本质.

本来源代码版本

  • 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 本身又是基于 httphttps 封装的

HTTPS 是 HTTP 基于 TLS/SSL 的版本

http里面有个方法叫 http.request , http-proxy主要就是基于这个实现的代理.

http与net模块还有一定关系, net核心方法和类关系如下

浅浅的说http-proxy-middleware

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》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Cyberwar

Cyberwar

Kathleen Hall Jamieson / Oxford University Press / 2018-10-3 / USD 16.96

The question of how Donald Trump won the 2016 election looms over his presidency. In particular, were the 78,000 voters who gave him an Electoral College victory affected by the Russian trolls and hac......一起来看看 《Cyberwar》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换