UNPKG

express-gateway

Version:

A microservices API gateway built on top of ExpressJS

112 lines (92 loc) 3.69 kB
const httpProxy = require('http-proxy'); const fs = require('fs'); const ProxyAgent = require('proxy-agent'); const logger = require('../../logger').gateway; const strategies = require('./strategies'); const createStrategy = (strategy, proxyOptions, endpointUrls) => { const Strategy = strategies[strategy]; return new Strategy(proxyOptions, endpointUrls); }; module.exports = function (params, config) { const serviceEndpointKey = params.serviceEndpoint; const endpoint = config.gatewayConfig.serviceEndpoints[serviceEndpointKey]; const proxyOptions = parseProxyOptions(params.proxyOptions, endpoint); if (!endpoint) { throw new Error( `service endpoint ${serviceEndpointKey} (referenced in 'proxy' ` + 'policy configuration) does not exist'); } if (!endpoint.url && !endpoint.urls) { throw new Error( `service endpoint ${serviceEndpointKey} (referenced in 'proxy' ` + 'policy configuration) does not contain a `url` or `urls` property'); } const proxy = httpProxy.createProxyServer({ changeOrigin: params.changeOrigin !== false }); proxy.on('error', (err, _req, res) => { logger.warn(err); if (!res) { throw err; } if (!res.headersSent) { res.status(502).send('Bad gateway.'); } else { res.end(); } }); let balancer; if (endpoint.url) { // single `url` property takes precedence over `urls` array balancer = createStrategy('static', proxyOptions, [endpoint.url]); } else { const strategy = params.strategy || 'round-robin'; balancer = createStrategy(strategy, proxyOptions, endpoint.urls); } return function proxyHandler (req, res) { const targetUrl = balancer.nextTarget(); proxyOptions.target = Object.assign(proxyOptions.target, targetUrl); logger.debug(`proxying to ${targetUrl}, ${req.method} ${req.url}`); const intermediateProxySettings = process.env.http_proxy || params.proxyUrl; if (intermediateProxySettings) { logger.debug(`using intermediate proxy ${intermediateProxySettings}`); proxyOptions.agent = new ProxyAgent(intermediateProxySettings); } prepareHeaders(params, proxyOptions, req.egContext); proxy.web(req, res, proxyOptions); }; // multiple urls will load balance, defaulting to round-robin }; function prepareHeaders (params, proxyOptions, egContext) { proxyOptions.headers = proxyOptions.headers || {}; // Default headers always sent to downstream // TODO: allow configuration if (egContext.requestId) { proxyOptions.headers['eg-request-id'] = egContext.requestId; } proxyOptions.headers['eg-consumer-id'] = egContext.consumer && egContext.consumer.id ? egContext.consumer.id : 'anonymous'; } // Parse endpoint URL if single URL is provided. // Extend proxy options by allowing and parsing keyFile, certFile and caFile. function parseProxyOptions (proxyOptions = {}, endpoint = {}) { let parsedProxyOptions = {}; if (endpoint.proxyOptions) { parsedProxyOptions = Object.assign(parsedProxyOptions, endpoint.proxyOptions); } parsedProxyOptions = Object.assign(parsedProxyOptions, proxyOptions); if (parsedProxyOptions.target) { if (parsedProxyOptions.target.keyFile) { parsedProxyOptions.target.key = fs.readFileSync(parsedProxyOptions.target.keyFile); } if (parsedProxyOptions.target.certFile) { parsedProxyOptions.target.cert = fs.readFileSync(parsedProxyOptions.target.certFile); } if (parsedProxyOptions.target.caFile) { parsedProxyOptions.target.ca = fs.readFileSync(parsedProxyOptions.target.caFile); } } else { parsedProxyOptions.target = {}; } return parsedProxyOptions; }