| 1 | "use strict";
|
|---|
| 2 | Object.defineProperty(exports, "__esModule", { value: true });
|
|---|
| 3 | exports.HttpProxyMiddleware = void 0;
|
|---|
| 4 | const httpProxy = require("http-proxy");
|
|---|
| 5 | const config_factory_1 = require("./config-factory");
|
|---|
| 6 | const contextMatcher = require("./context-matcher");
|
|---|
| 7 | const handlers = require("./_handlers");
|
|---|
| 8 | const logger_1 = require("./logger");
|
|---|
| 9 | const PathRewriter = require("./path-rewriter");
|
|---|
| 10 | const Router = require("./router");
|
|---|
| 11 | class HttpProxyMiddleware {
|
|---|
| 12 | constructor(context, opts) {
|
|---|
| 13 | this.logger = (0, logger_1.getInstance)();
|
|---|
| 14 | this.wsInternalSubscribed = false;
|
|---|
| 15 | this.serverOnCloseSubscribed = false;
|
|---|
| 16 | // https://github.com/Microsoft/TypeScript/wiki/'this'-in-TypeScript#red-flags-for-this
|
|---|
| 17 | this.middleware = async (req, res, next) => {
|
|---|
| 18 | var _a, _b;
|
|---|
| 19 | if (this.shouldProxy(this.config.context, req)) {
|
|---|
| 20 | try {
|
|---|
| 21 | const activeProxyOptions = await this.prepareProxyRequest(req);
|
|---|
| 22 | this.proxy.web(req, res, activeProxyOptions);
|
|---|
| 23 | }
|
|---|
| 24 | catch (err) {
|
|---|
| 25 | next(err);
|
|---|
| 26 | }
|
|---|
| 27 | }
|
|---|
| 28 | else {
|
|---|
| 29 | next();
|
|---|
| 30 | }
|
|---|
| 31 | /**
|
|---|
| 32 | * Get the server object to subscribe to server events;
|
|---|
| 33 | * 'upgrade' for websocket and 'close' for graceful shutdown
|
|---|
| 34 | *
|
|---|
| 35 | * NOTE:
|
|---|
| 36 | * req.socket: node >= 13
|
|---|
| 37 | * req.connection: node < 13 (Remove this when node 12/13 support is dropped)
|
|---|
| 38 | */
|
|---|
| 39 | const server = (_b = ((_a = req.socket) !== null && _a !== void 0 ? _a : req.connection)) === null || _b === void 0 ? void 0 : _b.server;
|
|---|
| 40 | if (server && !this.serverOnCloseSubscribed) {
|
|---|
| 41 | server.on('close', () => {
|
|---|
| 42 | this.logger.info('[HPM] server close signal received: closing proxy server');
|
|---|
| 43 | this.proxy.close();
|
|---|
| 44 | });
|
|---|
| 45 | this.serverOnCloseSubscribed = true;
|
|---|
| 46 | }
|
|---|
| 47 | if (this.proxyOptions.ws === true) {
|
|---|
| 48 | // use initial request to access the server object to subscribe to http upgrade event
|
|---|
| 49 | this.catchUpgradeRequest(server);
|
|---|
| 50 | }
|
|---|
| 51 | };
|
|---|
| 52 | this.catchUpgradeRequest = (server) => {
|
|---|
| 53 | if (!this.wsInternalSubscribed) {
|
|---|
| 54 | server.on('upgrade', this.handleUpgrade);
|
|---|
| 55 | // prevent duplicate upgrade handling;
|
|---|
| 56 | // in case external upgrade is also configured
|
|---|
| 57 | this.wsInternalSubscribed = true;
|
|---|
| 58 | }
|
|---|
| 59 | };
|
|---|
| 60 | this.handleUpgrade = async (req, socket, head) => {
|
|---|
| 61 | if (this.shouldProxy(this.config.context, req)) {
|
|---|
| 62 | const activeProxyOptions = await this.prepareProxyRequest(req);
|
|---|
| 63 | this.proxy.ws(req, socket, head, activeProxyOptions);
|
|---|
| 64 | this.logger.info('[HPM] Upgrading to WebSocket');
|
|---|
| 65 | }
|
|---|
| 66 | };
|
|---|
| 67 | /**
|
|---|
| 68 | * Determine whether request should be proxied.
|
|---|
| 69 | *
|
|---|
| 70 | * @private
|
|---|
| 71 | * @param {String} context [description]
|
|---|
| 72 | * @param {Object} req [description]
|
|---|
| 73 | * @return {Boolean}
|
|---|
| 74 | */
|
|---|
| 75 | this.shouldProxy = (context, req) => {
|
|---|
| 76 | try {
|
|---|
| 77 | const path = req.originalUrl || req.url;
|
|---|
| 78 | return contextMatcher.match(context, path, req);
|
|---|
| 79 | }
|
|---|
| 80 | catch (error) {
|
|---|
| 81 | this.logger.error(error);
|
|---|
| 82 | return false;
|
|---|
| 83 | }
|
|---|
| 84 | };
|
|---|
| 85 | /**
|
|---|
| 86 | * Apply option.router and option.pathRewrite
|
|---|
| 87 | * Order matters:
|
|---|
| 88 | * Router uses original path for routing;
|
|---|
| 89 | * NOT the modified path, after it has been rewritten by pathRewrite
|
|---|
| 90 | * @param {Object} req
|
|---|
| 91 | * @return {Object} proxy options
|
|---|
| 92 | */
|
|---|
| 93 | this.prepareProxyRequest = async (req) => {
|
|---|
| 94 | // https://github.com/chimurai/http-proxy-middleware/issues/17
|
|---|
| 95 | // https://github.com/chimurai/http-proxy-middleware/issues/94
|
|---|
| 96 | req.url = req.originalUrl || req.url;
|
|---|
| 97 | // store uri before it gets rewritten for logging
|
|---|
| 98 | const originalPath = req.url;
|
|---|
| 99 | const newProxyOptions = Object.assign({}, this.proxyOptions);
|
|---|
| 100 | // Apply in order:
|
|---|
| 101 | // 1. option.router
|
|---|
| 102 | // 2. option.pathRewrite
|
|---|
| 103 | await this.applyRouter(req, newProxyOptions);
|
|---|
| 104 | await this.applyPathRewrite(req, this.pathRewriter);
|
|---|
| 105 | // debug logging for both http(s) and websockets
|
|---|
| 106 | if (this.proxyOptions.logLevel === 'debug') {
|
|---|
| 107 | const arrow = (0, logger_1.getArrow)(originalPath, req.url, this.proxyOptions.target, newProxyOptions.target);
|
|---|
| 108 | this.logger.debug('[HPM] %s %s %s %s', req.method, originalPath, arrow, newProxyOptions.target);
|
|---|
| 109 | }
|
|---|
| 110 | return newProxyOptions;
|
|---|
| 111 | };
|
|---|
| 112 | // Modify option.target when router present.
|
|---|
| 113 | this.applyRouter = async (req, options) => {
|
|---|
| 114 | let newTarget;
|
|---|
| 115 | if (options.router) {
|
|---|
| 116 | newTarget = await Router.getTarget(req, options);
|
|---|
| 117 | if (newTarget) {
|
|---|
| 118 | this.logger.debug('[HPM] Router new target: %s -> "%s"', options.target, newTarget);
|
|---|
| 119 | options.target = newTarget;
|
|---|
| 120 | }
|
|---|
| 121 | }
|
|---|
| 122 | };
|
|---|
| 123 | // rewrite path
|
|---|
| 124 | this.applyPathRewrite = async (req, pathRewriter) => {
|
|---|
| 125 | if (pathRewriter) {
|
|---|
| 126 | const path = await pathRewriter(req.url, req);
|
|---|
| 127 | if (typeof path === 'string') {
|
|---|
| 128 | req.url = path;
|
|---|
| 129 | }
|
|---|
| 130 | else {
|
|---|
| 131 | this.logger.info('[HPM] pathRewrite: No rewritten path found. (%s)', req.url);
|
|---|
| 132 | }
|
|---|
| 133 | }
|
|---|
| 134 | };
|
|---|
| 135 | this.logError = (err, req, res, target) => {
|
|---|
| 136 | var _a;
|
|---|
| 137 | const hostname = ((_a = req.headers) === null || _a === void 0 ? void 0 : _a.host) || req.hostname || req.host; // (websocket) || (node0.10 || node 4/5)
|
|---|
| 138 | const requestHref = `${hostname}${req.url}`;
|
|---|
| 139 | const targetHref = `${target === null || target === void 0 ? void 0 : target.href}`; // target is undefined when websocket errors
|
|---|
| 140 | const errorMessage = '[HPM] Error occurred while proxying request %s to %s [%s] (%s)';
|
|---|
| 141 | const errReference = 'https://nodejs.org/api/errors.html#errors_common_system_errors'; // link to Node Common Systems Errors page
|
|---|
| 142 | this.logger.error(errorMessage, requestHref, targetHref, err.code || err, errReference);
|
|---|
| 143 | };
|
|---|
| 144 | this.config = (0, config_factory_1.createConfig)(context, opts);
|
|---|
| 145 | this.proxyOptions = this.config.options;
|
|---|
| 146 | // create proxy
|
|---|
| 147 | this.proxy = httpProxy.createProxyServer({});
|
|---|
| 148 | this.logger.info(`[HPM] Proxy created: ${this.config.context} -> ${this.proxyOptions.target}`);
|
|---|
| 149 | this.pathRewriter = PathRewriter.createPathRewriter(this.proxyOptions.pathRewrite); // returns undefined when "pathRewrite" is not provided
|
|---|
| 150 | // attach handler to http-proxy events
|
|---|
| 151 | handlers.init(this.proxy, this.proxyOptions);
|
|---|
| 152 | // log errors for debug purpose
|
|---|
| 153 | this.proxy.on('error', this.logError);
|
|---|
| 154 | // https://github.com/chimurai/http-proxy-middleware/issues/19
|
|---|
| 155 | // expose function to upgrade externally
|
|---|
| 156 | this.middleware.upgrade = (req, socket, head) => {
|
|---|
| 157 | if (!this.wsInternalSubscribed) {
|
|---|
| 158 | this.handleUpgrade(req, socket, head);
|
|---|
| 159 | }
|
|---|
| 160 | };
|
|---|
| 161 | }
|
|---|
| 162 | }
|
|---|
| 163 | exports.HttpProxyMiddleware = HttpProxyMiddleware;
|
|---|