UNPKG

@reldens/server-utils

Version:
158 lines (146 loc) 5.05 kB
/** * * Reldens - ReverseProxyConfigurer * */ const { createProxyMiddleware } = require('http-proxy-middleware'); const { ServerHeaders } = require('../server-headers'); const { ServerErrorHandler } = require('../server-error-handler'); const { EventDispatcher } = require('../event-dispatcher'); class ReverseProxyConfigurer { constructor() { this.isDevelopmentMode = false; this.useVirtualHosts = false; this.reverseProxyRules = []; this.reverseProxyOptions = { changeOrigin: true, ws: true, secure: false, logLevel: 'warn' }; this.serverHeaders = new ServerHeaders(); this.onError = null; this.onEvent = null; } setup(app, config) { this.isDevelopmentMode = config.isDevelopmentMode || false; this.useVirtualHosts = config.useVirtualHosts || false; this.reverseProxyRules = config.reverseProxyRules || []; this.reverseProxyOptions = config.reverseProxyOptions || this.reverseProxyOptions; this.onError = config.onError || null; this.onEvent = config.onEvent || null; if(0 === this.reverseProxyRules.length){ return; } this.applyRules(app); EventDispatcher.dispatch( this.onEvent, 'reverse-proxy-configured', 'reverseProxyConfigurer', this, {rulesCount: this.reverseProxyRules.length, useVirtualHosts: this.useVirtualHosts} ); } applyRules(app) { for(let rule of this.reverseProxyRules){ if(!this.validateProxyRule(rule)){ continue; } let proxyMiddleware = this.createProxyMiddleware(rule); if(!proxyMiddleware){ continue; } let pathPrefix = rule.pathPrefix || '/'; if(this.useVirtualHosts){ app.use(pathPrefix, (req, res, next) => { let hostname = this.extractHostname(req); if(hostname !== rule.hostname){ return next(); } return proxyMiddleware(req, res, next); }); continue; } app.use(pathPrefix, proxyMiddleware); } } extractHostname(req) { if(req.domain && req.domain.hostname){ return req.domain.hostname; } let host = req.get('host') || ''; return host.split(':')[0].toLowerCase(); } validateProxyRule(rule) { if(!rule){ return false; } if(!rule.hostname){ return false; } if(!rule.target){ return false; } return true; } createProxyMiddleware(rule) { let options = Object.assign({}, this.reverseProxyOptions); if('boolean' === typeof rule.changeOrigin){ options.changeOrigin = rule.changeOrigin; } if('boolean' === typeof rule.websocket){ options.ws = rule.websocket; } if('boolean' === typeof rule.secure){ options.secure = rule.secure; } if('string' === typeof rule.logLevel){ options.logLevel = rule.logLevel; } options.target = rule.target; options.onError = (err, req, res) => { this.handleProxyError(err, req, res); }; options.onProxyReq = (proxyReq, req) => { if(req.headers['x-forwarded-for']){ return; } let clientIp = req.ip || req.connection.remoteAddress || ''; proxyReq.setHeader(this.serverHeaders.proxyForwardedFor, clientIp); proxyReq.setHeader(this.serverHeaders.proxyForwardedProto, req.protocol); proxyReq.setHeader(this.serverHeaders.proxyForwardedHost, req.get('host') || ''); }; return createProxyMiddleware(options); } handleProxyError(err, req, res) { let hostname = req.get('host') || 'unknown'; let requestPath = req.path || req.url || 'unknown'; ServerErrorHandler.handleError( this.onError, 'reverseProxyConfigurer', this, 'proxy-error', err, {request: req, response: res, hostname: hostname, path: requestPath} ); if(res.headersSent){ return; } if('ECONNREFUSED' === err.code){ return res.status(502).send('Bad Gateway - Backend server unavailable'); } if('ETIMEDOUT' === err.code || 'ESOCKETTIMEDOUT' === err.code){ return res.status(504).send('Gateway Timeout'); } return res.status(500).send('Proxy Error'); } } module.exports.ReverseProxyConfigurer = ReverseProxyConfigurer;