http-proxy-middleware 1.1.2-alpha.1 → 1.2.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 CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## [v1.2.0](https://github.com/chimurai/http-proxy-middleware/releases/tag/v1.2.0)
4
+
5
+ - feat(handler): response interceptor ([#520](https://github.com/chimurai/http-proxy-middleware/pull/520))
6
+ - fix(log error): handle undefined target when websocket errors ([#527](https://github.com/chimurai/http-proxy-middleware/pull/527))
7
+
8
+ ## [v1.1.2](https://github.com/chimurai/http-proxy-middleware/releases/tag/v1.1.2)
9
+
10
+ - fix(log error): handle optional target ([#523](https://github.com/chimurai/http-proxy-middleware/pull/523))
11
+
3
12
  ## [v1.1.1](https://github.com/chimurai/http-proxy-middleware/releases/tag/v1.1.1)
4
13
 
5
14
  - fix(error handler): re-throw http-proxy missing target error ([#517](https://github.com/chimurai/http-proxy-middleware/pull/517))
package/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # http-proxy-middleware
2
2
 
3
- [![Build Status](https://img.shields.io/travis/chimurai/http-proxy-middleware/master.svg?style=flat-square)](https://travis-ci.org/chimurai/http-proxy-middleware)
3
+ [![GitHub Workflow Status (branch)](https://img.shields.io/github/workflow/status/chimurai/http-proxy-middleware/Test/master?style=flat-square)](https://github.com/chimurai/http-proxy-middleware/actions?query=branch%3Amaster)
4
4
  [![Coveralls](https://img.shields.io/coveralls/chimurai/http-proxy-middleware.svg?style=flat-square)](https://coveralls.io/r/chimurai/http-proxy-middleware)
5
5
  [![dependency Status](https://img.shields.io/david/chimurai/http-proxy-middleware.svg?style=flat-square)](https://david-dm.org/chimurai/http-proxy-middleware#info=dependencies)
6
6
  [![dependency Status](https://snyk.io/test/npm/http-proxy-middleware/badge.svg?style=flat-square)](https://snyk.io/test/npm/http-proxy-middleware)
7
- [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier)
7
+ [![npm](https://img.shields.io/npm/v/http-proxy-middleware?color=%23CC3534&style=flat-square)](https://www.npmjs.com/package/http-proxy-middleware)
8
8
 
9
9
  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).
10
10
 
@@ -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 responses](#intercept-and-manipulate-responses)
72
73
  - [Working examples](#working-examples)
73
74
  - [Recipes](#recipes)
74
75
  - [Compatible servers](#compatible-servers)
@@ -298,7 +299,7 @@ Subscribe to [http-proxy events](https://github.com/nodejitsu/node-http-proxy#li
298
299
  - **option.onError**: function, subscribe to http-proxy's `error` event for custom error handling.
299
300
 
300
301
  ```javascript
301
- function onError(err, req, res) {
302
+ function onError(err, req, res, target) {
302
303
  res.writeHead(500, {
303
304
  'Content-Type': 'text/plain',
304
305
  });
@@ -481,6 +482,39 @@ const server = app.listen(3000);
481
482
  server.on('upgrade', wsProxy.upgrade); // <-- subscribe to http 'upgrade'
482
483
  ```
483
484
 
485
+ ## Intercept and manipulate responses
486
+
487
+ Intercept responses from upstream with `responseInterceptor`. (Make sure to set `selfHandleResponse: true`)
488
+
489
+ Responses which are compressed with `brotli`, `gzip` and `deflate` will be decompressed automatically. The response will be returned as `buffer` ([docs](https://nodejs.org/api/buffer.html)) which you can manipulate.
490
+
491
+ With `buffer`, response manipulation is not limited to text responses (html/css/js, etc...); image manipulation will be possible too. ([example](https://github.com/chimurai/http-proxy-middleware/blob/master/recipes/response-interceptor.md#manipulate-image-response))
492
+
493
+ NOTE: `responseInterceptor` disables streaming of target's response.
494
+
495
+ Example:
496
+
497
+ ```javascript
498
+ const { createProxyMiddleware, responseInterceptor } = require('http-proxy-middleware');
499
+
500
+ const proxy = createProxyMiddleware({
501
+ /**
502
+ * IMPORTANT: avoid res.end being called automatically
503
+ **/
504
+ selfHandleResponse: true, // res.end() will be called internally by responseInterceptor()
505
+
506
+ /**
507
+ * Intercept response and replace 'Hello' with 'Goodbye'
508
+ **/
509
+ onProxyRes: responseInterceptor(async (responseBuffer, proxyRes, req, res) => {
510
+ const response = responseBuffer.toString('utf-8'); // convert buffer to string
511
+ return response.replace('Hello', 'Goodbye'); // manipulate response and return the result
512
+ }),
513
+ });
514
+ ```
515
+
516
+ Check out [interception recipes](https://github.com/chimurai/http-proxy-middleware/blob/master/recipes/response-interceptor.md#readme) for more examples.
517
+
484
518
  ## Working examples
485
519
 
486
520
  View and play around with [working examples](https://github.com/chimurai/http-proxy-middleware/tree/master/examples).
@@ -489,6 +523,7 @@ View and play around with [working examples](https://github.com/chimurai/http-pr
489
523
  - express ([example source](https://github.com/chimurai/http-proxy-middleware/tree/master/examples/express/index.js))
490
524
  - connect ([example source](https://github.com/chimurai/http-proxy-middleware/tree/master/examples/connect/index.js))
491
525
  - WebSocket ([example source](https://github.com/chimurai/http-proxy-middleware/tree/master/examples/websocket/index.js))
526
+ - Response Manipulation ([example source](https://github.com/chimurai/http-proxy-middleware/blob/master/examples/response-interceptor/index.js))
492
527
 
493
528
  ## Recipes
494
529
 
File without changes
File without changes
@@ -0,0 +1 @@
1
+ export * from './public';
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
+ }) : (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ o[k2] = m[k];
8
+ }));
9
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
10
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
11
+ };
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ __exportStar(require("./public"), exports);
@@ -0,0 +1 @@
1
+ export { responseInterceptor } from './response-interceptor';
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.responseInterceptor = void 0;
4
+ var response_interceptor_1 = require("./response-interceptor");
5
+ Object.defineProperty(exports, "responseInterceptor", { enumerable: true, get: function () { return response_interceptor_1.responseInterceptor; } });
@@ -0,0 +1,12 @@
1
+ /// <reference types="node" />
2
+ import type * as http from 'http';
3
+ declare type Interceptor = (buffer: Buffer, proxyRes: http.IncomingMessage, req: http.IncomingMessage, res: http.ServerResponse) => Promise<Buffer | string>;
4
+ /**
5
+ * Intercept responses from upstream.
6
+ * Automatically decompress (deflate, gzip, brotli).
7
+ * Give developer the opportunity to modify intercepted Buffer and http.ServerResponse
8
+ *
9
+ * NOTE: must set options.selfHandleResponse=true (prevent automatic call of res.end())
10
+ */
11
+ export declare function responseInterceptor(interceptor: Interceptor): (proxyRes: http.IncomingMessage, req: http.IncomingMessage, res: http.ServerResponse) => Promise<void>;
12
+ export {};
@@ -0,0 +1,72 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.responseInterceptor = void 0;
13
+ const zlib = require("zlib");
14
+ /**
15
+ * Intercept responses from upstream.
16
+ * Automatically decompress (deflate, gzip, brotli).
17
+ * Give developer the opportunity to modify intercepted Buffer and http.ServerResponse
18
+ *
19
+ * NOTE: must set options.selfHandleResponse=true (prevent automatic call of res.end())
20
+ */
21
+ function responseInterceptor(interceptor) {
22
+ return function proxyRes(proxyRes, req, res) {
23
+ return __awaiter(this, void 0, void 0, function* () {
24
+ const originalProxyRes = proxyRes;
25
+ let buffer = Buffer.from('', 'utf8');
26
+ // decompress proxy response
27
+ const _proxyRes = decompress(proxyRes, proxyRes.headers['content-encoding']);
28
+ // concat data stream
29
+ _proxyRes.on('data', (chunk) => (buffer = Buffer.concat([buffer, chunk])));
30
+ _proxyRes.on('end', () => __awaiter(this, void 0, void 0, function* () {
31
+ // set original content type from upstream
32
+ res.setHeader('content-type', originalProxyRes.headers['content-type'] || '');
33
+ // call interceptor with intercepted response (buffer)
34
+ const interceptedBuffer = Buffer.from(yield interceptor(buffer, originalProxyRes, req, res));
35
+ // set correct content-length (with double byte character support)
36
+ res.setHeader('content-length', Buffer.byteLength(interceptedBuffer, 'utf8'));
37
+ res.write(interceptedBuffer);
38
+ res.end();
39
+ }));
40
+ _proxyRes.on('error', (error) => {
41
+ res.end(`Error fetching proxied request: ${error.message}`);
42
+ });
43
+ });
44
+ };
45
+ }
46
+ exports.responseInterceptor = responseInterceptor;
47
+ /**
48
+ * Streaming decompression of proxy response
49
+ * source: https://github.com/apache/superset/blob/9773aba522e957ed9423045ca153219638a85d2f/superset-frontend/webpack.proxy-config.js#L116
50
+ */
51
+ function decompress(proxyRes, contentEncoding) {
52
+ let _proxyRes = proxyRes;
53
+ let decompress;
54
+ switch (contentEncoding) {
55
+ case 'gzip':
56
+ decompress = zlib.createGunzip();
57
+ break;
58
+ case 'br':
59
+ decompress = zlib.createBrotliDecompress();
60
+ break;
61
+ case 'deflate':
62
+ decompress = zlib.createInflate();
63
+ break;
64
+ default:
65
+ break;
66
+ }
67
+ if (decompress) {
68
+ _proxyRes.pipe(decompress);
69
+ _proxyRes = decompress;
70
+ }
71
+ return _proxyRes;
72
+ }
@@ -13,7 +13,7 @@ exports.HttpProxyMiddleware = void 0;
13
13
  const httpProxy = require("http-proxy");
14
14
  const config_factory_1 = require("./config-factory");
15
15
  const contextMatcher = require("./context-matcher");
16
- const handlers = require("./handlers");
16
+ const handlers = require("./_handlers");
17
17
  const logger_1 = require("./logger");
18
18
  const PathRewriter = require("./path-rewriter");
19
19
  const Router = require("./router");
@@ -135,14 +135,15 @@ class HttpProxyMiddleware {
135
135
  }
136
136
  }
137
137
  });
138
- this.logError = (err, req, res) => __awaiter(this, void 0, void 0, function* () {
139
- const hostname = (req.headers && req.headers.host) || req.hostname || req.host; // (websocket) || (node0.10 || node 4/5)
140
- const routerTarget = yield Router.getTarget(req, this.proxyOptions);
141
- const target = routerTarget || this.proxyOptions.target.host || this.proxyOptions.target;
142
- const errorMessage = '[HPM] Error occurred while trying to proxy request %s from %s to %s (%s) (%s)';
138
+ this.logError = (err, req, res, target) => {
139
+ var _a;
140
+ const hostname = ((_a = req.headers) === null || _a === void 0 ? void 0 : _a.host) || req.hostname || req.host; // (websocket) || (node0.10 || node 4/5)
141
+ const requestHref = `${hostname}${req.url}`;
142
+ const targetHref = `${target === null || target === void 0 ? void 0 : target.href}`; // target is undefined when websocket errors
143
+ const errorMessage = '[HPM] Error occurred while proxying request %s to %s [%s] (%s)';
143
144
  const errReference = 'https://nodejs.org/api/errors.html#errors_common_system_errors'; // link to Node Common Systems Errors page
144
- this.logger.error(errorMessage, req.url, hostname, target, err.code || err, errReference);
145
- });
145
+ this.logger.error(errorMessage, requestHref, targetHref, err.code || err, errReference);
146
+ };
146
147
  this.config = config_factory_1.createConfig(context, opts);
147
148
  this.proxyOptions = this.config.options;
148
149
  // create proxy
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  import { Filter, Options } from './types';
2
2
  export declare function createProxyMiddleware(context: Filter | Options, options?: Options): import("./types").RequestHandler;
3
+ export * from './handlers';
3
4
  export { Filter, Options, RequestHandler } from './types';
package/dist/index.js CHANGED
@@ -1,4 +1,14 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
+ }) : (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ o[k2] = m[k];
8
+ }));
9
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
10
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
11
+ };
2
12
  Object.defineProperty(exports, "__esModule", { value: true });
3
13
  exports.createProxyMiddleware = void 0;
4
14
  const http_proxy_middleware_1 = require("./http-proxy-middleware");
@@ -7,3 +17,4 @@ function createProxyMiddleware(context, options) {
7
17
  return middleware;
8
18
  }
9
19
  exports.createProxyMiddleware = createProxyMiddleware;
20
+ __exportStar(require("./handlers"), exports);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "http-proxy-middleware",
3
- "version": "1.1.2-alpha.1",
3
+ "version": "1.2.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",