探究http-proxy-middleware源码

一、http-proxy-middleware 与 node-http-proxy

  • 配置http-proxy-middleware可以很容易地在connect, express, browser-sync实现http代理

  • http-proxy-middleware 是基于node-http-proxy实现的中间件

二、http-proxy-middleware简单用法

- /api 请求代理到http://www.example.org的例子
    var express = require('express');
    var proxy = require('http-proxy-middleware');
    
    var app = express();
    
    app.use('/api', proxy({target: 'http://www.example.org', changeOrigin: true}));
    app.listen(3000);

三、使用node-http-proxy进行http代理的代码对比

var express = require('express')
var httpProxy = require('http-proxy')


var options = {target: "http://jsonplaceholder.typicode.com/users",changeOrigin: true};
var httpProxyServer = httpProxy.createProxyServer(options);
function jsonHttpProxy(req, res) {
    httpProxyServer.web(req, res);
}
var app = express()
app.use('/users', jsonHttpProxy)
app.listen(3002)
  • 由上述代码可以看出,当要访问/users时,需要target中对应代理网站中也设置对应的/users路径

  • 而在http-proxy-middleware中有一个req.url的设置,可以根据当前访问路径自动映射代理网站相应路径

  • httpProxyServer.web()表示代理http或者https常规的代理连接,而想要代理websocket的连接则需要使用httpProxyServer.ws()方法

四、探究http-proxy-middleware源码处理过程

4.1. http-proxy-middleware源码目录

clipboard.png logger.js和errors.js分别用于输出日志和错误信息这里不作分析

4.2 http-proxy-middleware代理普通请求(http和https)

  • 在indexjs中能看到主入口文件,下面这段省略的源码能充分反映普通请求代理中http-proxy-middleware和node-http-proxy的关系

var httpProxy = require('http-proxy')

function HttpProxyMiddleware (context, opts) {
    ……
    return middleware
    
     var proxy = httpProxy.createProxyServer({})
     
     function middleware (req, res, next) {
      ……
      var activeProxyOptions = prepareProxyRequest(req)
      proxy.web(req, res, activeProxyOptions)
      ……
    
      }
}
  • 与三中的代码比较最主要的区别是对options做了处理,也就是activeProxyOptions = prepareProxyRequest(req),下面分析prepareProxyRequest函数做的处理

  • 以访问options = {target: "http://jsonplaceholder.typicode.com",changeOrigin: true}为例,访问http://localhost:3000/users

function prepareProxyRequest (req) {
    //这一步的处理非常关键,可以将请求路径对应上代理的路径
    //如果不设置这一步,那么代理的路径就是对应了target不会变化
    req.url = (req.originalUrl || req.url)

    // proxyOptions就是原始的options
    var originalPath = req.url
    var newProxyOptions = _.assign({}, proxyOptions)

    //这里是处理更多变得路径代理情况,下面详细介绍
    __applyRouter(req, newProxyOptions)
    __applyPathRewrite(req, pathRewriter)

    ……省略logger部分代码

    return newProxyOptions
  }
  • 从http-proxy-middleware文档中可以看到关于router和pathRewriter的举例用法,处理过程就是对应上面的__applyRouter和__applyRewriter方法

  • __applyRouter的处理在router.js中,主要处理过程是遍历options.router,然后和请求的host或者host+path比对是否相符,然后将相对应的值赋予target;如果router是函数则传入req值,执行函数得到返回值

  • __applyRewriter的处理在path-rewriter.js中,主要处理过程是遍历options.pathRewrite,用正则包装key,对应value,返回一个检测并改变req.url的函数,以此对应相应的代理网站的路径

    pathRewrite: {
        '^/api/old-path' : '/api/new-path',     // rewrite path
        '^/api/remove/path' : '/path'           // remove base path
        },
    router: {
        // when request.headers.host == 'dev.localhost:3000',
        // override target 'http://www.example.org' to 'http://localhost:8000'
        'dev.localhost:3000' : 'http://localhost:8000'
    }

  • config-factory.js用于处理传入的context和options参数

  • 例如:处理proxy(['/api', '/ajax', '/someotherpath'], {...})的context


  • context-matcher.js用于检测当proxy中传入了context参数时与请求路径的比对

  • 例如: proxy('/api', {...}) - 只能代理请求以/api开头的请求

4.3 http-proxy-middleware代理websocket请求

  • ws代理的核心处理代码

    // 这里的debounced起到了节流函数的作用,防止短时间内多次调用处理函数
    var wsUpgradeDebounced = _.debounce(handleUpgrade)
     if (proxyOptions.ws === true) {
      catchUpgradeRequest(req.connection.server)
    }
    
    //监听upgrade
    function catchUpgradeRequest (server) {
        if (!wsInitialized) {
          server.on('upgrade', wsUpgradeDebounced)
          wsInitialized = true
        }
      }
    
    //真正调用node-http-proxy的ws方法的处理函数
    function handleUpgrade (req, socket, head) {
        wsInitialized = true
    
        if (shouldProxy(config.context, req)) {
          var activeProxyOptions = prepareProxyRequest(req)
          proxy.ws(req, socket, head, activeProxyOptions)
        }
  }

五、总结与思考

  • 研究http-proxy-middleware源码的初衷是想要知道node是如何做到代理请求的,进行到一定步骤才发现http-proxy-middleware并没有做任何这方面的处理,只是封装了代理的步骤,让参数更灵活,所以下一步还是要探究node-http-proxy是如何对请求做处理的

  • 但是通过研究http-proxy-middleware不仅对用法更加熟悉,还大概了解了如何做一个express插件

  • 最后 http-proxy-middleware源码 https://github.com/chimurai/h...

阅读 8.3k

推荐阅读
前端之路
用户专栏

用自己的语言描述我认识的前端

33 人关注
24 篇文章
专栏主页