diff --git a/README.md b/README.md index 95a37684..3bb1c9c5 100644 --- a/README.md +++ b/README.md @@ -228,7 +228,6 @@ Providing an alternative way to decide which requests should be proxied; In case if (should_add_something) path += "something"; return path; } - ``` - **option.router**: object/function, re-target `option.target` for specific requests. @@ -243,11 +242,20 @@ Providing an alternative way to decide which requests should be proxied; In case '/rest' : 'http://localhost:8004' // path only } - // Custom router function + // 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 + }; + } + // Asynchronous router function which returns promise router: async function(req) { const url = await doSomeIO(); diff --git a/src/types.ts b/src/types.ts index d735606c..e845cf81 100644 --- a/src/types.ts +++ b/src/types.ts @@ -18,14 +18,14 @@ export interface Options extends httpProxy.ServerOptions { | ((path: string, req: Request) => string) | ((path: string, req: Request) => Promise); router?: - | { [hostOrPath: string]: string } - | ((req: Request) => string) - | ((req: Request) => Promise); + | { [hostOrPath: string]: httpProxy.ServerOptions['target'] } + | ((req: Request) => httpProxy.ServerOptions['target']) + | ((req: Request) => Promise); logLevel?: 'debug' | 'info' | 'warn' | 'error' | 'silent'; logProvider?(provider: LogProvider): LogProvider; onError?(err: Error, req: Request, res: Response): void; - onProxyRes?(proxyRes: http.ServerResponse, req: Request, res: Response): void; + onProxyRes?(proxyRes: http.IncomingMessage, req: Request, res: Response): void; onProxyReq?(proxyReq: http.ClientRequest, req: Request, res: Response): void; onProxyReqWs?( proxyReq: http.ClientRequest, diff --git a/test/e2e/router.spec.ts b/test/e2e/router.spec.ts index ee25c995..bca9cae1 100644 --- a/test/e2e/router.spec.ts +++ b/test/e2e/router.spec.ts @@ -1,6 +1,10 @@ import { createProxyMiddleware, createApp, createAppWithPath } from './_utils'; import * as request from 'supertest'; -import { getLocal, Mockttp } from 'mockttp'; +import { getLocal, generateCACertificate, Mockttp } from 'mockttp'; + +const untrustedCACert = generateCACertificate({ bits: 1024 }); + +process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; describe('E2E router', () => { let targetServerA: Mockttp; @@ -8,17 +12,33 @@ describe('E2E router', () => { let targetServerC: Mockttp; beforeEach(async () => { - targetServerA = getLocal(); - targetServerB = getLocal(); - targetServerC = getLocal(); + targetServerA = getLocal({ https: await untrustedCACert }); + targetServerB = getLocal({ https: await untrustedCACert }); + targetServerC = getLocal({ https: await untrustedCACert }); + + await targetServerA + .anyRequest() + .thenPassThrough({ ignoreHostCertificateErrors: ['localhost'] }); + await targetServerB + .anyRequest() + .thenPassThrough({ ignoreHostCertificateErrors: ['localhost'] }); + await targetServerC + .anyRequest() + .thenPassThrough({ ignoreHostCertificateErrors: ['localhost'] }); + + await targetServerA + .anyRequest() + .thenCallback(({ protocol }) => ({ body: protocol === 'https' ? 'A' : 'NOT HTTPS A' })); + await targetServerB + .anyRequest() + .thenCallback(({ protocol }) => ({ body: protocol === 'https' ? 'B' : 'NOT HTTPS B' })); + await targetServerC + .anyRequest() + .thenCallback(({ protocol }) => ({ body: protocol === 'https' ? 'C' : 'NOT HTTPS C' })); await targetServerA.start(6001); await targetServerB.start(6002); await targetServerC.start(6003); - - targetServerA.get().thenReply(200, 'A'); - targetServerB.get().thenReply(200, 'B'); - targetServerC.get().thenReply(200, 'C'); }); afterEach(async () => { @@ -27,13 +47,50 @@ describe('E2E router', () => { await targetServerC.stop(); }); - describe('router with proxyTable', () => { - it('should proxy to: "localhost:6003/api"', async () => { + describe('router with req', () => { + it('should work with a string', async () => { + const app = createApp( + createProxyMiddleware({ + target: 'https://localhost:6001', + secure: false, + changeOrigin: true, + router(req) { + return 'https://localhost:6003'; + } + }) + ); + + const agent = request(app); + const response = await agent.get('/api').expect(200); + expect(response.text).toBe('C'); + }); + + it('should work with an object', async () => { const app = createApp( createProxyMiddleware({ - target: `http://localhost:6001`, + target: 'https://localhost:6001', + secure: false, + changeOrigin: true, router(req) { - return 'http://localhost:6003'; + return { host: 'localhost', port: 6003, protocol: 'https:' }; + } + }) + ); + const agent = request(app); + const response = await agent.get('/api').expect(200); + expect(response.text).toBe('C'); + }); + + it('should work with an async callback', async () => { + const app = createApp( + createProxyMiddleware({ + target: 'https://localhost:6001', + secure: false, + changeOrigin: true, + router: async req => { + return new Promise(resolve => + resolve({ host: 'localhost', port: 6003, protocol: 'https:' }) + ); } }) ); @@ -42,6 +99,25 @@ describe('E2E router', () => { const response = await agent.get('/api').expect(200); expect(response.text).toBe('C'); }); + + it('missing a : will cause it to use http', async () => { + const app = createApp( + createProxyMiddleware({ + target: 'https://localhost:6001', + secure: false, + changeOrigin: true, + router: async req => { + return new Promise(resolve => + resolve({ host: 'localhost', port: 6003, protocol: 'https' }) + ); + } + }) + ); + + const agent = request(app); + const response = await agent.get('/api').expect(200); + expect(response.text).toBe('NOT HTTPS C'); + }); }); describe('router with proxyTable', () => { @@ -51,11 +127,13 @@ describe('E2E router', () => { const app = createAppWithPath( '/', createProxyMiddleware({ - target: 'http://localhost:6001', + target: 'https://localhost:6001', + secure: false, + changeOrigin: true, router: { - 'alpha.localhost:6000': 'http://localhost:6001', - 'beta.localhost:6000': 'http://localhost:6002', - 'localhost:6000/api': 'http://localhost:6003' + 'alpha.localhost:6000': 'https://localhost:6001', + 'beta.localhost:6000': 'https://localhost:6002', + 'localhost:6000/api': 'https://localhost:6003' } }) );