Skip to content

Commit 6d30314

Browse files
authored
fix(typescript): fix proxyRes and router types (chimurai#410)
1 parent 47608aa commit 6d30314

File tree

3 files changed

+108
-22
lines changed

3 files changed

+108
-22
lines changed

README.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,6 @@ Providing an alternative way to decide which requests should be proxied; In case
228228
if (should_add_something) path += "something";
229229
return path;
230230
}
231-
232231
```
233232

234233
- **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
243242
'/rest' : 'http://localhost:8004' // path only
244243
}
245244

246-
// Custom router function
245+
// Custom router function (string target)
247246
router: function(req) {
248247
return 'http://localhost:8004';
249248
}
250249

250+
// Custom router function (target object)
251+
router: function(req) {
252+
return {
253+
protocol: 'https:', // The : is required
254+
host: 'localhost',
255+
port: 8004
256+
};
257+
}
258+
251259
// Asynchronous router function which returns promise
252260
router: async function(req) {
253261
const url = await doSomeIO();

src/types.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,14 @@ export interface Options extends httpProxy.ServerOptions {
1818
| ((path: string, req: Request) => string)
1919
| ((path: string, req: Request) => Promise<string>);
2020
router?:
21-
| { [hostOrPath: string]: string }
22-
| ((req: Request) => string)
23-
| ((req: Request) => Promise<string>);
21+
| { [hostOrPath: string]: httpProxy.ServerOptions['target'] }
22+
| ((req: Request) => httpProxy.ServerOptions['target'])
23+
| ((req: Request) => Promise<httpProxy.ServerOptions['target']>);
2424
logLevel?: 'debug' | 'info' | 'warn' | 'error' | 'silent';
2525
logProvider?(provider: LogProvider): LogProvider;
2626

2727
onError?(err: Error, req: Request, res: Response): void;
28-
onProxyRes?(proxyRes: http.ServerResponse, req: Request, res: Response): void;
28+
onProxyRes?(proxyRes: http.IncomingMessage, req: Request, res: Response): void;
2929
onProxyReq?(proxyReq: http.ClientRequest, req: Request, res: Response): void;
3030
onProxyReqWs?(
3131
proxyReq: http.ClientRequest,

test/e2e/router.spec.ts

Lines changed: 94 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,44 @@
11
import { createProxyMiddleware, createApp, createAppWithPath } from './_utils';
22
import * as request from 'supertest';
3-
import { getLocal, Mockttp } from 'mockttp';
3+
import { getLocal, generateCACertificate, Mockttp } from 'mockttp';
4+
5+
const untrustedCACert = generateCACertificate({ bits: 1024 });
6+
7+
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
48

59
describe('E2E router', () => {
610
let targetServerA: Mockttp;
711
let targetServerB: Mockttp;
812
let targetServerC: Mockttp;
913

1014
beforeEach(async () => {
11-
targetServerA = getLocal();
12-
targetServerB = getLocal();
13-
targetServerC = getLocal();
15+
targetServerA = getLocal({ https: await untrustedCACert });
16+
targetServerB = getLocal({ https: await untrustedCACert });
17+
targetServerC = getLocal({ https: await untrustedCACert });
18+
19+
await targetServerA
20+
.anyRequest()
21+
.thenPassThrough({ ignoreHostCertificateErrors: ['localhost'] });
22+
await targetServerB
23+
.anyRequest()
24+
.thenPassThrough({ ignoreHostCertificateErrors: ['localhost'] });
25+
await targetServerC
26+
.anyRequest()
27+
.thenPassThrough({ ignoreHostCertificateErrors: ['localhost'] });
28+
29+
await targetServerA
30+
.anyRequest()
31+
.thenCallback(({ protocol }) => ({ body: protocol === 'https' ? 'A' : 'NOT HTTPS A' }));
32+
await targetServerB
33+
.anyRequest()
34+
.thenCallback(({ protocol }) => ({ body: protocol === 'https' ? 'B' : 'NOT HTTPS B' }));
35+
await targetServerC
36+
.anyRequest()
37+
.thenCallback(({ protocol }) => ({ body: protocol === 'https' ? 'C' : 'NOT HTTPS C' }));
1438

1539
await targetServerA.start(6001);
1640
await targetServerB.start(6002);
1741
await targetServerC.start(6003);
18-
19-
targetServerA.get().thenReply(200, 'A');
20-
targetServerB.get().thenReply(200, 'B');
21-
targetServerC.get().thenReply(200, 'C');
2242
});
2343

2444
afterEach(async () => {
@@ -27,13 +47,50 @@ describe('E2E router', () => {
2747
await targetServerC.stop();
2848
});
2949

30-
describe('router with proxyTable', () => {
31-
it('should proxy to: "localhost:6003/api"', async () => {
50+
describe('router with req', () => {
51+
it('should work with a string', async () => {
52+
const app = createApp(
53+
createProxyMiddleware({
54+
target: 'https://localhost:6001',
55+
secure: false,
56+
changeOrigin: true,
57+
router(req) {
58+
return 'https://localhost:6003';
59+
}
60+
})
61+
);
62+
63+
const agent = request(app);
64+
const response = await agent.get('/api').expect(200);
65+
expect(response.text).toBe('C');
66+
});
67+
68+
it('should work with an object', async () => {
3269
const app = createApp(
3370
createProxyMiddleware({
34-
target: `http://localhost:6001`,
71+
target: 'https://localhost:6001',
72+
secure: false,
73+
changeOrigin: true,
3574
router(req) {
36-
return 'http://localhost:6003';
75+
return { host: 'localhost', port: 6003, protocol: 'https:' };
76+
}
77+
})
78+
);
79+
const agent = request(app);
80+
const response = await agent.get('/api').expect(200);
81+
expect(response.text).toBe('C');
82+
});
83+
84+
it('should work with an async callback', async () => {
85+
const app = createApp(
86+
createProxyMiddleware({
87+
target: 'https://localhost:6001',
88+
secure: false,
89+
changeOrigin: true,
90+
router: async req => {
91+
return new Promise(resolve =>
92+
resolve({ host: 'localhost', port: 6003, protocol: 'https:' })
93+
);
3794
}
3895
})
3996
);
@@ -42,6 +99,25 @@ describe('E2E router', () => {
4299
const response = await agent.get('/api').expect(200);
43100
expect(response.text).toBe('C');
44101
});
102+
103+
it('missing a : will cause it to use http', async () => {
104+
const app = createApp(
105+
createProxyMiddleware({
106+
target: 'https://localhost:6001',
107+
secure: false,
108+
changeOrigin: true,
109+
router: async req => {
110+
return new Promise(resolve =>
111+
resolve({ host: 'localhost', port: 6003, protocol: 'https' })
112+
);
113+
}
114+
})
115+
);
116+
117+
const agent = request(app);
118+
const response = await agent.get('/api').expect(200);
119+
expect(response.text).toBe('NOT HTTPS C');
120+
});
45121
});
46122

47123
describe('router with proxyTable', () => {
@@ -51,11 +127,13 @@ describe('E2E router', () => {
51127
const app = createAppWithPath(
52128
'/',
53129
createProxyMiddleware({
54-
target: 'http://localhost:6001',
130+
target: 'https://localhost:6001',
131+
secure: false,
132+
changeOrigin: true,
55133
router: {
56-
'alpha.localhost:6000': 'http://localhost:6001',
57-
'beta.localhost:6000': 'http://localhost:6002',
58-
'localhost:6000/api': 'http://localhost:6003'
134+
'alpha.localhost:6000': 'https://localhost:6001',
135+
'beta.localhost:6000': 'https://localhost:6002',
136+
'localhost:6000/api': 'https://localhost:6003'
59137
}
60138
})
61139
);

0 commit comments

Comments
 (0)