http-proxy-middleware.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  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. const path = req.originalUrl || req.url;
  77. return contextMatcher.match(context, path, req);
  78. };
  79. /**
  80. * Apply option.router and option.pathRewrite
  81. * Order matters:
  82. * Router uses original path for routing;
  83. * NOT the modified path, after it has been rewritten by pathRewrite
  84. * @param {Object} req
  85. * @return {Object} proxy options
  86. */
  87. this.prepareProxyRequest = async (req) => {
  88. // https://github.com/chimurai/http-proxy-middleware/issues/17
  89. // https://github.com/chimurai/http-proxy-middleware/issues/94
  90. req.url = req.originalUrl || req.url;
  91. // store uri before it gets rewritten for logging
  92. const originalPath = req.url;
  93. const newProxyOptions = Object.assign({}, this.proxyOptions);
  94. // Apply in order:
  95. // 1. option.router
  96. // 2. option.pathRewrite
  97. await this.applyRouter(req, newProxyOptions);
  98. await this.applyPathRewrite(req, this.pathRewriter);
  99. // debug logging for both http(s) and websockets
  100. if (this.proxyOptions.logLevel === 'debug') {
  101. const arrow = (0, logger_1.getArrow)(originalPath, req.url, this.proxyOptions.target, newProxyOptions.target);
  102. this.logger.debug('[HPM] %s %s %s %s', req.method, originalPath, arrow, newProxyOptions.target);
  103. }
  104. return newProxyOptions;
  105. };
  106. // Modify option.target when router present.
  107. this.applyRouter = async (req, options) => {
  108. let newTarget;
  109. if (options.router) {
  110. newTarget = await Router.getTarget(req, options);
  111. if (newTarget) {
  112. this.logger.debug('[HPM] Router new target: %s -> "%s"', options.target, newTarget);
  113. options.target = newTarget;
  114. }
  115. }
  116. };
  117. // rewrite path
  118. this.applyPathRewrite = async (req, pathRewriter) => {
  119. if (pathRewriter) {
  120. const path = await pathRewriter(req.url, req);
  121. if (typeof path === 'string') {
  122. req.url = path;
  123. }
  124. else {
  125. this.logger.info('[HPM] pathRewrite: No rewritten path found. (%s)', req.url);
  126. }
  127. }
  128. };
  129. this.logError = (err, req, res, target) => {
  130. var _a;
  131. const hostname = ((_a = req.headers) === null || _a === void 0 ? void 0 : _a.host) || req.hostname || req.host; // (websocket) || (node0.10 || node 4/5)
  132. const requestHref = `${hostname}${req.url}`;
  133. const targetHref = `${target === null || target === void 0 ? void 0 : target.href}`; // target is undefined when websocket errors
  134. const errorMessage = '[HPM] Error occurred while proxying request %s to %s [%s] (%s)';
  135. const errReference = 'https://nodejs.org/api/errors.html#errors_common_system_errors'; // link to Node Common Systems Errors page
  136. this.logger.error(errorMessage, requestHref, targetHref, err.code || err, errReference);
  137. };
  138. this.config = (0, config_factory_1.createConfig)(context, opts);
  139. this.proxyOptions = this.config.options;
  140. // create proxy
  141. this.proxy = httpProxy.createProxyServer({});
  142. this.logger.info(`[HPM] Proxy created: ${this.config.context} -> ${this.proxyOptions.target}`);
  143. this.pathRewriter = PathRewriter.createPathRewriter(this.proxyOptions.pathRewrite); // returns undefined when "pathRewrite" is not provided
  144. // attach handler to http-proxy events
  145. handlers.init(this.proxy, this.proxyOptions);
  146. // log errors for debug purpose
  147. this.proxy.on('error', this.logError);
  148. // https://github.com/chimurai/http-proxy-middleware/issues/19
  149. // expose function to upgrade externally
  150. this.middleware.upgrade = (req, socket, head) => {
  151. if (!this.wsInternalSubscribed) {
  152. this.handleUpgrade(req, socket, head);
  153. }
  154. };
  155. }
  156. }
  157. exports.HttpProxyMiddleware = HttpProxyMiddleware;