http-proxy-middleware
Advanced tools
Comparing version 2.0.6 to 3.0.0-beta.0
export declare enum ERRORS { | ||
ERR_CONFIG_FACTORY_TARGET_MISSING = "[HPM] Missing \"target\" option. Example: {target: \"http://www.example.org\"}", | ||
ERR_CONTEXT_MATCHER_GENERIC = "[HPM] Invalid context. Expecting something like: \"/api\" or [\"/api\", \"/ajax\"]", | ||
ERR_CONTEXT_MATCHER_INVALID_ARRAY = "[HPM] Invalid context. Expecting something like: [\"/api\", \"/ajax\"] or [\"/api/**\", \"!**.html\"]", | ||
ERR_CONTEXT_MATCHER_INVALID_ARRAY = "[HPM] Invalid pathFilter. Expecting something like: [\"/api\", \"/ajax\"] or [\"/api/**\", \"!**.html\"]", | ||
ERR_PATH_REWRITER_CONFIG = "[HPM] Invalid pathRewrite config. Expecting object with pathRewrite config or a rewrite function" | ||
} |
@@ -8,4 +8,4 @@ "use strict"; | ||
ERRORS["ERR_CONTEXT_MATCHER_GENERIC"] = "[HPM] Invalid context. Expecting something like: \"/api\" or [\"/api\", \"/ajax\"]"; | ||
ERRORS["ERR_CONTEXT_MATCHER_INVALID_ARRAY"] = "[HPM] Invalid context. Expecting something like: [\"/api\", \"/ajax\"] or [\"/api/**\", \"!**.html\"]"; | ||
ERRORS["ERR_CONTEXT_MATCHER_INVALID_ARRAY"] = "[HPM] Invalid pathFilter. Expecting something like: [\"/api\", \"/ajax\"] or [\"/api/**\", \"!**.html\"]"; | ||
ERRORS["ERR_PATH_REWRITER_CONFIG"] = "[HPM] Invalid pathRewrite config. Expecting object with pathRewrite config or a rewrite function"; | ||
})(ERRORS = exports.ERRORS || (exports.ERRORS = {})); |
/// <reference types="node" /> | ||
import type * as http from 'http'; | ||
import type { Request } from '../types'; | ||
/** | ||
* Fix proxied body if bodyParser is involved. | ||
*/ | ||
export declare function fixRequestBody(proxyReq: http.ClientRequest, req: http.IncomingMessage): void; | ||
export declare function fixRequestBody(proxyReq: http.ClientRequest, req: Request): void; |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -6,0 +10,0 @@ if (k2 === undefined) k2 = k; |
@@ -5,2 +5,5 @@ "use strict"; | ||
const zlib = require("zlib"); | ||
const debug_1 = require("../debug"); | ||
const function_1 = require("../utils/function"); | ||
const debug = debug_1.Debug.extend('response-interceptor'); | ||
/** | ||
@@ -14,3 +17,4 @@ * Intercept responses from upstream. | ||
function responseInterceptor(interceptor) { | ||
return async function proxyRes(proxyRes, req, res) { | ||
return async function proxyResResponseInterceptor(proxyRes, req, res) { | ||
debug('intercept proxy response'); | ||
const originalProxyRes = proxyRes; | ||
@@ -26,5 +30,8 @@ let buffer = Buffer.from('', 'utf8'); | ||
// call interceptor with intercepted response (buffer) | ||
debug('call interceptor function: %s', (0, function_1.getFunctionName)(interceptor)); | ||
const interceptedBuffer = Buffer.from(await interceptor(buffer, originalProxyRes, req, res)); | ||
// set correct content-length (with double byte character support) | ||
debug('set content-length: %s', Buffer.byteLength(interceptedBuffer, 'utf8')); | ||
res.setHeader('content-length', Buffer.byteLength(interceptedBuffer, 'utf8')); | ||
debug('write intercepted response'); | ||
res.write(interceptedBuffer); | ||
@@ -60,2 +67,3 @@ res.end(); | ||
if (decompress) { | ||
debug(`decompress proxy response with 'content-encoding': %s`, contentEncoding); | ||
_proxyRes.pipe(decompress); | ||
@@ -71,2 +79,3 @@ _proxyRes = decompress; | ||
function copyHeaders(originalResponse, response) { | ||
debug('copy original response headers'); | ||
response.statusCode = originalResponse.statusCode; | ||
@@ -73,0 +82,0 @@ response.statusMessage = originalResponse.statusMessage; |
@@ -1,5 +0,3 @@ | ||
import type { Filter, RequestHandler, Options } from './types'; | ||
import type { RequestHandler, Options } from './types'; | ||
export declare class HttpProxyMiddleware { | ||
private logger; | ||
private config; | ||
private wsInternalSubscribed; | ||
@@ -10,4 +8,5 @@ private serverOnCloseSubscribed; | ||
private pathRewriter; | ||
constructor(context: Filter | Options, opts?: Options); | ||
constructor(options: Options); | ||
middleware: RequestHandler; | ||
private registerPlugins; | ||
private catchUpgradeRequest; | ||
@@ -17,7 +16,2 @@ private handleUpgrade; | ||
* Determine whether request should be proxied. | ||
* | ||
* @private | ||
* @param {String} context [description] | ||
* @param {Object} req [description] | ||
* @return {Boolean} | ||
*/ | ||
@@ -36,3 +30,2 @@ private shouldProxy; | ||
private applyPathRewrite; | ||
private logError; | ||
} |
@@ -5,11 +5,11 @@ "use strict"; | ||
const httpProxy = require("http-proxy"); | ||
const config_factory_1 = require("./config-factory"); | ||
const contextMatcher = require("./context-matcher"); | ||
const handlers = require("./_handlers"); | ||
const logger_1 = require("./logger"); | ||
const configuration_1 = require("./configuration"); | ||
const get_plugins_1 = require("./get-plugins"); | ||
const path_filter_1 = require("./path-filter"); | ||
const PathRewriter = require("./path-rewriter"); | ||
const Router = require("./router"); | ||
const debug_1 = require("./debug"); | ||
const function_1 = require("./utils/function"); | ||
class HttpProxyMiddleware { | ||
constructor(context, opts) { | ||
this.logger = (0, logger_1.getInstance)(); | ||
constructor(options) { | ||
this.wsInternalSubscribed = false; | ||
@@ -20,13 +20,14 @@ this.serverOnCloseSubscribed = false; | ||
var _a, _b; | ||
if (this.shouldProxy(this.config.context, req)) { | ||
if (this.shouldProxy(this.proxyOptions.pathFilter, req)) { | ||
try { | ||
const activeProxyOptions = await this.prepareProxyRequest(req); | ||
(0, debug_1.Debug)(`proxy request to target: %O`, activeProxyOptions.target); | ||
this.proxy.web(req, res, activeProxyOptions); | ||
} | ||
catch (err) { | ||
next(err); | ||
next && next(err); | ||
} | ||
} | ||
else { | ||
next(); | ||
next && next(); | ||
} | ||
@@ -41,6 +42,6 @@ /** | ||
*/ | ||
const server = (_b = ((_a = req.socket) !== null && _a !== void 0 ? _a : req.connection)) === null || _b === void 0 ? void 0 : _b.server; | ||
const server = (_b = (((_a = req.socket) !== null && _a !== void 0 ? _a : req.connection))) === null || _b === void 0 ? void 0 : _b.server; | ||
if (server && !this.serverOnCloseSubscribed) { | ||
server.on('close', () => { | ||
this.logger.info('[HPM] server close signal received: closing proxy server'); | ||
(0, debug_1.Debug)('server close signal received: closing proxy server'); | ||
this.proxy.close(); | ||
@@ -57,2 +58,3 @@ }); | ||
if (!this.wsInternalSubscribed) { | ||
(0, debug_1.Debug)('subscribing to server upgrade event'); | ||
server.on('upgrade', this.handleUpgrade); | ||
@@ -65,6 +67,6 @@ // prevent duplicate upgrade handling; | ||
this.handleUpgrade = async (req, socket, head) => { | ||
if (this.shouldProxy(this.config.context, req)) { | ||
if (this.shouldProxy(this.proxyOptions.pathFilter, req)) { | ||
const activeProxyOptions = await this.prepareProxyRequest(req); | ||
this.proxy.ws(req, socket, head, activeProxyOptions); | ||
this.logger.info('[HPM] Upgrading to WebSocket'); | ||
(0, debug_1.Debug)('server upgrade event received. Proxying WebSocket'); | ||
} | ||
@@ -74,11 +76,5 @@ }; | ||
* Determine whether request should be proxied. | ||
* | ||
* @private | ||
* @param {String} context [description] | ||
* @param {Object} req [description] | ||
* @return {Boolean} | ||
*/ | ||
this.shouldProxy = (context, req) => { | ||
const path = req.originalUrl || req.url; | ||
return contextMatcher.match(context, path, req); | ||
this.shouldProxy = (pathFilter, req) => { | ||
return (0, path_filter_1.matchPathFilter)(pathFilter, req.url, req); | ||
}; | ||
@@ -94,7 +90,10 @@ /** | ||
this.prepareProxyRequest = async (req) => { | ||
// https://github.com/chimurai/http-proxy-middleware/issues/17 | ||
// https://github.com/chimurai/http-proxy-middleware/issues/94 | ||
req.url = req.originalUrl || req.url; | ||
// store uri before it gets rewritten for logging | ||
const originalPath = req.url; | ||
/** | ||
* Incorrect usage confirmed: https://github.com/expressjs/express/issues/4854#issuecomment-1066171160 | ||
* Temporary restore req.url patch for {@link src/legacy/create-proxy-middleware.ts legacyCreateProxyMiddleware()} | ||
* FIXME: remove this patch in future release | ||
*/ | ||
if (this.middleware.__LEGACY_HTTP_PROXY_MIDDLEWARE__) { | ||
req.url = req.originalUrl || req.url; | ||
} | ||
const newProxyOptions = Object.assign({}, this.proxyOptions); | ||
@@ -106,7 +105,2 @@ // Apply in order: | ||
await this.applyPathRewrite(req, this.pathRewriter); | ||
// debug logging for both http(s) and websockets | ||
if (this.proxyOptions.logLevel === 'debug') { | ||
const arrow = (0, logger_1.getArrow)(originalPath, req.url, this.proxyOptions.target, newProxyOptions.target); | ||
this.logger.debug('[HPM] %s %s %s %s', req.method, originalPath, arrow, newProxyOptions.target); | ||
} | ||
return newProxyOptions; | ||
@@ -120,3 +114,3 @@ }; | ||
if (newTarget) { | ||
this.logger.debug('[HPM] Router new target: %s -> "%s"', options.target, newTarget); | ||
(0, debug_1.Debug)('router new target: "%s"', newTarget); | ||
options.target = newTarget; | ||
@@ -131,28 +125,16 @@ } | ||
if (typeof path === 'string') { | ||
(0, debug_1.Debug)('pathRewrite new path: %s', req.url); | ||
req.url = path; | ||
} | ||
else { | ||
this.logger.info('[HPM] pathRewrite: No rewritten path found. (%s)', req.url); | ||
(0, debug_1.Debug)('pathRewrite: no rewritten path found: %s', req.url); | ||
} | ||
} | ||
}; | ||
this.logError = (err, req, res, target) => { | ||
var _a; | ||
const hostname = ((_a = req.headers) === null || _a === void 0 ? void 0 : _a.host) || req.hostname || req.host; // (websocket) || (node0.10 || node 4/5) | ||
const requestHref = `${hostname}${req.url}`; | ||
const targetHref = `${target === null || target === void 0 ? void 0 : target.href}`; // target is undefined when websocket errors | ||
const errorMessage = '[HPM] Error occurred while proxying request %s to %s [%s] (%s)'; | ||
const errReference = 'https://nodejs.org/api/errors.html#errors_common_system_errors'; // link to Node Common Systems Errors page | ||
this.logger.error(errorMessage, requestHref, targetHref, err.code || err, errReference); | ||
}; | ||
this.config = (0, config_factory_1.createConfig)(context, opts); | ||
this.proxyOptions = this.config.options; | ||
// create proxy | ||
(0, configuration_1.verifyConfig)(options); | ||
this.proxyOptions = options; | ||
(0, debug_1.Debug)(`create proxy server`); | ||
this.proxy = httpProxy.createProxyServer({}); | ||
this.logger.info(`[HPM] Proxy created: ${this.config.context} -> ${this.proxyOptions.target}`); | ||
this.registerPlugins(this.proxy, this.proxyOptions); | ||
this.pathRewriter = PathRewriter.createPathRewriter(this.proxyOptions.pathRewrite); // returns undefined when "pathRewrite" is not provided | ||
// attach handler to http-proxy events | ||
handlers.init(this.proxy, this.proxyOptions); | ||
// log errors for debug purpose | ||
this.proxy.on('error', this.logError); | ||
// https://github.com/chimurai/http-proxy-middleware/issues/19 | ||
@@ -166,3 +148,10 @@ // expose function to upgrade externally | ||
} | ||
registerPlugins(proxy, options) { | ||
const plugins = (0, get_plugins_1.getPlugins)(options); | ||
plugins.forEach((plugin) => { | ||
(0, debug_1.Debug)(`register plugin: "${(0, function_1.getFunctionName)(plugin)}"`); | ||
plugin(proxy, options); | ||
}); | ||
} | ||
} | ||
exports.HttpProxyMiddleware = HttpProxyMiddleware; |
@@ -1,4 +0,12 @@ | ||
import { Filter, Options } from './types'; | ||
export declare function createProxyMiddleware(context: Filter | Options, options?: Options): import("./types").RequestHandler; | ||
import type { Options, RequestHandler } from './types'; | ||
export declare function createProxyMiddleware(options: Options): RequestHandler; | ||
export * from './handlers'; | ||
export { Filter, Options, RequestHandler } from './types'; | ||
export type { Filter, Options, RequestHandler } from './types'; | ||
/** | ||
* Default plugins | ||
*/ | ||
export * from './plugins/default'; | ||
/** | ||
* Legacy exports | ||
*/ | ||
export * from './legacy'; |
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
@@ -15,4 +19,4 @@ if (k2 === undefined) k2 = k; | ||
const http_proxy_middleware_1 = require("./http-proxy-middleware"); | ||
function createProxyMiddleware(context, options) { | ||
const { middleware } = new http_proxy_middleware_1.HttpProxyMiddleware(context, options); | ||
function createProxyMiddleware(options) { | ||
const { middleware } = new http_proxy_middleware_1.HttpProxyMiddleware(options); | ||
return middleware; | ||
@@ -22,1 +26,9 @@ } | ||
__exportStar(require("./handlers"), exports); | ||
/** | ||
* Default plugins | ||
*/ | ||
__exportStar(require("./plugins/default"), exports); | ||
/** | ||
* Legacy exports | ||
*/ | ||
__exportStar(require("./legacy"), exports); |
@@ -1,14 +0,2 @@ | ||
export declare function getInstance(): any; | ||
/** | ||
* -> normal proxy | ||
* => router | ||
* ~> pathRewrite | ||
* ≈> router + pathRewrite | ||
* | ||
* @param {String} originalPath | ||
* @param {String} newPath | ||
* @param {String} originalTarget | ||
* @param {String} newTarget | ||
* @return {String} | ||
*/ | ||
export declare function getArrow(originalPath: any, newPath: any, originalTarget: any, newTarget: any): string; | ||
import { Logger, Options } from './types'; | ||
export declare function getLogger(options: Options): Logger; |
"use strict"; | ||
/* eslint-disable prefer-rest-params */ | ||
/* eslint-disable @typescript-eslint/no-empty-function */ | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.getArrow = exports.getInstance = void 0; | ||
const util = require("util"); | ||
let loggerInstance; | ||
const defaultProvider = { | ||
// tslint:disable: no-console | ||
log: console.log, | ||
debug: console.log, | ||
info: console.info, | ||
warn: console.warn, | ||
error: console.error, | ||
}; | ||
// log level 'weight' | ||
var LEVELS; | ||
(function (LEVELS) { | ||
LEVELS[LEVELS["debug"] = 10] = "debug"; | ||
LEVELS[LEVELS["info"] = 20] = "info"; | ||
LEVELS[LEVELS["warn"] = 30] = "warn"; | ||
LEVELS[LEVELS["error"] = 50] = "error"; | ||
LEVELS[LEVELS["silent"] = 80] = "silent"; | ||
})(LEVELS || (LEVELS = {})); | ||
function getInstance() { | ||
if (!loggerInstance) { | ||
loggerInstance = new Logger(); | ||
} | ||
return loggerInstance; | ||
} | ||
exports.getInstance = getInstance; | ||
class Logger { | ||
constructor() { | ||
this.setLevel('info'); | ||
this.setProvider(() => defaultProvider); | ||
} | ||
// log will log messages, regardless of logLevels | ||
log() { | ||
this.provider.log(this._interpolate.apply(null, arguments)); | ||
} | ||
debug() { | ||
if (this._showLevel('debug')) { | ||
this.provider.debug(this._interpolate.apply(null, arguments)); | ||
} | ||
} | ||
info() { | ||
if (this._showLevel('info')) { | ||
this.provider.info(this._interpolate.apply(null, arguments)); | ||
} | ||
} | ||
warn() { | ||
if (this._showLevel('warn')) { | ||
this.provider.warn(this._interpolate.apply(null, arguments)); | ||
} | ||
} | ||
error() { | ||
if (this._showLevel('error')) { | ||
this.provider.error(this._interpolate.apply(null, arguments)); | ||
} | ||
} | ||
setLevel(v) { | ||
if (this.isValidLevel(v)) { | ||
this.logLevel = v; | ||
} | ||
} | ||
setProvider(fn) { | ||
if (fn && this.isValidProvider(fn)) { | ||
this.provider = fn(defaultProvider); | ||
} | ||
} | ||
isValidProvider(fnProvider) { | ||
const result = true; | ||
if (fnProvider && typeof fnProvider !== 'function') { | ||
throw new Error('[HPM] Log provider config error. Expecting a function.'); | ||
} | ||
return result; | ||
} | ||
isValidLevel(levelName) { | ||
const validLevels = Object.keys(LEVELS); | ||
const isValid = validLevels.includes(levelName); | ||
if (!isValid) { | ||
throw new Error('[HPM] Log level error. Invalid logLevel.'); | ||
} | ||
return isValid; | ||
} | ||
/** | ||
* Decide to log or not to log, based on the log levels 'weight' | ||
* @param {String} showLevel [debug, info, warn, error, silent] | ||
* @return {Boolean} | ||
*/ | ||
_showLevel(showLevel) { | ||
let result = false; | ||
const currentLogLevel = LEVELS[this.logLevel]; | ||
if (currentLogLevel && currentLogLevel <= LEVELS[showLevel]) { | ||
result = true; | ||
} | ||
return result; | ||
} | ||
// make sure logged messages and its data are return interpolated | ||
// make it possible for additional log data, such date/time or custom prefix. | ||
_interpolate(format, ...args) { | ||
const result = util.format(format, ...args); | ||
return result; | ||
} | ||
} | ||
exports.getLogger = void 0; | ||
/** | ||
* -> normal proxy | ||
* => router | ||
* ~> pathRewrite | ||
* ≈> router + pathRewrite | ||
* Compatibility matrix | ||
* | ||
* @param {String} originalPath | ||
* @param {String} newPath | ||
* @param {String} originalTarget | ||
* @param {String} newTarget | ||
* @return {String} | ||
| Library | log | info | warn | error | \<interpolation\> | | ||
|----------|:------|:-------|:------|:--------|:------------------| | ||
| console | ✅ | ✅ | ✅ | ✅ | ✅ (%s %o %O) | | ||
| bunyan | ❌ | ✅ | ✅ | ✅ | ✅ (%s %o %O) | | ||
| pino | ❌ | ✅ | ✅ | ✅ | ✅ (%s %o %O) | | ||
| winston | ❌ | ✅ | ✅ | ✅ | ✅ (%s %o %O)^1 | | ||
| log4js | ❌ | ✅ | ✅ | ✅ | ✅ (%s %o %O) | | ||
* | ||
* ^1: https://github.com/winstonjs/winston#string-interpolation | ||
*/ | ||
function getArrow(originalPath, newPath, originalTarget, newTarget) { | ||
const arrow = ['>']; | ||
const isNewTarget = originalTarget !== newTarget; // router | ||
const isNewPath = originalPath !== newPath; // pathRewrite | ||
if (isNewPath && !isNewTarget) { | ||
arrow.unshift('~'); | ||
} | ||
else if (!isNewPath && isNewTarget) { | ||
arrow.unshift('='); | ||
} | ||
else if (isNewPath && isNewTarget) { | ||
arrow.unshift('≈'); | ||
} | ||
else { | ||
arrow.unshift('-'); | ||
} | ||
return arrow.join(''); | ||
const noopLogger = { | ||
info: () => { }, | ||
warn: () => { }, | ||
error: () => { }, | ||
}; | ||
function getLogger(options) { | ||
return options.logger || noopLogger; | ||
} | ||
exports.getArrow = getArrow; | ||
exports.getLogger = getLogger; |
@@ -6,4 +6,4 @@ "use strict"; | ||
const errors_1 = require("./errors"); | ||
const logger_1 = require("./logger"); | ||
const logger = (0, logger_1.getInstance)(); | ||
const debug_1 = require("./debug"); | ||
const debug = debug_1.Debug.extend('path-rewriter'); | ||
/** | ||
@@ -33,3 +33,3 @@ * Create rewrite function, to cache parsed rewrite rules. | ||
result = result.replace(rule.regex, rule.value); | ||
logger.debug('[HPM] Rewriting path from "%s" to "%s"', path, result); | ||
debug('rewriting path from "%s" to "%s"', path, result); | ||
break; | ||
@@ -59,8 +59,8 @@ } | ||
if (isPlainObj(rewriteConfig)) { | ||
for (const [key] of Object.entries(rewriteConfig)) { | ||
for (const [key, value] of Object.entries(rewriteConfig)) { | ||
rules.push({ | ||
regex: new RegExp(key), | ||
value: rewriteConfig[key], | ||
value: value, | ||
}); | ||
logger.info('[HPM] Proxy rewrite rule created: "%s" ~> "%s"', key, rewriteConfig[key]); | ||
debug('rewrite rule created: "%s" ~> "%s"', key, value); | ||
} | ||
@@ -67,0 +67,0 @@ } |
@@ -5,4 +5,4 @@ "use strict"; | ||
const isPlainObj = require("is-plain-obj"); | ||
const logger_1 = require("./logger"); | ||
const logger = (0, logger_1.getInstance)(); | ||
const debug_1 = require("./debug"); | ||
const debug = debug_1.Debug.extend('router'); | ||
async function getTarget(req, config) { | ||
@@ -25,8 +25,8 @@ let newTarget; | ||
const hostAndPath = host + path; | ||
for (const [key] of Object.entries(table)) { | ||
for (const [key, value] of Object.entries(table)) { | ||
if (containsPath(key)) { | ||
if (hostAndPath.indexOf(key) > -1) { | ||
// match 'localhost:3000/api' | ||
result = table[key]; | ||
logger.debug('[HPM] Router table match: "%s"', key); | ||
result = value; | ||
debug('match: "%s" -> "%s"', key, result); | ||
break; | ||
@@ -38,4 +38,4 @@ } | ||
// match 'localhost:3000' | ||
result = table[key]; | ||
logger.debug('[HPM] Router table match: "%s"', host); | ||
result = value; | ||
debug('match: "%s" -> "%s"', host, result); | ||
break; | ||
@@ -42,0 +42,0 @@ } |
@@ -6,50 +6,86 @@ /** | ||
/// <reference types="node" /> | ||
import type * as express from 'express'; | ||
import type * as http from 'http'; | ||
import type * as httpProxy from 'http-proxy'; | ||
import type * as net from 'net'; | ||
import type * as url from 'url'; | ||
export interface Request extends express.Request { | ||
} | ||
export interface Response extends express.Response { | ||
} | ||
export interface RequestHandler extends express.RequestHandler { | ||
export declare type Request<T = http.IncomingMessage> = T; | ||
export declare type Response<T = http.ServerResponse> = T; | ||
export declare type NextFunction<T = (err?: any) => void> = T; | ||
export interface RequestHandler { | ||
(req: Request, res: Response, next?: NextFunction): void | Promise<void>; | ||
upgrade?: (req: Request, socket: net.Socket, head: any) => void; | ||
} | ||
export declare type Filter = string | string[] | ((pathname: string, req: Request) => boolean); | ||
export declare type Plugin = (proxyServer: httpProxy, options: Options) => void; | ||
export declare type OnProxyEvent = { | ||
error?: httpProxy.ErrorCallback; | ||
proxyReq?: httpProxy.ProxyReqCallback; | ||
proxyReqWs?: httpProxy.ProxyReqWsCallback; | ||
proxyRes?: httpProxy.ProxyResCallback; | ||
open?: httpProxy.OpenCallback; | ||
close?: httpProxy.CloseCallback; | ||
start?: httpProxy.StartCallback; | ||
end?: httpProxy.EndCallback; | ||
econnreset?: httpProxy.EconnresetCallback; | ||
}; | ||
export declare type Logger = Pick<Console, 'info' | 'warn' | 'error'>; | ||
export interface Options extends httpProxy.ServerOptions { | ||
/** | ||
* Narrow down requests to proxy or not. | ||
* Filter on {@link http.IncomingMessage.url `pathname`} which is relative to the proxy's "mounting" point in the server. | ||
* Or use the {@link http.IncomingMessage `req`} object for more complex filtering. | ||
*/ | ||
pathFilter?: Filter; | ||
pathRewrite?: { | ||
[regexp: string]: string; | ||
} | ((path: string, req: Request) => string) | ((path: string, req: Request) => Promise<string>); | ||
/** | ||
* Access the internal http-proxy server instance to customize behavior | ||
* | ||
* @example | ||
* ```js | ||
* createProxyMiddleware({ | ||
* plugins: [(proxyServer, options) => { | ||
* proxyServer.on('error', (error, req, res) => { | ||
* console.error(error); | ||
* }); | ||
* }] | ||
* }); | ||
* ``` | ||
*/ | ||
plugins?: Plugin[]; | ||
/** | ||
* Eject pre-configured plugins. | ||
* NOTE: register your own error handlers to prevent server from crashing. | ||
* | ||
* @since v3.0.0 | ||
*/ | ||
ejectPlugins?: boolean; | ||
/** | ||
* Listen to http-proxy events | ||
* @see {@link OnProxyEvent} for available events | ||
* @example | ||
* ```js | ||
* createProxyMiddleware({ | ||
* on: { | ||
* error: (error, req, res, target) => { | ||
* console.error(error); | ||
* } | ||
* } | ||
* }); | ||
* ``` | ||
*/ | ||
on?: OnProxyEvent; | ||
router?: { | ||
[hostOrPath: string]: httpProxy.ServerOptions['target']; | ||
} | ((req: Request) => httpProxy.ServerOptions['target']) | ((req: Request) => Promise<httpProxy.ServerOptions['target']>); | ||
logLevel?: 'debug' | 'info' | 'warn' | 'error' | 'silent'; | ||
logProvider?: LogProviderCallback; | ||
onError?: OnErrorCallback; | ||
onProxyRes?: OnProxyResCallback; | ||
onProxyReq?: OnProxyReqCallback; | ||
onProxyReqWs?: OnProxyReqWsCallback; | ||
onOpen?: OnOpenCallback; | ||
onClose?: OnCloseCallback; | ||
/** | ||
* Log information from http-proxy-middleware | ||
* @example | ||
* ```js | ||
* createProxyMiddleware({ | ||
* logger: console | ||
* }); | ||
* ``` | ||
*/ | ||
logger?: Logger | any; | ||
} | ||
interface LogProvider { | ||
log: Logger; | ||
debug?: Logger; | ||
info?: Logger; | ||
warn?: Logger; | ||
error?: Logger; | ||
} | ||
declare type Logger = (...args: any[]) => void; | ||
export declare type LogProviderCallback = (provider: LogProvider) => LogProvider; | ||
/** | ||
* Use types based on the events listeners from http-proxy | ||
* https://github.com/DefinitelyTyped/DefinitelyTyped/blob/51504fd999031b7f025220fab279f1b2155cbaff/types/http-proxy/index.d.ts | ||
*/ | ||
export declare type OnErrorCallback = (err: Error, req: Request, res: Response, target?: string | Partial<url.Url>) => void; | ||
export declare type OnProxyResCallback = (proxyRes: http.IncomingMessage, req: Request, res: Response) => void; | ||
export declare type OnProxyReqCallback = (proxyReq: http.ClientRequest, req: Request, res: Response, options: httpProxy.ServerOptions) => void; | ||
export declare type OnProxyReqWsCallback = (proxyReq: http.ClientRequest, req: Request, socket: net.Socket, options: httpProxy.ServerOptions, head: any) => void; | ||
export declare type OnCloseCallback = (proxyRes: Response, proxySocket: net.Socket, proxyHead: any) => void; | ||
export declare type OnOpenCallback = (proxySocket: net.Socket) => void; | ||
export {}; |
{ | ||
"name": "http-proxy-middleware", | ||
"version": "2.0.6", | ||
"description": "The one-liner node.js proxy middleware for connect, express and browser-sync", | ||
"version": "3.0.0-beta.0", | ||
"description": "The one-liner node.js proxy middleware for connect, express, next.js and more", | ||
"main": "dist/index.js", | ||
@@ -42,2 +42,3 @@ "types": "dist/index.d.ts", | ||
"polka", | ||
"next.js", | ||
"browser-sync", | ||
@@ -57,18 +58,19 @@ "gulp", | ||
"devDependencies": { | ||
"@commitlint/cli": "16.2.1", | ||
"@commitlint/cli": "16.2.3", | ||
"@commitlint/config-conventional": "16.2.1", | ||
"@types/debug": "4.1.7", | ||
"@types/express": "4.17.13", | ||
"@types/is-glob": "4.0.2", | ||
"@types/jest": "27.4.0", | ||
"@types/jest": "27.4.1", | ||
"@types/micromatch": "4.0.2", | ||
"@types/node": "17.0.18", | ||
"@types/supertest": "2.0.11", | ||
"@types/ws": "8.2.2", | ||
"@typescript-eslint/eslint-plugin": "5.12.0", | ||
"@typescript-eslint/parser": "5.12.0", | ||
"body-parser": "1.19.2", | ||
"browser-sync": "2.27.7", | ||
"@types/node": "17.0.25", | ||
"@types/supertest": "2.0.12", | ||
"@types/ws": "8.5.3", | ||
"@typescript-eslint/eslint-plugin": "5.20.0", | ||
"@typescript-eslint/parser": "5.20.0", | ||
"body-parser": "1.20.0", | ||
"browser-sync": "2.27.9", | ||
"connect": "3.7.0", | ||
"eslint": "8.9.0", | ||
"eslint-config-prettier": "8.3.0", | ||
"eslint": "8.13.0", | ||
"eslint-config-prettier": "8.5.0", | ||
"eslint-plugin-prettier": "4.0.0", | ||
@@ -79,9 +81,9 @@ "express": "4.17.3", | ||
"jest": "27.5.1", | ||
"lint-staged": "12.3.4", | ||
"mockttp": "2.6.0", | ||
"lint-staged": "12.3.8", | ||
"mockttp": "2.7.0", | ||
"open": "8.4.0", | ||
"prettier": "2.5.1", | ||
"prettier": "2.6.2", | ||
"supertest": "6.2.2", | ||
"ts-jest": "27.1.3", | ||
"typescript": "4.5.5", | ||
"ts-jest": "27.1.4", | ||
"typescript": "4.6.3", | ||
"ws": "8.5.0" | ||
@@ -91,6 +93,7 @@ }, | ||
"@types/http-proxy": "^1.17.8", | ||
"debug": "^4.3.4", | ||
"http-proxy": "^1.18.1", | ||
"is-glob": "^4.0.1", | ||
"is-plain-obj": "^3.0.0", | ||
"micromatch": "^4.0.2" | ||
"micromatch": "^4.0.5" | ||
}, | ||
@@ -105,2 +108,5 @@ "peerDependencies": { | ||
}, | ||
"resolutions": { | ||
"browser-sync/portscanner": "2.2.0" | ||
}, | ||
"engines": { | ||
@@ -107,0 +113,0 @@ "node": ">=12.0.0" |
450
README.md
@@ -8,3 +8,3 @@ # http-proxy-middleware | ||
Node.js proxying made simple. Configure proxy middleware with ease for [connect](https://github.com/senchalabs/connect), [express](https://github.com/strongloop/express), [browser-sync](https://github.com/BrowserSync/browser-sync) and [many more](#compatible-servers). | ||
Node.js proxying made simple. Configure proxy middleware with ease for [connect](https://github.com/senchalabs/connect), [express](https://github.com/expressjs/express), [next.js](https://github.com/vercel/next.js) and [many more](#compatible-servers). | ||
@@ -15,7 +15,11 @@ Powered by the popular Nodejitsu [`http-proxy`](https://github.com/nodejitsu/node-http-proxy). [![GitHub stars](https://img.shields.io/github/stars/nodejitsu/node-http-proxy.svg?style=social&label=Star)](https://github.com/nodejitsu/node-http-proxy) | ||
This page is showing documentation for version v2.x.x ([release notes](https://github.com/chimurai/http-proxy-middleware/releases)) | ||
This page is showing documentation for version v3.x.x ([release notes](https://github.com/chimurai/http-proxy-middleware/releases)) | ||
If you're looking for v0.x documentation. Go to: | ||
https://github.com/chimurai/http-proxy-middleware/tree/v0.21.0#readme | ||
See [MIGRATION.md](https://github.com/chimurai/http-proxy-middleware/blob/master/MIGRATION.md) for details on how to migrate from v2.x.x to v3.x.x | ||
If you're looking for older documentation. Go to: | ||
- <https://github.com/chimurai/http-proxy-middleware/tree/v2.0.4#readme> | ||
- <https://github.com/chimurai/http-proxy-middleware/tree/v0.21.0#readme> | ||
## TL;DR <!-- omit in toc --> | ||
@@ -25,2 +29,4 @@ | ||
:bulb: **Tip:** Set the option `changeOrigin` to `true` for [name-based virtual hosted sites](http://en.wikipedia.org/wiki/Virtual_hosting#Name-based). | ||
```javascript | ||
@@ -34,6 +40,14 @@ // javascript | ||
app.use('/api', createProxyMiddleware({ target: 'http://www.example.org', changeOrigin: true })); | ||
app.use( | ||
'/api', | ||
createProxyMiddleware({ | ||
target: 'http://www.example.org/secret', | ||
changeOrigin: true, | ||
}) | ||
); | ||
app.listen(3000); | ||
// http://localhost:3000/api/foo/bar -> http://www.example.org/api/foo/bar | ||
// proxy and change the base path from "/api" to "/secret" | ||
// http://localhost:3000/api/foo/bar -> http://www.example.org/secret/foo/bar | ||
``` | ||
@@ -49,5 +63,13 @@ | ||
app.use('/api', createProxyMiddleware({ target: 'http://www.example.org', changeOrigin: true })); | ||
app.use( | ||
'/api', | ||
createProxyMiddleware({ | ||
target: 'http://www.example.org/api', | ||
changeOrigin: true, | ||
}) | ||
); | ||
app.listen(3000); | ||
// proxy and keep the same base path "/api" | ||
// http://localhost:3000/api/foo/bar -> http://www.example.org/api/foo/bar | ||
@@ -58,16 +80,19 @@ ``` | ||
:bulb: **Tip:** Set the option `changeOrigin` to `true` for [name-based virtual hosted sites](http://en.wikipedia.org/wiki/Virtual_hosting#Name-based). | ||
## Table of Contents <!-- omit in toc --> | ||
<!-- // spell-checker:disable --> | ||
- [Install](#install) | ||
- [Core concept](#core-concept) | ||
- [Example](#example) | ||
- [Context matching](#context-matching) | ||
- [Basic usage](#basic-usage) | ||
- [Express Server Example](#express-server-example) | ||
- [app.use(path, proxy)](#appusepath-proxy) | ||
- [Options](#options) | ||
- [http-proxy-middleware options](#http-proxy-middleware-options) | ||
- [http-proxy events](#http-proxy-events) | ||
- [http-proxy options](#http-proxy-options) | ||
- [Shorthand](#shorthand) | ||
- [app.use(path, proxy)](#appusepath-proxy) | ||
- [`pathFilter` (string, []string, glob, []glob, function)](#pathfilter-string-string-glob-glob-function) | ||
- [`pathRewrite` (object/function)](#pathrewrite-objectfunction) | ||
- [`router` (object/function)](#router-objectfunction) | ||
- [`plugins` (Array)](#plugins-array) | ||
- [`ejectPlugins` (boolean) default: `false`](#ejectplugins-boolean-default-false) | ||
- [`logger` (Object)](#logger-object) | ||
- [`http-proxy` events](#http-proxy-events) | ||
- [`http-proxy` options](#http-proxy-options) | ||
- [WebSocket](#websocket) | ||
@@ -77,2 +102,3 @@ - [External WebSocket upgrade](#external-websocket-upgrade) | ||
- [Intercept and manipulate responses](#intercept-and-manipulate-responses) | ||
- [Debugging](#debugging) | ||
- [Working examples](#working-examples) | ||
@@ -85,21 +111,21 @@ - [Recipes](#recipes) | ||
<!-- // spell-checker:enable --> | ||
## Install | ||
```bash | ||
$ npm install --save-dev http-proxy-middleware | ||
```shell | ||
npm install --save-dev http-proxy-middleware | ||
``` | ||
## Core concept | ||
## Basic usage | ||
Proxy middleware configuration. | ||
Create and configure a proxy middleware with: `createProxyMiddleware(config)`. | ||
#### createProxyMiddleware([context,] config) | ||
```javascript | ||
const { createProxyMiddleware } = require('http-proxy-middleware'); | ||
const apiProxy = createProxyMiddleware('/api', { target: 'http://www.example.org' }); | ||
// \____/ \_____________________________/ | ||
// | | | ||
// context options | ||
const apiProxy = createProxyMiddleware({ | ||
target: 'http://www.example.org', | ||
changeOrigin: true, | ||
}); | ||
@@ -109,19 +135,9 @@ // 'apiProxy' is now ready to be used as middleware in a server. | ||
- **context**: Determine which requests should be proxied to the target host. | ||
(more on [context matching](#context-matching)) | ||
- **options.target**: target host to proxy to. _(protocol + host)_ | ||
- **options.changeOrigin**: for virtual hosted sites | ||
(full list of [`http-proxy-middleware` configuration options](#options)) | ||
- see full list of [`http-proxy-middleware` configuration options](#options) | ||
#### createProxyMiddleware(uri [, config]) | ||
## Express Server Example | ||
```javascript | ||
// shorthand syntax for the example above: | ||
const apiProxy = createProxyMiddleware('http://www.example.org/api'); | ||
``` | ||
More about the [shorthand configuration](#shorthand). | ||
## Example | ||
An example with `express` server. | ||
@@ -134,24 +150,15 @@ | ||
const app = express(); | ||
// proxy middleware options | ||
/** @type {import('http-proxy-middleware/dist/types').Options} */ | ||
const options = { | ||
target: 'http://www.example.org', // target host | ||
target: 'http://www.example.org/api', // target host with the same base path | ||
changeOrigin: true, // needed for virtual hosted sites | ||
ws: true, // proxy websockets | ||
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', | ||
}, | ||
}; | ||
// create the proxy (without context) | ||
// create the proxy | ||
const exampleProxy = createProxyMiddleware(options); | ||
// mount `exampleProxy` in web server | ||
const app = express(); | ||
app.use('/api', exampleProxy); | ||
@@ -161,24 +168,39 @@ app.listen(3000); | ||
## Context matching | ||
### app.use(path, proxy) | ||
Providing an alternative way to decide which requests should be proxied; In case you are not able to use the server's [`path` parameter](http://expressjs.com/en/4x/api.html#app.use) to mount the proxy or when you need more flexibility. | ||
If you want to use the server's `app.use` `path` parameter to match requests. | ||
Use `pathFilter` option to further include/exclude requests which you want to proxy. | ||
[RFC 3986 `path`](https://tools.ietf.org/html/rfc3986#section-3.3) is used for context matching. | ||
```ascii | ||
foo://example.com:8042/over/there?name=ferret#nose | ||
\_/ \______________/\_________/ \_________/ \__/ | ||
| | | | | | ||
scheme authority path query fragment | ||
```javascript | ||
app.use( | ||
createProxyMiddleware({ | ||
target: 'http://www.example.org/api', | ||
changeOrigin: true, | ||
pathFilter: '/api/proxy-only-this-path', | ||
}) | ||
); | ||
``` | ||
`app.use` documentation: | ||
- express: <http://expressjs.com/en/4x/api.html#app.use> | ||
- connect: <https://github.com/senchalabs/connect#mount-middleware> | ||
- polka: <https://github.com/lukeed/polka#usebase-fn> | ||
## Options | ||
http-proxy-middleware options: | ||
### `pathFilter` (string, []string, glob, []glob, function) | ||
Narrow down which requests should be proxied. The `path` used for filtering is the `request.url` pathname. In Express, this is the `path` relative to the mount-point of the proxy. | ||
- **path matching** | ||
- `createProxyMiddleware({...})` - matches any path, all requests will be proxied. | ||
- `createProxyMiddleware('/', {...})` - matches any path, all requests will be proxied. | ||
- `createProxyMiddleware('/api', {...})` - matches paths starting with `/api` | ||
- `createProxyMiddleware({...})` - matches any path, all requests will be proxied when `pathFilter` is not configured. | ||
- `createProxyMiddleware({ pathFilter: '/api', ...})` - matches paths starting with `/api` | ||
- **multiple path matching** | ||
- `createProxyMiddleware(['/api', '/ajax', '/someotherpath'], {...})` | ||
- `createProxyMiddleware({ pathFilter: ['/api', '/ajax', '/someotherpath'], ...})` | ||
@@ -189,8 +211,8 @@ - **wildcard path matching** | ||
- `createProxyMiddleware('**', {...})` matches any path, all requests will be proxied. | ||
- `createProxyMiddleware('**/*.html', {...})` matches any path which ends with `.html` | ||
- `createProxyMiddleware('/*.html', {...})` matches paths directly under path-absolute | ||
- `createProxyMiddleware('/api/**/*.html', {...})` matches requests ending with `.html` in the path of `/api` | ||
- `createProxyMiddleware(['/api/**', '/ajax/**'], {...})` combine multiple patterns | ||
- `createProxyMiddleware(['/api/**', '!**/bad.json'], {...})` exclusion | ||
- `createProxyMiddleware({ pathFilter: '**', ...})` matches any path, all requests will be proxied. | ||
- `createProxyMiddleware({ pathFilter: '**/*.html', ...})` matches any path which ends with `.html` | ||
- `createProxyMiddleware({ pathFilter: '/*.html', ...})` matches paths directly under path-absolute | ||
- `createProxyMiddleware({ pathFilter: '/api/**/*.html', ...})` matches requests ending with `.html` in the path of `/api` | ||
- `createProxyMiddleware({ pathFilter: ['/api/**', '/ajax/**'], ...})` combine multiple patterns | ||
- `createProxyMiddleware({ pathFilter: ['/api/**', '!**/bad.json'], ...})` exclusion | ||
@@ -207,105 +229,151 @@ **Note**: In multiple path matching, you cannot use string paths and wildcard paths together. | ||
*/ | ||
const filter = function (pathname, req) { | ||
return pathname.match('^/api') && req.method === 'GET'; | ||
const pathFilter = function (path, req) { | ||
return path.match('^/api') && req.method === 'GET'; | ||
}; | ||
const apiProxy = createProxyMiddleware(filter, { | ||
const apiProxy = createProxyMiddleware({ | ||
target: 'http://www.example.org', | ||
pathFilter: pathFilter, | ||
}); | ||
``` | ||
## Options | ||
### `pathRewrite` (object/function) | ||
### http-proxy-middleware options | ||
Rewrite target's url path. Object-keys will be used as _RegExp_ to match paths. | ||
- **option.pathRewrite**: object/function, rewrite target's url path. Object-keys will be used as _RegExp_ to match paths. | ||
```javascript | ||
// rewrite path | ||
pathRewrite: {'^/old/api' : '/new/api'} | ||
```javascript | ||
// rewrite path | ||
pathRewrite: {'^/old/api' : '/new/api'} | ||
// remove path | ||
pathRewrite: {'^/remove/api' : ''} | ||
// remove path | ||
pathRewrite: {'^/remove/api' : ''} | ||
// add base path | ||
pathRewrite: {'^/' : '/basepath/'} | ||
// add base path | ||
pathRewrite: {'^/' : '/basepath/'} | ||
// custom rewriting | ||
pathRewrite: function (path, req) { return path.replace('/api', '/base/api') } | ||
// custom rewriting | ||
pathRewrite: function (path, req) { return path.replace('/api', '/base/api') } | ||
// custom rewriting, returning Promise | ||
pathRewrite: async function (path, req) { | ||
const should_add_something = await httpRequestToDecideSomething(path); | ||
if (should_add_something) path += "something"; | ||
return path; | ||
} | ||
``` | ||
// custom rewriting, returning Promise | ||
pathRewrite: async function (path, req) { | ||
const should_add_something = await httpRequestToDecideSomething(path); | ||
if (should_add_something) path += "something"; | ||
return path; | ||
} | ||
``` | ||
### `router` (object/function) | ||
- **option.router**: object/function, re-target `option.target` for specific requests. | ||
Re-target `option.target` for specific requests. | ||
```javascript | ||
// Use `host` and/or `path` to match requests. First match will be used. | ||
// The order of the configuration matters. | ||
router: { | ||
'integration.localhost:3000' : 'http://localhost:8001', // host only | ||
'staging.localhost:3000' : 'http://localhost:8002', // host only | ||
'localhost:3000/api' : 'http://localhost:8003', // host + path | ||
'/rest' : 'http://localhost:8004' // path only | ||
} | ||
```javascript | ||
// Use `host` and/or `path` to match requests. First match will be used. | ||
// The order of the configuration matters. | ||
router: { | ||
'integration.localhost:3000' : 'http://localhost:8001', // host only | ||
'staging.localhost:3000' : 'http://localhost:8002', // host only | ||
'localhost:3000/api' : 'http://localhost:8003', // host + path | ||
'/rest' : 'http://localhost:8004' // path only | ||
} | ||
// Custom router function (string target) | ||
router: function(req) { | ||
return 'http://localhost:8004'; | ||
} | ||
// Custom router function (string target) | ||
router: function(req) { | ||
return 'http://localhost:8004'; | ||
} | ||
// Custom router function (target object) | ||
router: function(req) { | ||
return { | ||
protocol: 'https:', // The : is required | ||
host: 'localhost', | ||
port: 8004 | ||
}; | ||
} | ||
// Custom router function (target object) | ||
router: function(req) { | ||
return { | ||
protocol: 'https:', // The : is required | ||
host: 'localhost', | ||
port: 8004 | ||
}; | ||
} | ||
// Asynchronous router function which returns promise | ||
router: async function(req) { | ||
const url = await doSomeIO(); | ||
return url; | ||
} | ||
``` | ||
// Asynchronous router function which returns promise | ||
router: async function(req) { | ||
const url = await doSomeIO(); | ||
return url; | ||
} | ||
``` | ||
- **option.logLevel**: string, ['debug', 'info', 'warn', 'error', 'silent']. Default: `'info'` | ||
### `plugins` (Array) | ||
- **option.logProvider**: function, modify or replace log provider. Default: `console`. | ||
```js | ||
const simpleRequestLogger = (proxyServer, options) => { | ||
proxyServer.on('proxyReq', (proxyReq, req, res) => { | ||
console.log(`[HPM] [${req.method}] ${req.url}`); // outputs: [HPM] GET /users | ||
}); | ||
}, | ||
```javascript | ||
// simple replace | ||
function logProvider(provider) { | ||
// replace the default console log provider. | ||
return require('winston'); | ||
} | ||
``` | ||
const config = { | ||
target: `http://example.org`, | ||
changeOrigin: true, | ||
plugins: [simpleRequestLogger], | ||
}; | ||
``` | ||
```javascript | ||
// verbose replacement | ||
function logProvider(provider) { | ||
const logger = new (require('winston').Logger)(); | ||
### `ejectPlugins` (boolean) default: `false` | ||
const myCustomProvider = { | ||
log: logger.log, | ||
debug: logger.debug, | ||
info: logger.info, | ||
warn: logger.warn, | ||
error: logger.error, | ||
}; | ||
return myCustomProvider; | ||
} | ||
``` | ||
If you're not satisfied with the pre-configured plugins, you can eject them by configuring `ejectPlugins: true`. | ||
### http-proxy events | ||
NOTE: register your own error handlers to prevent server from crashing. | ||
Subscribe to [http-proxy events](https://github.com/nodejitsu/node-http-proxy#listening-for-proxy-events): | ||
```js | ||
// eject default plugins and manually add them back | ||
- **option.onError**: function, subscribe to http-proxy's `error` event for custom error handling. | ||
const { | ||
debugProxyErrorsPlugin, // subscribe to proxy errors to prevent server from crashing | ||
loggerPlugin, // log proxy events to a logger (ie. console) | ||
errorResponsePlugin, // return 5xx response on proxy error | ||
proxyEventsPlugin, // implements the "on:" option | ||
} = require('http-proxy-middleware'); | ||
createProxyMiddleware({ | ||
target: `http://example.org`, | ||
changeOrigin: true, | ||
ejectPlugins: true, | ||
plugins: [debugProxyErrorsPlugin, loggerPlugin, errorResponsePlugin, proxyEventsPlugin], | ||
}); | ||
``` | ||
### `logger` (Object) | ||
Configure a logger to output information from http-proxy-middleware: ie. `console`, `winston`, `pino`, `bunyan`, `log4js`, etc... | ||
Only `info`, `warn`, `error` are used internally for compatibility across different loggers. | ||
If you use `winston`, make sure to enable interpolation: <https://github.com/winstonjs/winston#string-interpolation> | ||
See also logger recipes ([recipes/logger.md](https://github.com/chimurai/http-proxy-middleware/blob/master/recipes/logger.md)) for more details. | ||
```javascript | ||
createProxyMiddleware({ | ||
logger: console, | ||
}); | ||
``` | ||
## `http-proxy` events | ||
Subscribe to [http-proxy events](https://github.com/nodejitsu/node-http-proxy#listening-for-proxy-events) with the `on` option: | ||
```js | ||
createProxyMiddleware({ | ||
target: 'http://www.example.org', | ||
on: { | ||
proxyReq: (proxyReq, req, res) => { | ||
/* handle proxyReq */ | ||
}, | ||
proxyRes: (proxyRes, req, res) => { | ||
/* handle proxyRes */ | ||
}, | ||
error: (err, req, res) => { | ||
/* handle error */ | ||
}, | ||
}, | ||
}); | ||
``` | ||
- **option.on.error**: function, subscribe to http-proxy's `error` event for custom error handling. | ||
```javascript | ||
@@ -320,3 +388,3 @@ function onError(err, req, res, target) { | ||
- **option.onProxyRes**: function, subscribe to http-proxy's `proxyRes` event. | ||
- **option.on.proxyRes**: function, subscribe to http-proxy's `proxyRes` event. | ||
@@ -330,3 +398,3 @@ ```javascript | ||
- **option.onProxyReq**: function, subscribe to http-proxy's `proxyReq` event. | ||
- **option.on.proxyReq**: function, subscribe to http-proxy's `proxyReq` event. | ||
@@ -341,3 +409,3 @@ ```javascript | ||
- **option.onProxyReqWs**: function, subscribe to http-proxy's `proxyReqWs` event. | ||
- **option.on.proxyReqWs**: function, subscribe to http-proxy's `proxyReqWs` event. | ||
@@ -351,3 +419,3 @@ ```javascript | ||
- **option.onOpen**: function, subscribe to http-proxy's `open` event. | ||
- **option.on.open**: function, subscribe to http-proxy's `open` event. | ||
@@ -361,3 +429,3 @@ ```javascript | ||
- **option.onClose**: function, subscribe to http-proxy's `close` event. | ||
- **option.on.close**: function, subscribe to http-proxy's `close` event. | ||
@@ -371,3 +439,3 @@ ```javascript | ||
### http-proxy options | ||
## `http-proxy` options | ||
@@ -394,2 +462,3 @@ The following options are provided by the underlying [http-proxy](https://github.com/nodejitsu/node-http-proxy#options) library. | ||
- **option.cookieDomainRewrite**: rewrites domain of `set-cookie` headers. Possible values: | ||
- `false` (default): disable cookie rewriting | ||
@@ -399,2 +468,3 @@ - String: new domain, for example `cookieDomainRewrite: "new.domain"`. To remove the domain, use `cookieDomainRewrite: ""`. | ||
For example keep one domain unchanged, rewrite one domain and remove other domains: | ||
```json | ||
@@ -407,3 +477,5 @@ cookieDomainRewrite: { | ||
``` | ||
- **option.cookiePathRewrite**: rewrites path of `set-cookie` headers. Possible values: | ||
- `false` (default): disable cookie rewriting | ||
@@ -413,2 +485,3 @@ - String: new path, for example `cookiePathRewrite: "/newPath/"`. To remove the path, use `cookiePathRewrite: ""`. To set path to root use `cookiePathRewrite: "/"`. | ||
For example, to keep one path unchanged, rewrite one path and remove other paths: | ||
```json | ||
@@ -421,2 +494,3 @@ cookiePathRewrite: { | ||
``` | ||
- **option.headers**: object, adds [request headers](https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Request_fields). (Example: `{host:'www.example.org'}`) | ||
@@ -449,32 +523,2 @@ - **option.proxyTimeout**: timeout (in millis) when proxy receives no response from target | ||
## Shorthand | ||
Use the shorthand syntax when verbose configuration is not needed. The `context` and `option.target` will be automatically configured when shorthand is used. Options can still be used if needed. | ||
```javascript | ||
createProxyMiddleware('http://www.example.org:8000/api'); | ||
// createProxyMiddleware('/api', {target: 'http://www.example.org:8000'}); | ||
createProxyMiddleware('http://www.example.org:8000/api/books/*/**.json'); | ||
// createProxyMiddleware('/api/books/*/**.json', {target: 'http://www.example.org:8000'}); | ||
createProxyMiddleware('http://www.example.org:8000/api', { changeOrigin: true }); | ||
// createProxyMiddleware('/api', {target: 'http://www.example.org:8000', changeOrigin: true}); | ||
``` | ||
### app.use(path, proxy) | ||
If you want to use the server's `app.use` `path` parameter to match requests; | ||
Create and mount the proxy without the http-proxy-middleware `context` parameter: | ||
```javascript | ||
app.use('/api', createProxyMiddleware({ target: 'http://www.example.org', changeOrigin: true })); | ||
``` | ||
`app.use` documentation: | ||
- express: http://expressjs.com/en/4x/api.html#app.use | ||
- connect: https://github.com/senchalabs/connect#mount-middleware | ||
- polka: https://github.com/lukeed/polka#usebase-fn | ||
## WebSocket | ||
@@ -484,9 +528,3 @@ | ||
// verbose api | ||
createProxyMiddleware('/', { target: 'http://echo.websocket.org', ws: true }); | ||
// shorthand | ||
createProxyMiddleware('http://echo.websocket.org', { ws: true }); | ||
// shorter shorthand | ||
createProxyMiddleware('ws://echo.websocket.org'); | ||
createProxyMiddleware({ pathFilter: '/', target: 'http://echo.websocket.org', ws: true }); | ||
``` | ||
@@ -499,3 +537,3 @@ | ||
```javascript | ||
const wsProxy = createProxyMiddleware('ws://echo.websocket.org', { changeOrigin: true }); | ||
const wsProxy = createProxyMiddleware({ target: 'ws://echo.websocket.org', changeOrigin: true }); | ||
@@ -524,3 +562,5 @@ const app = express(); | ||
**/ | ||
onProxyReq: fixRequestBody, | ||
on: { | ||
proxyReq: fixRequestBody, | ||
}, | ||
}); | ||
@@ -553,6 +593,8 @@ ``` | ||
**/ | ||
onProxyRes: responseInterceptor(async (responseBuffer, proxyRes, req, res) => { | ||
const response = responseBuffer.toString('utf8'); // convert buffer to string | ||
return response.replace('Hello', 'Goodbye'); // manipulate response and return the result | ||
}), | ||
on: { | ||
proxyRes: responseInterceptor(async (responseBuffer, proxyRes, req, res) => { | ||
const response = responseBuffer.toString('utf8'); // convert buffer to string | ||
return response.replace('Hello', 'Goodbye'); // manipulate response and return the result | ||
}), | ||
}, | ||
}); | ||
@@ -563,2 +605,15 @@ ``` | ||
## Debugging | ||
Configure the `DEBUG` environment variable enable debug logging. | ||
See [`debug`](https://github.com/debug-js/debug#readme) project for more options. | ||
```shell | ||
DEBUG=http-proxy-middleware* node server.js | ||
$ http-proxy-middleware proxy created +0ms | ||
$ http-proxy-middleware proxying request to target: 'http://www.example.org' +359ms | ||
``` | ||
## Working examples | ||
@@ -584,2 +639,3 @@ | ||
- [express](https://www.npmjs.com/package/express) | ||
- [next.js](https://www.npmjs.com/package/next) | ||
- [fastify](https://www.npmjs.com/package/fastify) | ||
@@ -586,0 +642,0 @@ - [browser-sync](https://www.npmjs.com/package/browser-sync) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
76518
57
1273
655
7
30
1
2
+ Addeddebug@^4.3.4
+ Addeddebug@4.3.7(transitive)
+ Addedms@2.1.3(transitive)
Updatedmicromatch@^4.0.5