http-proxy-middleware 1.2.1-alpha.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/README.md +22 -2
- package/dist/handlers/fix-request-body.d.ts +7 -0
- package/dist/handlers/fix-request-body.js +25 -0
- package/dist/handlers/public.d.ts +1 -0
- package/dist/handlers/public.js +3 -1
- package/dist/handlers/response-interceptor.js +19 -34
- package/dist/http-proxy-middleware.js +16 -25
- package/dist/router.js +10 -21
- package/package.json +6 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [v2.0.0](https://github.com/chimurai/http-proxy-middleware/releases/tag/v2.0.0)
|
|
4
|
+
|
|
5
|
+
- chore(package): drop node 10 [BREAKING CHANGE] ([#577](https://github.com/chimurai/http-proxy-middleware/pull/577))
|
|
6
|
+
|
|
7
|
+
## [v1.3.1](https://github.com/chimurai/http-proxy-middleware/releases/tag/v1.3.1)
|
|
8
|
+
|
|
9
|
+
- fix(fix-request-body): make sure the content-type exists ([#578](https://github.com/chimurai/http-proxy-middleware/pull/578)) ([oufeng](https://github.com/oufeng))
|
|
10
|
+
|
|
11
|
+
## [v1.3.0](https://github.com/chimurai/http-proxy-middleware/releases/tag/v1.3.0)
|
|
12
|
+
|
|
13
|
+
- docs(response interceptor): align with nodejs default utf8 ([#567](https://github.com/chimurai/http-proxy-middleware/pull/567))
|
|
14
|
+
- feat: try to proxy body even after body-parser middleware ([#492](https://github.com/chimurai/http-proxy-middleware/pull/492)) ([midgleyc](https://github.com/midgleyc))
|
|
15
|
+
|
|
16
|
+
## [v1.2.1](https://github.com/chimurai/http-proxy-middleware/releases/tag/v1.2.1)
|
|
17
|
+
|
|
18
|
+
- fix(response interceptor): proxy original response headers ([#563](https://github.com/chimurai/http-proxy-middleware/pull/563))
|
|
19
|
+
|
|
3
20
|
## [v1.2.0](https://github.com/chimurai/http-proxy-middleware/releases/tag/v1.2.0)
|
|
4
21
|
|
|
5
22
|
- feat(handler): response interceptor ([#520](https://github.com/chimurai/http-proxy-middleware/pull/520))
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# http-proxy-middleware
|
|
2
2
|
|
|
3
|
-
[](https://img.shields.io/github/workflow/status/chimurai/http-proxy-middleware/CI/master?style=flat-square)](https://github.com/chimurai/http-proxy-middleware/actions?query=branch%3Amaster)
|
|
4
4
|
[](https://coveralls.io/r/chimurai/http-proxy-middleware)
|
|
5
5
|
[](https://david-dm.org/chimurai/http-proxy-middleware#info=dependencies)
|
|
6
6
|
[](https://snyk.io/test/npm/http-proxy-middleware)
|
|
@@ -69,6 +69,7 @@ _All_ `http-proxy` [options](https://github.com/nodejitsu/node-http-proxy#option
|
|
|
69
69
|
- [app.use\(path, proxy\)](#appusepath-proxy)
|
|
70
70
|
- [WebSocket](#websocket)
|
|
71
71
|
- [External WebSocket upgrade](#external-websocket-upgrade)
|
|
72
|
+
- [Intercept and manipulate requests](#intercept-and-manipulate-requests)
|
|
72
73
|
- [Intercept and manipulate responses](#intercept-and-manipulate-responses)
|
|
73
74
|
- [Working examples](#working-examples)
|
|
74
75
|
- [Recipes](#recipes)
|
|
@@ -482,6 +483,25 @@ const server = app.listen(3000);
|
|
|
482
483
|
server.on('upgrade', wsProxy.upgrade); // <-- subscribe to http 'upgrade'
|
|
483
484
|
```
|
|
484
485
|
|
|
486
|
+
## Intercept and manipulate requests
|
|
487
|
+
|
|
488
|
+
Intercept requests from downstream by defining `onProxyReq` in `createProxyMiddleware`.
|
|
489
|
+
|
|
490
|
+
Currently the only pre-provided request interceptor is `fixRequestBody`, which is used to fix proxied POST requests when `bodyParser` is applied before this middleware.
|
|
491
|
+
|
|
492
|
+
Example:
|
|
493
|
+
|
|
494
|
+
```javascript
|
|
495
|
+
const { createProxyMiddleware, fixRequestBody } = require('http-proxy-middleware');
|
|
496
|
+
|
|
497
|
+
const proxy = createProxyMiddleware({
|
|
498
|
+
/**
|
|
499
|
+
* Fix bodyParser
|
|
500
|
+
**/
|
|
501
|
+
onProxyReq: fixRequestBody,
|
|
502
|
+
});
|
|
503
|
+
```
|
|
504
|
+
|
|
485
505
|
## Intercept and manipulate responses
|
|
486
506
|
|
|
487
507
|
Intercept responses from upstream with `responseInterceptor`. (Make sure to set `selfHandleResponse: true`)
|
|
@@ -507,7 +527,7 @@ const proxy = createProxyMiddleware({
|
|
|
507
527
|
* Intercept response and replace 'Hello' with 'Goodbye'
|
|
508
528
|
**/
|
|
509
529
|
onProxyRes: responseInterceptor(async (responseBuffer, proxyRes, req, res) => {
|
|
510
|
-
const response = responseBuffer.toString('
|
|
530
|
+
const response = responseBuffer.toString('utf8'); // convert buffer to string
|
|
511
531
|
return response.replace('Hello', 'Goodbye'); // manipulate response and return the result
|
|
512
532
|
}),
|
|
513
533
|
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.fixRequestBody = void 0;
|
|
4
|
+
const querystring = require("querystring");
|
|
5
|
+
/**
|
|
6
|
+
* Fix proxied body if bodyParser is involved.
|
|
7
|
+
*/
|
|
8
|
+
function fixRequestBody(proxyReq, req) {
|
|
9
|
+
if (!req.body || !Object.keys(req.body).length) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
const contentType = proxyReq.getHeader('Content-Type');
|
|
13
|
+
const writeBody = (bodyData) => {
|
|
14
|
+
// deepcode ignore ContentLengthInCode: bodyParser fix
|
|
15
|
+
proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData));
|
|
16
|
+
proxyReq.write(bodyData);
|
|
17
|
+
};
|
|
18
|
+
if (contentType && contentType.includes('application/json')) {
|
|
19
|
+
writeBody(JSON.stringify(req.body));
|
|
20
|
+
}
|
|
21
|
+
if (contentType === 'application/x-www-form-urlencoded') {
|
|
22
|
+
writeBody(querystring.stringify(req.body));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
exports.fixRequestBody = fixRequestBody;
|
package/dist/handlers/public.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.responseInterceptor = void 0;
|
|
3
|
+
exports.fixRequestBody = exports.responseInterceptor = void 0;
|
|
4
4
|
var response_interceptor_1 = require("./response-interceptor");
|
|
5
5
|
Object.defineProperty(exports, "responseInterceptor", { enumerable: true, get: function () { return response_interceptor_1.responseInterceptor; } });
|
|
6
|
+
var fix_request_body_1 = require("./fix-request-body");
|
|
7
|
+
Object.defineProperty(exports, "fixRequestBody", { enumerable: true, get: function () { return fix_request_body_1.fixRequestBody; } });
|
|
@@ -1,13 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
3
|
exports.responseInterceptor = void 0;
|
|
13
4
|
const zlib = require("zlib");
|
|
@@ -19,27 +10,25 @@ const zlib = require("zlib");
|
|
|
19
10
|
* NOTE: must set options.selfHandleResponse=true (prevent automatic call of res.end())
|
|
20
11
|
*/
|
|
21
12
|
function responseInterceptor(interceptor) {
|
|
22
|
-
return function proxyRes(proxyRes, req, res) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
res.end(`Error fetching proxied request: ${error.message}`);
|
|
42
|
-
});
|
|
13
|
+
return async function proxyRes(proxyRes, req, res) {
|
|
14
|
+
const originalProxyRes = proxyRes;
|
|
15
|
+
let buffer = Buffer.from('', 'utf8');
|
|
16
|
+
// decompress proxy response
|
|
17
|
+
const _proxyRes = decompress(proxyRes, proxyRes.headers['content-encoding']);
|
|
18
|
+
// concat data stream
|
|
19
|
+
_proxyRes.on('data', (chunk) => (buffer = Buffer.concat([buffer, chunk])));
|
|
20
|
+
_proxyRes.on('end', async () => {
|
|
21
|
+
// copy original headers
|
|
22
|
+
copyHeaders(proxyRes, res);
|
|
23
|
+
// call interceptor with intercepted response (buffer)
|
|
24
|
+
const interceptedBuffer = Buffer.from(await interceptor(buffer, originalProxyRes, req, res));
|
|
25
|
+
// set correct content-length (with double byte character support)
|
|
26
|
+
res.setHeader('content-length', Buffer.byteLength(interceptedBuffer, 'utf8'));
|
|
27
|
+
res.write(interceptedBuffer);
|
|
28
|
+
res.end();
|
|
29
|
+
});
|
|
30
|
+
_proxyRes.on('error', (error) => {
|
|
31
|
+
res.end(`Error fetching proxied request: ${error.message}`);
|
|
43
32
|
});
|
|
44
33
|
};
|
|
45
34
|
}
|
|
@@ -88,10 +77,6 @@ function copyHeaders(originalResponse, response) {
|
|
|
88
77
|
value = Array.isArray(value) ? value : [value];
|
|
89
78
|
value = value.map((x) => x.replace(/Domain=[^;]+?/i, ''));
|
|
90
79
|
}
|
|
91
|
-
else if (key === 'location') {
|
|
92
|
-
// set redirects to use local URL
|
|
93
|
-
// value = (value || '').replace(target, ''); // TODO
|
|
94
|
-
}
|
|
95
80
|
response.setHeader(key, value);
|
|
96
81
|
});
|
|
97
82
|
}
|
|
@@ -1,13 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
3
|
exports.HttpProxyMiddleware = void 0;
|
|
13
4
|
const httpProxy = require("http-proxy");
|
|
@@ -23,11 +14,11 @@ class HttpProxyMiddleware {
|
|
|
23
14
|
this.wsInternalSubscribed = false;
|
|
24
15
|
this.serverOnCloseSubscribed = false;
|
|
25
16
|
// https://github.com/Microsoft/TypeScript/wiki/'this'-in-TypeScript#red-flags-for-this
|
|
26
|
-
this.middleware = (req, res, next) =>
|
|
17
|
+
this.middleware = async (req, res, next) => {
|
|
27
18
|
var _a, _b;
|
|
28
19
|
if (this.shouldProxy(this.config.context, req)) {
|
|
29
20
|
try {
|
|
30
|
-
const activeProxyOptions =
|
|
21
|
+
const activeProxyOptions = await this.prepareProxyRequest(req);
|
|
31
22
|
this.proxy.web(req, res, activeProxyOptions);
|
|
32
23
|
}
|
|
33
24
|
catch (err) {
|
|
@@ -57,7 +48,7 @@ class HttpProxyMiddleware {
|
|
|
57
48
|
// use initial request to access the server object to subscribe to http upgrade event
|
|
58
49
|
this.catchUpgradeRequest(server);
|
|
59
50
|
}
|
|
60
|
-
}
|
|
51
|
+
};
|
|
61
52
|
this.catchUpgradeRequest = (server) => {
|
|
62
53
|
if (!this.wsInternalSubscribed) {
|
|
63
54
|
server.on('upgrade', this.handleUpgrade);
|
|
@@ -66,13 +57,13 @@ class HttpProxyMiddleware {
|
|
|
66
57
|
this.wsInternalSubscribed = true;
|
|
67
58
|
}
|
|
68
59
|
};
|
|
69
|
-
this.handleUpgrade = (req, socket, head) =>
|
|
60
|
+
this.handleUpgrade = async (req, socket, head) => {
|
|
70
61
|
if (this.shouldProxy(this.config.context, req)) {
|
|
71
|
-
const activeProxyOptions =
|
|
62
|
+
const activeProxyOptions = await this.prepareProxyRequest(req);
|
|
72
63
|
this.proxy.ws(req, socket, head, activeProxyOptions);
|
|
73
64
|
this.logger.info('[HPM] Upgrading to WebSocket');
|
|
74
65
|
}
|
|
75
|
-
}
|
|
66
|
+
};
|
|
76
67
|
/**
|
|
77
68
|
* Determine whether request should be proxied.
|
|
78
69
|
*
|
|
@@ -93,7 +84,7 @@ class HttpProxyMiddleware {
|
|
|
93
84
|
* @param {Object} req
|
|
94
85
|
* @return {Object} proxy options
|
|
95
86
|
*/
|
|
96
|
-
this.prepareProxyRequest = (req) =>
|
|
87
|
+
this.prepareProxyRequest = async (req) => {
|
|
97
88
|
// https://github.com/chimurai/http-proxy-middleware/issues/17
|
|
98
89
|
// https://github.com/chimurai/http-proxy-middleware/issues/94
|
|
99
90
|
req.url = req.originalUrl || req.url;
|
|
@@ -103,30 +94,30 @@ class HttpProxyMiddleware {
|
|
|
103
94
|
// Apply in order:
|
|
104
95
|
// 1. option.router
|
|
105
96
|
// 2. option.pathRewrite
|
|
106
|
-
|
|
107
|
-
|
|
97
|
+
await this.applyRouter(req, newProxyOptions);
|
|
98
|
+
await this.applyPathRewrite(req, this.pathRewriter);
|
|
108
99
|
// debug logging for both http(s) and websockets
|
|
109
100
|
if (this.proxyOptions.logLevel === 'debug') {
|
|
110
101
|
const arrow = logger_1.getArrow(originalPath, req.url, this.proxyOptions.target, newProxyOptions.target);
|
|
111
102
|
this.logger.debug('[HPM] %s %s %s %s', req.method, originalPath, arrow, newProxyOptions.target);
|
|
112
103
|
}
|
|
113
104
|
return newProxyOptions;
|
|
114
|
-
}
|
|
105
|
+
};
|
|
115
106
|
// Modify option.target when router present.
|
|
116
|
-
this.applyRouter = (req, options) =>
|
|
107
|
+
this.applyRouter = async (req, options) => {
|
|
117
108
|
let newTarget;
|
|
118
109
|
if (options.router) {
|
|
119
|
-
newTarget =
|
|
110
|
+
newTarget = await Router.getTarget(req, options);
|
|
120
111
|
if (newTarget) {
|
|
121
112
|
this.logger.debug('[HPM] Router new target: %s -> "%s"', options.target, newTarget);
|
|
122
113
|
options.target = newTarget;
|
|
123
114
|
}
|
|
124
115
|
}
|
|
125
|
-
}
|
|
116
|
+
};
|
|
126
117
|
// rewrite path
|
|
127
|
-
this.applyPathRewrite = (req, pathRewriter) =>
|
|
118
|
+
this.applyPathRewrite = async (req, pathRewriter) => {
|
|
128
119
|
if (pathRewriter) {
|
|
129
|
-
const path =
|
|
120
|
+
const path = await pathRewriter(req.url, req);
|
|
130
121
|
if (typeof path === 'string') {
|
|
131
122
|
req.url = path;
|
|
132
123
|
}
|
|
@@ -134,7 +125,7 @@ class HttpProxyMiddleware {
|
|
|
134
125
|
this.logger.info('[HPM] pathRewrite: No rewritten path found. (%s)', req.url);
|
|
135
126
|
}
|
|
136
127
|
}
|
|
137
|
-
}
|
|
128
|
+
};
|
|
138
129
|
this.logError = (err, req, res, target) => {
|
|
139
130
|
var _a;
|
|
140
131
|
const hostname = ((_a = req.headers) === null || _a === void 0 ? void 0 : _a.host) || req.hostname || req.host; // (websocket) || (node0.10 || node 4/5)
|
package/dist/router.js
CHANGED
|
@@ -1,30 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
3
|
exports.getTarget = void 0;
|
|
13
4
|
const isPlainObj = require("is-plain-obj");
|
|
14
5
|
const logger_1 = require("./logger");
|
|
15
6
|
const logger = logger_1.getInstance();
|
|
16
|
-
function getTarget(req, config) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
return newTarget;
|
|
27
|
-
});
|
|
7
|
+
async function getTarget(req, config) {
|
|
8
|
+
let newTarget;
|
|
9
|
+
const router = config.router;
|
|
10
|
+
if (isPlainObj(router)) {
|
|
11
|
+
newTarget = getTargetFromProxyTable(req, router);
|
|
12
|
+
}
|
|
13
|
+
else if (typeof router === 'function') {
|
|
14
|
+
newTarget = await router(req);
|
|
15
|
+
}
|
|
16
|
+
return newTarget;
|
|
28
17
|
}
|
|
29
18
|
exports.getTarget = getTarget;
|
|
30
19
|
function getTargetFromProxyTable(req, table) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "http-proxy-middleware",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "The one-liner node.js proxy middleware for connect, express and browser-sync",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -15,11 +15,13 @@
|
|
|
15
15
|
"eslint:fix": "yarn eslint --fix",
|
|
16
16
|
"prettier": "prettier --list-different \"**/*.{js,ts,md,yml,json,html}\"",
|
|
17
17
|
"prettier:fix": "prettier --write \"**/*.{js,ts,md,yml,json,html}\"",
|
|
18
|
+
"prebuild": "yarn clean",
|
|
18
19
|
"build": "tsc",
|
|
19
20
|
"pretest": "yarn build",
|
|
20
21
|
"test": "jest",
|
|
22
|
+
"precoverage": "yarn build",
|
|
21
23
|
"coverage": "jest --coverage --coverageReporters=lcov",
|
|
22
|
-
"prepare": "yarn
|
|
24
|
+
"prepare": "yarn build && rm dist/tsconfig.tsbuildinfo"
|
|
23
25
|
},
|
|
24
26
|
"repository": {
|
|
25
27
|
"type": "git",
|
|
@@ -60,6 +62,7 @@
|
|
|
60
62
|
"@types/ws": "^7.4.0",
|
|
61
63
|
"@typescript-eslint/eslint-plugin": "^4.19.0",
|
|
62
64
|
"@typescript-eslint/parser": "^4.19.0",
|
|
65
|
+
"body-parser": "^1.19.0",
|
|
63
66
|
"browser-sync": "^2.26.14",
|
|
64
67
|
"connect": "^3.7.0",
|
|
65
68
|
"eslint": "^7.23.0",
|
|
@@ -85,7 +88,7 @@
|
|
|
85
88
|
"micromatch": "^4.0.2"
|
|
86
89
|
},
|
|
87
90
|
"engines": {
|
|
88
|
-
"node": ">=
|
|
91
|
+
"node": ">=12.0.0"
|
|
89
92
|
},
|
|
90
93
|
"husky": {
|
|
91
94
|
"hooks": {
|