diff --git a/README.md b/README.md index 8690b9e6..ef2f1838 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,7 @@ _All_ `http-proxy` [options](https://github.com/nodejitsu/node-http-proxy#option - [`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) - [`logLevel` (string)](#loglevel-string) - [`logProvider` (function)](#logprovider-function) - [`http-proxy` events](#http-proxy-events) @@ -270,6 +271,22 @@ router: async function(req) { } ``` +### `plugins` (Array) + +```js +const simpleRequestLogger = (proxy, options) => { + proxy.on('proxyReq', (proxyReq, req, res) => { + console.log(`[HPM] [${req.method}] ${req.url}`); // outputs: [HPM] GET /users + }); +}, + +const config = { + target: `http://example.org`, + changeOrigin: true, + plugins: [simpleRequestLogger], +}; +``` + ### `logLevel` (string) Default: `'info'` diff --git a/src/http-proxy-middleware.ts b/src/http-proxy-middleware.ts index 061bec46..844b8f23 100644 --- a/src/http-proxy-middleware.ts +++ b/src/http-proxy-middleware.ts @@ -25,6 +25,8 @@ export class HttpProxyMiddleware { this.proxy = httpProxy.createProxyServer({}); this.logger.info(`[HPM] Proxy created: ${options.pathFilter ?? '/'} -> ${options.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 @@ -79,6 +81,10 @@ export class HttpProxyMiddleware { } }; + private registerPlugins(proxy: httpProxy, options: Options) { + (options.plugins ?? []).forEach((plugin) => plugin(proxy, options)); + } + private catchUpgradeRequest = (server: https.Server) => { if (!this.wsInternalSubscribed) { server.on('upgrade', this.handleUpgrade); diff --git a/src/types.ts b/src/types.ts index 523abb74..e3b845ec 100644 --- a/src/types.ts +++ b/src/types.ts @@ -21,6 +21,8 @@ export interface RequestHandler { export type Filter = string | string[] | ((pathname: string, req: Request) => boolean); +export type Plugin = (proxy: httpProxy, options: Options) => void; + export interface Options extends httpProxy.ServerOptions { /** * Narrow down requests to proxy or not. @@ -32,6 +34,10 @@ export interface Options extends httpProxy.ServerOptions { | { [regexp: string]: string } | ((path: string, req: Request) => string) | ((path: string, req: Request) => Promise); + /** + * Plugins have access to the internal http-proxy instance to customize behavior. + */ + plugins?: Plugin[]; router?: | { [hostOrPath: string]: httpProxy.ServerOptions['target'] } | ((req: Request) => httpProxy.ServerOptions['target']) diff --git a/test/e2e/plugins.spec.ts b/test/e2e/plugins.spec.ts new file mode 100644 index 00000000..fc909ac8 --- /dev/null +++ b/test/e2e/plugins.spec.ts @@ -0,0 +1,40 @@ +import { createProxyMiddleware, createApp } from './test-kit'; +import * as request from 'supertest'; +import { getLocal, Mockttp } from 'mockttp'; +import type { Options, Plugin } from '../../src/types'; + +describe('E2E Plugins', () => { + let mockTargetServer: Mockttp; + + beforeEach(async () => { + mockTargetServer = getLocal(); + await mockTargetServer.start(); + }); + + afterEach(async () => { + await mockTargetServer.stop(); + }); + + it('should register a plugin and access the http-proxy object', async () => { + let proxyReqUrl: string; + + mockTargetServer.forGet('/users/1').thenReply(200, '{"userName":"John"}'); + + const simplePlugin: Plugin = (proxy) => { + proxy.on('proxyReq', (proxyReq, req, res) => (proxyReqUrl = req.url)); + }; + + const config: Options = { + target: `http://localhost:${mockTargetServer.port}`, + plugins: [simplePlugin], // register a plugin + }; + const proxyMiddleware = createProxyMiddleware(config); + const app = createApp(proxyMiddleware); + const agent = request(app); + + const response = await agent.get('/users/1').expect(200); + + expect(proxyReqUrl).toBe('/users/1'); + expect(response.text).toBe('{"userName":"John"}'); + }); +});