http-proxy-middleware 3.0.0-beta.0 → 3.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/README.md CHANGED
@@ -1,13 +1,13 @@
1
1
  # http-proxy-middleware
2
2
 
3
- [![GitHub Workflow Status (branch)](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
- [![Coveralls](https://img.shields.io/coveralls/chimurai/http-proxy-middleware.svg?style=flat-square)](https://coveralls.io/r/chimurai/http-proxy-middleware)
5
- [![dependency Status](https://snyk.io/test/npm/http-proxy-middleware/badge.svg?style=flat-square)](https://snyk.io/test/npm/http-proxy-middleware)
6
- [![npm](https://img.shields.io/npm/v/http-proxy-middleware?color=%23CC3534&style=flat-square)](https://www.npmjs.com/package/http-proxy-middleware)
3
+ [![GitHub Workflow Status (with branch)](https://img.shields.io/github/actions/workflow/status/chimurai/http-proxy-middleware/ci.yml?branch=master&logo=github-actions&logoColor=white&style=flat-square)](https://github.com/chimurai/http-proxy-middleware/actions/workflows/ci.yml?query=branch%3Amaster)
4
+ [![Coveralls](https://img.shields.io/coveralls/chimurai/http-proxy-middleware.svg?style=flat-square&logo=coveralls)](https://coveralls.io/r/chimurai/http-proxy-middleware)
5
+ [![Known Vulnerabilities](https://snyk.io/test/github/chimurai/http-proxy-middleware/badge.svg)](https://snyk.io/test/github/chimurai/http-proxy-middleware)
6
+ [![npm](https://img.shields.io/npm/v/http-proxy-middleware?color=%23CC3534&style=flat-square&logo=npm)](https://www.npmjs.com/package/http-proxy-middleware)
7
7
 
8
8
  Node.js proxying made simple. Configure proxy middleware with ease for [connect](https://github.com/senchalabs/connect), [express](https://github.com/expressjs/express), [next.js](https://github.com/vercel/next.js) and [many more](#compatible-servers).
9
9
 
10
- Powered by the popular Nodejitsu [`http-proxy`](https://github.com/nodejitsu/node-http-proxy). [![GitHub stars](https://img.shields.io/github/stars/nodejitsu/node-http-proxy.svg?style=social&label=Star)](https://github.com/nodejitsu/node-http-proxy)
10
+ Powered by the popular Nodejitsu [`http-proxy`](https://github.com/http-party/node-http-proxy). [![GitHub stars](https://img.shields.io/github/stars/http-party/node-http-proxy.svg?style=social&label=Star)](https://github.com/http-party/node-http-proxy)
11
11
 
12
12
  ## ⚠️ Note <!-- omit in toc -->
13
13
 
@@ -39,13 +39,13 @@ app.use(
39
39
  createProxyMiddleware({
40
40
  target: 'http://www.example.org/secret',
41
41
  changeOrigin: true,
42
- })
42
+ }),
43
43
  );
44
44
 
45
45
  app.listen(3000);
46
46
 
47
47
  // proxy and change the base path from "/api" to "/secret"
48
- // http://localhost:3000/api/foo/bar -> http://www.example.org/secret/foo/bar
48
+ // http://127.0.0.1:3000/api/foo/bar -> http://www.example.org/secret/foo/bar
49
49
  ```
50
50
 
51
51
  ```typescript
@@ -61,13 +61,13 @@ app.use(
61
61
  createProxyMiddleware({
62
62
  target: 'http://www.example.org/api',
63
63
  changeOrigin: true,
64
- })
64
+ }),
65
65
  );
66
66
 
67
67
  app.listen(3000);
68
68
 
69
69
  // proxy and keep the same base path "/api"
70
- // http://localhost:3000/api/foo/bar -> http://www.example.org/api/foo/bar
70
+ // http://127.0.0.1:3000/api/foo/bar -> http://www.example.org/api/foo/bar
71
71
  ```
72
72
 
73
73
  _All_ `http-proxy` [options](https://github.com/nodejitsu/node-http-proxy#options) can be used, along with some extra `http-proxy-middleware` [options](#options).
@@ -81,7 +81,7 @@ _All_ `http-proxy` [options](https://github.com/nodejitsu/node-http-proxy#option
81
81
  - [Express Server Example](#express-server-example)
82
82
  - [app.use(path, proxy)](#appusepath-proxy)
83
83
  - [Options](#options)
84
- - [`pathFilter` (string, []string, glob, []glob, function)](#pathfilter-string-string-glob-glob-function)
84
+ - [`pathFilter` (string, \[\]string, glob, \[\]glob, function)](#pathfilter-string-string-glob-glob-function)
85
85
  - [`pathRewrite` (object/function)](#pathrewrite-objectfunction)
86
86
  - [`router` (object/function)](#router-objectfunction)
87
87
  - [`plugins` (Array)](#plugins-array)
@@ -93,6 +93,7 @@ _All_ `http-proxy` [options](https://github.com/nodejitsu/node-http-proxy#option
93
93
  - [External WebSocket upgrade](#external-websocket-upgrade)
94
94
  - [Intercept and manipulate requests](#intercept-and-manipulate-requests)
95
95
  - [Intercept and manipulate responses](#intercept-and-manipulate-responses)
96
+ - [Node.js 17+: ECONNREFUSED issue with IPv6 and localhost (#705)](#nodejs-17-econnrefused-issue-with-ipv6-and-localhost-705)
96
97
  - [Debugging](#debugging)
97
98
  - [Working examples](#working-examples)
98
99
  - [Recipes](#recipes)
@@ -140,15 +141,12 @@ const { createProxyMiddleware } = require('http-proxy-middleware');
140
141
 
141
142
  const app = express();
142
143
 
143
- // proxy middleware options
144
- /** @type {import('http-proxy-middleware/dist/types').Options} */
145
- const options = {
144
+ // create the proxy
145
+ /** @type {import('http-proxy-middleware/dist/types').RequestHandler<express.Request, express.Response>} */
146
+ const exampleProxy = createProxyMiddleware({
146
147
  target: 'http://www.example.org/api', // target host with the same base path
147
148
  changeOrigin: true, // needed for virtual hosted sites
148
- };
149
-
150
- // create the proxy
151
- const exampleProxy = createProxyMiddleware(options);
149
+ });
152
150
 
153
151
  // mount `exampleProxy` in web server
154
152
  app.use('/api', exampleProxy);
@@ -166,7 +164,7 @@ app.use(
166
164
  target: 'http://www.example.org/api',
167
165
  changeOrigin: true,
168
166
  pathFilter: '/api/proxy-only-this-path',
169
- })
167
+ }),
170
168
  );
171
169
  ```
172
170
 
@@ -257,22 +255,22 @@ Re-target `option.target` for specific requests.
257
255
  // Use `host` and/or `path` to match requests. First match will be used.
258
256
  // The order of the configuration matters.
259
257
  router: {
260
- 'integration.localhost:3000' : 'http://localhost:8001', // host only
261
- 'staging.localhost:3000' : 'http://localhost:8002', // host only
262
- 'localhost:3000/api' : 'http://localhost:8003', // host + path
263
- '/rest' : 'http://localhost:8004' // path only
258
+ 'integration.localhost:3000' : 'http://127.0.0.1:8001', // host only
259
+ 'staging.localhost:3000' : 'http://127.0.0.1:8002', // host only
260
+ 'localhost:3000/api' : 'http://127.0.0.1:8003', // host + path
261
+ '/rest' : 'http://127.0.0.1:8004' // path only
264
262
  }
265
263
 
266
264
  // Custom router function (string target)
267
265
  router: function(req) {
268
- return 'http://localhost:8004';
266
+ return 'http://127.0.0.1:8004';
269
267
  }
270
268
 
271
269
  // Custom router function (target object)
272
270
  router: function(req) {
273
271
  return {
274
272
  protocol: 'https:', // The : is required
275
- host: 'localhost',
273
+ host: '127.0.0.1',
276
274
  port: 8004
277
275
  };
278
276
  }
@@ -488,10 +486,10 @@ The following options are provided by the underlying [http-proxy](https://github
488
486
  req,
489
487
  res,
490
488
  {
491
- target: 'http://localhost:4003/',
489
+ target: 'http://127.0.0.1:4003/',
492
490
  buffer: streamify(req.rawBody),
493
491
  },
494
- next
492
+ next,
495
493
  );
496
494
  };
497
495
  ```
@@ -573,6 +571,21 @@ const proxy = createProxyMiddleware({
573
571
 
574
572
  Check out [interception recipes](https://github.com/chimurai/http-proxy-middleware/blob/master/recipes/response-interceptor.md#readme) for more examples.
575
573
 
574
+ ## Node.js 17+: ECONNREFUSED issue with IPv6 and localhost ([#705](https://github.com/chimurai/http-proxy-middleware/issues/705))
575
+
576
+ Node.js 17+ no longer prefers IPv4 over IPv6 for DNS lookups.
577
+ E.g. It's **not** guaranteed that `localhost` will be resolved to `127.0.0.1` – it might just as well be `::1` (or some other IP address).
578
+
579
+ If your target server only accepts IPv4 connections, trying to proxy to `localhost` will fail if resolved to `::1` (IPv6).
580
+
581
+ Ways to solve it:
582
+
583
+ - Change `target: "http://localhost"` to `target: "http://127.0.0.1"` (IPv4).
584
+ - Change the target server to (also) accept IPv6 connections.
585
+ - Add this flag when running `node`: `node index.js --dns-result-order=ipv4first`. (Not recommended.)
586
+
587
+ > Note: There’s a thing called [Happy Eyeballs](https://en.wikipedia.org/wiki/Happy_Eyeballs) which means connecting to both IPv4 and IPv6 in parallel, which Node.js doesn’t have, but explains why for example `curl` can connect.
588
+
576
589
  ## Debugging
577
590
 
578
591
  Configure the `DEBUG` environment variable enable debug logging.
@@ -1,2 +1,2 @@
1
1
  import { Options } from './types';
2
- export declare function verifyConfig(options: Options): void;
2
+ export declare function verifyConfig<TReq, TRes>(options: Options<TReq, TRes>): void;
package/dist/errors.js CHANGED
@@ -7,4 +7,4 @@ var ERRORS;
7
7
  ERRORS["ERR_CONTEXT_MATCHER_GENERIC"] = "[HPM] Invalid context. Expecting something like: \"/api\" or [\"/api\", \"/ajax\"]";
8
8
  ERRORS["ERR_CONTEXT_MATCHER_INVALID_ARRAY"] = "[HPM] Invalid pathFilter. Expecting something like: [\"/api\", \"/ajax\"] or [\"/api/**\", \"!**.html\"]";
9
9
  ERRORS["ERR_PATH_REWRITER_CONFIG"] = "[HPM] Invalid pathRewrite config. Expecting object with pathRewrite config or a rewrite function";
10
- })(ERRORS = exports.ERRORS || (exports.ERRORS = {}));
10
+ })(ERRORS || (exports.ERRORS = ERRORS = {}));
@@ -1,2 +1,2 @@
1
1
  import type { Options, Plugin } from './types';
2
- export declare function getPlugins(options: Options): Plugin[];
2
+ export declare function getPlugins<TReq, TRes>(options: Options<TReq, TRes>): Plugin<TReq, TRes>[];
@@ -3,13 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getPlugins = void 0;
4
4
  const default_1 = require("./plugins/default");
5
5
  function getPlugins(options) {
6
- var _a, _b;
7
6
  // don't load default errorResponsePlugin if user has specified their own
8
- const maybeErrorResponsePlugin = !!((_a = options.on) === null || _a === void 0 ? void 0 : _a.error) ? [] : [default_1.errorResponsePlugin];
7
+ const maybeErrorResponsePlugin = !!options.on?.error ? [] : [default_1.errorResponsePlugin];
9
8
  const defaultPlugins = !!options.ejectPlugins
10
9
  ? [] // no default plugins when ejecting
11
10
  : [default_1.debugProxyErrorsPlugin, default_1.proxyEventsPlugin, default_1.loggerPlugin, ...maybeErrorResponsePlugin];
12
- const userPlugins = (_b = options.plugins) !== null && _b !== void 0 ? _b : [];
11
+ const userPlugins = options.plugins ?? [];
13
12
  return [...defaultPlugins, ...userPlugins];
14
13
  }
15
14
  exports.getPlugins = getPlugins;
@@ -1,7 +1,9 @@
1
1
  /// <reference types="node" />
2
2
  import type * as http from 'http';
3
- import type { Request } from '../types';
3
+ export type BodyParserLikeRequest = http.IncomingMessage & {
4
+ body: any;
5
+ };
4
6
  /**
5
7
  * Fix proxied body if bodyParser is involved.
6
8
  */
7
- export declare function fixRequestBody(proxyReq: http.ClientRequest, req: Request): void;
9
+ export declare function fixRequestBody<TReq = http.IncomingMessage>(proxyReq: http.ClientRequest, req: TReq): void;
@@ -1,6 +1,7 @@
1
1
  /// <reference types="node" />
2
+ /// <reference types="node" />
2
3
  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
+ type Interceptor<TReq = http.IncomingMessage, TRes = http.ServerResponse> = (buffer: Buffer, proxyRes: TReq, req: TReq, res: TRes) => Promise<Buffer | string>;
4
5
  /**
5
6
  * Intercept responses from upstream.
6
7
  * Automatically decompress (deflate, gzip, brotli).
@@ -8,5 +9,5 @@ declare type Interceptor = (buffer: Buffer, proxyRes: http.IncomingMessage, req:
8
9
  *
9
10
  * NOTE: must set options.selfHandleResponse=true (prevent automatic call of res.end())
10
11
  */
11
- export declare function responseInterceptor(interceptor: Interceptor): (proxyRes: http.IncomingMessage, req: http.IncomingMessage, res: http.ServerResponse) => Promise<void>;
12
+ export declare function responseInterceptor<TReq extends http.IncomingMessage = http.IncomingMessage, TRes extends http.ServerResponse = http.ServerResponse>(interceptor: Interceptor<TReq, TRes>): (proxyRes: TReq, req: TReq, res: TRes) => Promise<void>;
12
13
  export {};
@@ -1,11 +1,11 @@
1
1
  import type { RequestHandler, Options } from './types';
2
- export declare class HttpProxyMiddleware {
2
+ export declare class HttpProxyMiddleware<TReq, TRes> {
3
3
  private wsInternalSubscribed;
4
4
  private serverOnCloseSubscribed;
5
5
  private proxyOptions;
6
6
  private proxy;
7
7
  private pathRewriter;
8
- constructor(options: Options);
8
+ constructor(options: Options<TReq, TRes>);
9
9
  middleware: RequestHandler;
10
10
  private registerPlugins;
11
11
  private catchUpgradeRequest;
@@ -14,8 +14,7 @@ class HttpProxyMiddleware {
14
14
  this.wsInternalSubscribed = false;
15
15
  this.serverOnCloseSubscribed = false;
16
16
  // https://github.com/Microsoft/TypeScript/wiki/'this'-in-TypeScript#red-flags-for-this
17
- this.middleware = async (req, res, next) => {
18
- var _a, _b;
17
+ this.middleware = (async (req, res, next) => {
19
18
  if (this.shouldProxy(this.proxyOptions.pathFilter, req)) {
20
19
  try {
21
20
  const activeProxyOptions = await this.prepareProxyRequest(req);
@@ -37,7 +36,7 @@ class HttpProxyMiddleware {
37
36
  * req.socket: node >= 13
38
37
  * req.connection: node < 13 (Remove this when node 12/13 support is dropped)
39
38
  */
40
- const server = (_b = (((_a = req.socket) !== null && _a !== void 0 ? _a : req.connection))) === null || _b === void 0 ? void 0 : _b.server;
39
+ const server = (req.socket ?? req.connection)?.server;
41
40
  if (server && !this.serverOnCloseSubscribed) {
42
41
  server.on('close', () => {
43
42
  (0, debug_1.Debug)('server close signal received: closing proxy server');
@@ -49,7 +48,7 @@ class HttpProxyMiddleware {
49
48
  // use initial request to access the server object to subscribe to http upgrade event
50
49
  this.catchUpgradeRequest(server);
51
50
  }
52
- };
51
+ });
53
52
  this.catchUpgradeRequest = (server) => {
54
53
  if (!this.wsInternalSubscribed) {
55
54
  (0, debug_1.Debug)('subscribing to server upgrade event');
package/dist/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
- import type { Options, RequestHandler } from './types';
2
- export declare function createProxyMiddleware(options: Options): RequestHandler;
1
+ /// <reference types="node" />
2
+ import type { Options, RequestHandler, NextFunction } from './types';
3
+ import type * as http from 'http';
4
+ export declare function createProxyMiddleware<TReq = http.IncomingMessage, TRes = http.ServerResponse, TNext = NextFunction>(options: Options<TReq, TRes>): RequestHandler<TReq, TRes, TNext>;
3
5
  export * from './handlers';
4
6
  export type { Filter, Options, RequestHandler } from './types';
5
7
  /**
@@ -1,11 +1,13 @@
1
+ /// <reference types="node" />
1
2
  import { Filter, RequestHandler } from '../types';
2
3
  import { LegacyOptions } from './types';
4
+ import type * as http from 'http';
3
5
  /**
4
6
  * @deprecated
5
7
  * This function is deprecated and will be removed in a future version.
6
8
  *
7
9
  * Use {@link createProxyMiddleware} instead.
8
10
  */
9
- export declare function legacyCreateProxyMiddleware(shortHand: string): RequestHandler;
10
- export declare function legacyCreateProxyMiddleware(legacyOptions: LegacyOptions): RequestHandler;
11
- export declare function legacyCreateProxyMiddleware(legacyContext: Filter, legacyOptions: LegacyOptions): RequestHandler;
11
+ export declare function legacyCreateProxyMiddleware<TReq = http.IncomingMessage, TRes = http.ServerResponse>(shortHand: string): RequestHandler<TReq, TRes>;
12
+ export declare function legacyCreateProxyMiddleware<TReq = http.IncomingMessage, TRes = http.ServerResponse>(legacyOptions: LegacyOptions<TReq, TRes>): RequestHandler<TReq, TRes>;
13
+ export declare function legacyCreateProxyMiddleware<TReq = http.IncomingMessage, TRes = http.ServerResponse>(legacyContext: Filter<TReq>, legacyOptions: LegacyOptions<TReq, TRes>): RequestHandler<TReq, TRes>;
@@ -3,4 +3,4 @@ import { LegacyOptions } from './types';
3
3
  /**
4
4
  * Convert {@link LegacyOptions legacy Options} to new {@link Options}
5
5
  */
6
- export declare function legacyOptionsAdapter(legacyContext: Filter | LegacyOptions, legacyOptions: LegacyOptions): Options;
6
+ export declare function legacyOptionsAdapter<TReq, TRes>(legacyContext: Filter<TReq> | LegacyOptions<TReq, TRes>, legacyOptions: LegacyOptions<TReq, TRes>): Options<TReq, TRes>;
@@ -18,12 +18,15 @@ const proxyEventMap = {
18
18
  * Convert {@link LegacyOptions legacy Options} to new {@link Options}
19
19
  */
20
20
  function legacyOptionsAdapter(legacyContext, legacyOptions) {
21
- let options;
21
+ let options = {};
22
22
  let logger;
23
23
  // https://github.com/chimurai/http-proxy-middleware/pull/716
24
24
  if (typeof legacyContext === 'string' && !!url.parse(legacyContext).host) {
25
25
  throw new Error(`Shorthand syntax is removed from legacyCreateProxyMiddleware().
26
- Please use "legacyCreateProxyMiddleware({ target: 'http://www.example.org' })" instead.`);
26
+ Please use "legacyCreateProxyMiddleware({ target: 'http://www.example.org' })" instead.
27
+
28
+ More details: https://github.com/chimurai/http-proxy-middleware/blob/master/MIGRATION.md#removed-shorthand-usage
29
+ `);
27
30
  }
28
31
  // detect old "context" argument and convert to "options.pathFilter"
29
32
  // https://github.com/chimurai/http-proxy-middleware/pull/722/files#diff-a2a171449d862fe29692ce031981047d7ab755ae7f84c707aef80701b3ea0c80L4
@@ -37,13 +40,16 @@ function legacyOptionsAdapter(legacyContext, legacyOptions) {
37
40
  pathFilter: '${legacyContext}',
38
41
  }
39
42
 
40
- More details: https://github.com/chimurai/http-proxy-middleware/blob/master/MIGRATION.md
43
+ More details: https://github.com/chimurai/http-proxy-middleware/blob/master/MIGRATION.md#removed-context-argument
41
44
  `);
42
45
  }
43
46
  else if (legacyContext && !legacyOptions) {
44
47
  options = { ...legacyContext };
45
48
  logger = getLegacyLogger(options);
46
49
  }
50
+ else {
51
+ logger = getLegacyLogger({});
52
+ }
47
53
  // map old event names to new event names
48
54
  // https://github.com/chimurai/http-proxy-middleware/pull/745/files#diff-c54113cf61ec99691748a3890bfbeb00e10efb3f0a76f03a0fd9ec49072e410aL48-L53
49
55
  Object.entries(proxyEventMap).forEach(([legacyEventName, proxyEventName]) => {
@@ -59,7 +65,7 @@ function legacyOptionsAdapter(legacyContext, legacyOptions) {
59
65
  },
60
66
  }
61
67
 
62
- More details: https://github.com/chimurai/http-proxy-middleware/blob/master/MIGRATION.md
68
+ More details: https://github.com/chimurai/http-proxy-middleware/blob/master/MIGRATION.md#refactored-proxy-events
63
69
  `);
64
70
  }
65
71
  });
@@ -77,7 +83,7 @@ function legacyOptionsAdapter(legacyContext, legacyOptions) {
77
83
  logger: console,
78
84
  }
79
85
 
80
- More details: https://github.com/chimurai/http-proxy-middleware/blob/master/MIGRATION.md
86
+ More details: https://github.com/chimurai/http-proxy-middleware/blob/master/MIGRATION.md#removed-logprovider-and-loglevel-options
81
87
  `);
82
88
  }
83
89
  return options;
@@ -1,10 +1,12 @@
1
+ /// <reference types="node" />
2
+ import type * as http from 'http';
1
3
  import { Options } from '..';
2
4
  /**
3
5
  * @deprecated
4
6
  *
5
7
  * Will be removed in a future version.
6
8
  */
7
- export interface LegacyOptions extends Options {
9
+ export interface LegacyOptions<TReq = http.IncomingMessage, TRes = http.ServerResponse> extends Options<TReq, TRes> {
8
10
  /**
9
11
  * @deprecated
10
12
  * Use `on.error` instead.
@@ -1,2 +1,4 @@
1
- import type { Filter, Request } from './types';
2
- export declare function matchPathFilter(pathFilter: Filter, uri: string, req: Request): boolean;
1
+ /// <reference types="node" />
2
+ import type { Filter } from './types';
3
+ import type * as http from 'http';
4
+ export declare function matchPathFilter<TReq = http.IncomingMessage>(pathFilter: Filter<TReq> | undefined, uri: string | undefined, req: http.IncomingMessage): boolean;
@@ -39,7 +39,7 @@ exports.matchPathFilter = matchPathFilter;
39
39
  */
40
40
  function matchSingleStringPath(pathFilter, uri) {
41
41
  const pathname = getUrlPathName(uri);
42
- return pathname.indexOf(pathFilter) === 0;
42
+ return pathname?.indexOf(pathFilter) === 0;
43
43
  }
44
44
  function matchSingleGlobPath(pattern, uri) {
45
45
  const pathname = getUrlPathName(uri);
@@ -8,7 +8,7 @@ const errorResponsePlugin = (proxyServer, options) => {
8
8
  if (!req && !res) {
9
9
  throw err; // "Error: Must provide a proper URL as target"
10
10
  }
11
- if (res.writeHead && !res.headersSent) {
11
+ if ('writeHead' in res && !res.headersSent) {
12
12
  const statusCode = (0, status_code_1.getStatusCode)(err.code);
13
13
  res.writeHead(statusCode);
14
14
  }
@@ -5,10 +5,9 @@ const logger_1 = require("../../logger");
5
5
  const loggerPlugin = (proxyServer, options) => {
6
6
  const logger = (0, logger_1.getLogger)(options);
7
7
  proxyServer.on('error', (err, req, res, target) => {
8
- var _a;
9
- const hostname = (_a = req === null || req === void 0 ? void 0 : req.headers) === null || _a === void 0 ? void 0 : _a.host;
10
- const requestHref = `${hostname}${req === null || req === void 0 ? void 0 : req.url}`;
11
- const targetHref = `${target === null || target === void 0 ? void 0 : target.href}`; // target is undefined when websocket errors
8
+ const hostname = req?.headers?.host;
9
+ const requestHref = `${hostname}${req?.url}`;
10
+ const targetHref = `${target?.href}`; // target is undefined when websocket errors
12
11
  const errorMessage = '[HPM] Error occurred while proxying request %s to %s [%s] (%s)';
13
12
  const errReference = 'https://nodejs.org/api/errors.html#errors_common_system_errors'; // link to Node Common Systems Errors page
14
13
  logger.error(errorMessage, requestHref, targetHref, err.code || err, errReference);
@@ -21,9 +20,9 @@ const loggerPlugin = (proxyServer, options) => {
21
20
  * ```
22
21
  */
23
22
  proxyServer.on('proxyRes', (proxyRes, req, res) => {
24
- var _a;
25
23
  // BrowserSync uses req.originalUrl
26
- const originalUrl = (_a = req.originalUrl) !== null && _a !== void 0 ? _a : `${req.baseUrl}${req.path}`;
24
+ // Next.js doesn't have req.baseUrl
25
+ const originalUrl = req.originalUrl ?? `${req.baseUrl || ''}${req.url}`;
27
26
  const exchange = `[HPM] ${req.method} ${originalUrl} -> ${proxyRes.req.protocol}//${proxyRes.req.host}${proxyRes.req.path} [${proxyRes.statusCode}]`;
28
27
  logger.info(exchange);
29
28
  });
package/dist/types.d.ts CHANGED
@@ -3,40 +3,57 @@
3
3
  * https://github.com/DefinitelyTyped/DefinitelyTyped/blob/6f529c6c67a447190f86bfbf894d1061e41e07b7/types/http-proxy-middleware/index.d.ts
4
4
  */
5
5
  /// <reference types="node" />
6
+ /// <reference types="node" />
7
+ /// <reference types="node" />
8
+ /// <reference types="node" />
6
9
  import type * as http from 'http';
7
10
  import type * as httpProxy from 'http-proxy';
8
11
  import type * as net from 'net';
9
- export declare type Request<T = http.IncomingMessage> = T;
10
- export declare type Response<T = http.ServerResponse> = T;
11
- export declare type NextFunction<T = (err?: any) => void> = T;
12
- export interface RequestHandler {
13
- (req: Request, res: Response, next?: NextFunction): void | Promise<void>;
14
- upgrade?: (req: Request, socket: net.Socket, head: any) => void;
12
+ export type NextFunction<T = (err?: any) => void> = T;
13
+ export interface RequestHandler<TReq = http.IncomingMessage, TRes = http.ServerResponse, TNext = NextFunction> {
14
+ (req: TReq, res: TRes, next?: TNext): void | Promise<void>;
15
+ upgrade: (req: http.IncomingMessage, socket: net.Socket, head: Buffer) => void;
16
+ }
17
+ export type Filter<TReq = http.IncomingMessage> = string | string[] | ((pathname: string, req: TReq) => boolean);
18
+ export interface Plugin<TReq = http.IncomingMessage, TRes = http.ServerResponse> {
19
+ (proxyServer: httpProxy<TReq, TRes>, options: Options<TReq, TRes>): void;
15
20
  }
16
- export declare type Filter = string | string[] | ((pathname: string, req: Request) => boolean);
17
- export declare type Plugin = (proxyServer: httpProxy, options: Options) => void;
18
- export declare type OnProxyEvent = {
19
- error?: httpProxy.ErrorCallback;
20
- proxyReq?: httpProxy.ProxyReqCallback;
21
- proxyReqWs?: httpProxy.ProxyReqWsCallback;
22
- proxyRes?: httpProxy.ProxyResCallback;
21
+ export interface OnProxyEvent<TReq = http.IncomingMessage, TRes = http.ServerResponse> {
22
+ error?: httpProxy.ErrorCallback<Error, TReq, TRes>;
23
+ proxyReq?: httpProxy.ProxyReqCallback<http.ClientRequest, TReq, TRes>;
24
+ proxyReqWs?: httpProxy.ProxyReqWsCallback<http.ClientRequest, TReq>;
25
+ proxyRes?: httpProxy.ProxyResCallback<TReq, TRes>;
23
26
  open?: httpProxy.OpenCallback;
24
- close?: httpProxy.CloseCallback;
25
- start?: httpProxy.StartCallback;
26
- end?: httpProxy.EndCallback;
27
- econnreset?: httpProxy.EconnresetCallback;
28
- };
29
- export declare type Logger = Pick<Console, 'info' | 'warn' | 'error'>;
30
- export interface Options extends httpProxy.ServerOptions {
27
+ close?: httpProxy.CloseCallback<TReq>;
28
+ start?: httpProxy.StartCallback<TReq, TRes>;
29
+ end?: httpProxy.EndCallback<TReq, TRes>;
30
+ econnreset?: httpProxy.EconnresetCallback<Error, TReq, TRes>;
31
+ }
32
+ export type Logger = Pick<Console, 'info' | 'warn' | 'error'>;
33
+ export interface Options<TReq = http.IncomingMessage, TRes = http.ServerResponse> extends httpProxy.ServerOptions {
31
34
  /**
32
35
  * Narrow down requests to proxy or not.
33
36
  * Filter on {@link http.IncomingMessage.url `pathname`} which is relative to the proxy's "mounting" point in the server.
34
37
  * Or use the {@link http.IncomingMessage `req`} object for more complex filtering.
38
+ * @link https://github.com/chimurai/http-proxy-middleware/blob/master/recipes/pathFilter.md
39
+ * @since v3.0.0
40
+ */
41
+ pathFilter?: Filter<TReq>;
42
+ /**
43
+ * Modify request paths before requests are send to the target.
44
+ * @example
45
+ * ```js
46
+ * createProxyMiddleware({
47
+ * pathRewrite: {
48
+ * '^/api/old-path': '/api/new-path', // rewrite path
49
+ * }
50
+ * });
51
+ * ```
52
+ * @link https://github.com/chimurai/http-proxy-middleware/blob/master/recipes/pathRewrite.md
35
53
  */
36
- pathFilter?: Filter;
37
54
  pathRewrite?: {
38
55
  [regexp: string]: string;
39
- } | ((path: string, req: Request) => string) | ((path: string, req: Request) => Promise<string>);
56
+ } | ((path: string, req: TReq) => string | undefined) | ((path: string, req: TReq) => Promise<string>);
40
57
  /**
41
58
  * Access the internal http-proxy server instance to customize behavior
42
59
  *
@@ -50,12 +67,15 @@ export interface Options extends httpProxy.ServerOptions {
50
67
  * }]
51
68
  * });
52
69
  * ```
70
+ * @link https://github.com/chimurai/http-proxy-middleware#plugins-array
71
+ * @since v3.0.0
53
72
  */
54
- plugins?: Plugin[];
73
+ plugins?: Plugin<TReq, TRes>[];
55
74
  /**
56
75
  * Eject pre-configured plugins.
57
76
  * NOTE: register your own error handlers to prevent server from crashing.
58
77
  *
78
+ * @link https://github.com/chimurai/http-proxy-middleware#ejectplugins-boolean-default-false
59
79
  * @since v3.0.0
60
80
  */
61
81
  ejectPlugins?: boolean;
@@ -72,11 +92,25 @@ export interface Options extends httpProxy.ServerOptions {
72
92
  * }
73
93
  * });
74
94
  * ```
95
+ * @link https://github.com/chimurai/http-proxy-middleware/blob/master/recipes/proxy-events.md
96
+ * @since v3.0.0
97
+ */
98
+ on?: OnProxyEvent<TReq, TRes>;
99
+ /**
100
+ * Dynamically set the {@link Options.target `options.target`}.
101
+ * @example
102
+ * ```js
103
+ * createProxyMiddleware({
104
+ * router: async (req) => {
105
+ * return 'http://127:0.0.1:3000';
106
+ * }
107
+ * });
108
+ * ```
109
+ * @link https://github.com/chimurai/http-proxy-middleware/blob/master/recipes/router.md
75
110
  */
76
- on?: OnProxyEvent;
77
111
  router?: {
78
112
  [hostOrPath: string]: httpProxy.ServerOptions['target'];
79
- } | ((req: Request) => httpProxy.ServerOptions['target']) | ((req: Request) => Promise<httpProxy.ServerOptions['target']>);
113
+ } | ((req: TReq) => httpProxy.ServerOptions['target']) | ((req: TReq) => Promise<httpProxy.ServerOptions['target']>);
80
114
  /**
81
115
  * Log information from http-proxy-middleware
82
116
  * @example
@@ -85,6 +119,8 @@ export interface Options extends httpProxy.ServerOptions {
85
119
  * logger: console
86
120
  * });
87
121
  * ```
122
+ * @link https://github.com/chimurai/http-proxy-middleware/blob/master/recipes/logger.md
123
+ * @since v3.0.0
88
124
  */
89
125
  logger?: Logger | any;
90
126
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "http-proxy-middleware",
3
- "version": "3.0.0-beta.0",
3
+ "version": "3.0.0",
4
4
  "description": "The one-liner node.js proxy middleware for connect, express, next.js and more",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -8,21 +8,18 @@
8
8
  "dist"
9
9
  ],
10
10
  "scripts": {
11
- "clean": "rm -rf dist && rm -rf coverage",
11
+ "clean": "rm -rf dist coverage tsconfig.tsbuildinfo .eslintcache",
12
12
  "lint": "yarn prettier && yarn eslint",
13
13
  "lint:fix": "yarn prettier:fix && yarn eslint:fix",
14
- "eslint": "eslint '{src,test}/**/*.ts'",
14
+ "eslint": "eslint '{src,test}/**/*.ts' --cache",
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",
19
- "build": "tsc",
20
- "pretest": "yarn build",
18
+ "build": "tsc --build",
21
19
  "test": "jest",
22
- "precoverage": "yarn build",
23
- "coverage": "jest --coverage --coverageReporters=lcov",
24
- "prepare": "husky install",
25
- "prepack": "yarn build && rm dist/tsconfig.tsbuildinfo",
20
+ "coverage": "jest --coverage",
21
+ "prepare": "husky",
22
+ "prepack": "yarn clean && yarn test && yarn build",
26
23
  "spellcheck": "npx --yes cspell --show-context --show-suggestions '**/*.*'"
27
24
  },
28
25
  "repository": {
@@ -54,58 +51,47 @@
54
51
  },
55
52
  "homepage": "https://github.com/chimurai/http-proxy-middleware#readme",
56
53
  "devDependencies": {
57
- "@commitlint/cli": "16.2.3",
58
- "@commitlint/config-conventional": "16.2.1",
59
- "@types/debug": "4.1.7",
60
- "@types/express": "4.17.13",
61
- "@types/is-glob": "4.0.2",
62
- "@types/jest": "27.4.1",
63
- "@types/micromatch": "4.0.2",
64
- "@types/node": "17.0.25",
54
+ "@commitlint/cli": "17.7.1",
55
+ "@commitlint/config-conventional": "17.7.0",
56
+ "@types/debug": "4.1.12",
57
+ "@types/express": "4.17.21",
58
+ "@types/is-glob": "4.0.4",
59
+ "@types/jest": "29.5.12",
60
+ "@types/micromatch": "4.0.6",
61
+ "@types/node": "20.11.30",
65
62
  "@types/supertest": "2.0.12",
66
- "@types/ws": "8.5.3",
67
- "@typescript-eslint/eslint-plugin": "5.20.0",
68
- "@typescript-eslint/parser": "5.20.0",
69
- "body-parser": "1.20.0",
70
- "browser-sync": "2.27.9",
63
+ "@types/ws": "8.5.10",
64
+ "@typescript-eslint/eslint-plugin": "7.4.0",
65
+ "@typescript-eslint/parser": "7.4.0",
66
+ "body-parser": "1.20.2",
67
+ "browser-sync": "3.0.2",
71
68
  "connect": "3.7.0",
72
- "eslint": "8.13.0",
73
- "eslint-config-prettier": "8.5.0",
74
- "eslint-plugin-prettier": "4.0.0",
75
- "express": "4.17.3",
69
+ "eslint": "8.57.0",
70
+ "eslint-config-prettier": "9.1.0",
71
+ "eslint-plugin-prettier": "5.1.3",
72
+ "express": "4.19.2",
76
73
  "get-port": "5.1.1",
77
- "husky": "7.0.4",
78
- "jest": "27.5.1",
79
- "lint-staged": "12.3.8",
80
- "mockttp": "2.7.0",
81
- "open": "8.4.0",
82
- "prettier": "2.6.2",
83
- "supertest": "6.2.2",
84
- "ts-jest": "27.1.4",
85
- "typescript": "4.6.3",
86
- "ws": "8.5.0"
74
+ "husky": "9.0.11",
75
+ "jest": "29.7.0",
76
+ "lint-staged": "15.2.2",
77
+ "mockttp": "3.10.1",
78
+ "open": "8.4.2",
79
+ "prettier": "3.2.5",
80
+ "supertest": "6.3.4",
81
+ "ts-jest": "29.1.2",
82
+ "typescript": "5.4.3",
83
+ "ws": "8.16.0"
87
84
  },
88
85
  "dependencies": {
89
- "@types/http-proxy": "^1.17.8",
86
+ "@types/http-proxy": "^1.17.10",
90
87
  "debug": "^4.3.4",
91
88
  "http-proxy": "^1.18.1",
92
89
  "is-glob": "^4.0.1",
93
90
  "is-plain-obj": "^3.0.0",
94
91
  "micromatch": "^4.0.5"
95
92
  },
96
- "peerDependencies": {
97
- "@types/express": "^4.17.13"
98
- },
99
- "peerDependenciesMeta": {
100
- "@types/express": {
101
- "optional": true
102
- }
103
- },
104
- "resolutions": {
105
- "browser-sync/portscanner": "2.2.0"
106
- },
107
93
  "engines": {
108
- "node": ">=12.0.0"
94
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
109
95
  },
110
96
  "commitlint": {
111
97
  "extends": [