Skip to content
59 changes: 6 additions & 53 deletions lib/Server.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
const os = require("node:os");
const path = require("node:path");
const url = require("node:url");
const util = require("node:util");
const fs = require("graceful-fs");
const ipaddr = require("ipaddr.js");
const { validate } = require("schema-utils");
Expand Down Expand Up @@ -138,14 +137,7 @@ const schema = require("./options.json");
*/

/**
* @callback ByPass
* @param {Request} req
* @param {Response} res
* @param {ProxyConfigArrayItem} proxyConfig
*/

/**
* @typedef {{ path?: HttpProxyMiddlewareOptionsFilter | undefined, context?: HttpProxyMiddlewareOptionsFilter | undefined } & { bypass?: ByPass } & HttpProxyMiddlewareOptions } ProxyConfigArrayItem
* @typedef {{ path?: HttpProxyMiddlewareOptionsFilter | undefined, context?: HttpProxyMiddlewareOptionsFilter | undefined } & HttpProxyMiddlewareOptions } ProxyConfigArrayItem
*/

/**
Expand Down Expand Up @@ -2148,10 +2140,9 @@ class Server {
* @returns {RequestHandler | undefined} request handler
*/
const getProxyMiddleware = (proxyConfig) => {
// It is possible to use the `bypass` method without a `target` or `router`.
// However, the proxy middleware has no use in this case, and will fail to instantiate.
if (proxyConfig.target) {
const context = proxyConfig.context || proxyConfig.path;
const context =
proxyConfig.context || proxyConfig.path || proxyConfig.pathFilter;

return createProxyMiddleware({
...proxyConfig,
Expand All @@ -2162,15 +2153,6 @@ class Server {
if (proxyConfig.router) {
return createProxyMiddleware(proxyConfig);
}

// TODO improve me after drop `bypass` to always generate error when configuration is bad
if (!proxyConfig.bypass) {
util.deprecate(
() => {},
`Invalid proxy configuration:\n\n${JSON.stringify(proxyConfig, null, 2)}\n\nThe use of proxy object notation as proxy routes has been removed.\nPlease use the 'router' or 'context' options. Read more at https://github.com/chimurai/http-proxy-middleware/tree/v2.0.6#http-proxy-middleware-options`,
"DEP_WEBPACK_DEV_SERVER_PROXY_ROUTES_ARGUMENT",
)();
}
};

/**
Expand Down Expand Up @@ -2236,40 +2218,11 @@ class Server {
}
}

// - Check if we have a bypass function defined
// - In case the bypass function is defined we'll retrieve the
// bypassUrl from it otherwise bypassUrl would be null
// TODO remove in the next major in favor `context` and `router` options
const isByPassFuncDefined = typeof proxyConfig.bypass === "function";
if (isByPassFuncDefined) {
util.deprecate(
() => {},
"Using the 'bypass' option is deprecated. Please use the 'router' or 'context' options. Read more at https://github.com/chimurai/http-proxy-middleware/tree/v2.0.6#http-proxy-middleware-options",
"DEP_WEBPACK_DEV_SERVER_PROXY_BYPASS_ARGUMENT",
)();
}
const bypassUrl = isByPassFuncDefined
? await /** @type {ByPass} */ (proxyConfig.bypass)(
req,
res,
proxyConfig,
)
: null;

if (typeof bypassUrl === "boolean") {
// skip the proxy
res.statusCode = 404;
req.url = "";
next();
} else if (typeof bypassUrl === "string") {
// byPass to that url
req.url = bypassUrl;
next();
} else if (proxyMiddleware) {
if (proxyMiddleware) {
return proxyMiddleware(req, res, next);
} else {
next();
}

next();
};

middlewares.push({
Expand Down
96 changes: 7 additions & 89 deletions test/server/proxy-option.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"use strict";

const path = require("node:path");
const util = require("node:util");
const express = require("express");
const request = require("supertest");
const webpack = require("webpack");
Expand All @@ -19,51 +18,14 @@ const proxyOptionPathsAsProperties = [
target: `http://localhost:${port1}`,
},
{
context: "/api/proxy2",
path: "/api/proxy2",
target: `http://localhost:${port2}`,
pathRewrite: { "^/api": "" },
},
{
context: "/foo",
bypass(req) {
if (/\.html$/.test(req.path || req.url)) {
return "/index.html";
}

return null;
},
},
{
context: "proxyfalse",
bypass(req) {
if (/\/proxyfalse$/.test(req.path || req.url)) {
return false;
}
},
},
{
context: "/proxy/async",
bypass(req, res) {
if (/\/proxy\/async$/.test(req.path || req.url)) {
return new Promise((resolve) => {
setTimeout(() => {
res.end("proxy async response");
resolve(true);
}, 10);
});
}
},
},
{
context: "/bypass-with-target",
pathFilter: "**/*.html",
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would expect that using router: "() => http://localhost:${port3}" should work and be somewhat similar to what bypass did, but it doesn’t fully work. I’m still thinking about a way to recreate bypass.

With bypass, the request never went through the proxy, which is why it could return index.html from the webpack-dev-server itself. Now we need to find a way for the request to go through the proxy and still reach the original server, without having to create a new route in the proxy.

target: `http://localhost:${port1}`,
changeOrigin: true,
secure: false,
bypass(req) {
if (/\.(html)$/i.test(req.path || req.url)) {
return req.url;
}
},
pathRewrite: () => "/index.html",
},
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we rewrite these test to using new options from http-proxy-middleware

Also let's add in migration guide how to achive the same behaviour with new http proxy middleware

];

Expand All @@ -77,7 +39,7 @@ const proxyOption = [
let maxServerListeners = 0;
const proxyOptionOfArray = [
{ context: "/proxy1", target: `http://localhost:${port1}` },
function proxy(req, res, next) {
function proxy(req) {
if (req) {
const socket = req.socket || req.connection;
const server = socket ? socket.server : null;
Expand All @@ -92,19 +54,6 @@ const proxyOptionOfArray = [
context: "/api/proxy2",
target: `http://localhost:${port2}`,
pathRewrite: { "^/api": "" },
bypass: () => {
if (req) {
const resolveUrl = new URL(req.url, `http://${req.headers.host}`);
const params = new URLSearchParams(resolveUrl.search);
const foo = params.get("foo");

if (foo) {
res.end(`foo+${next.name}+${typeof next}`);

return false;
}
}
},
};
},
];
Expand Down Expand Up @@ -167,6 +116,9 @@ describe("proxy option", () => {
proxyApp1.get("/api", (req, res) => {
res.send("api response from proxy1");
});
proxyApp1.get("/index.html", (req, res) => {
res.send("Hello");
});
proxyApp2.get("/proxy2", (req, res) => {
res.send("from proxy2");
});
Expand Down Expand Up @@ -248,26 +200,6 @@ describe("proxy option", () => {
});

describe("bypass", () => {
it("should log deprecation warning when bypass is used", async () => {
const utilSpy = jest.spyOn(util, "deprecate");

const response = await req.get("/foo/bar.html");

expect(response.status).toBe(200);
expect(response.text).toContain("Hello");

const lastCall = utilSpy.mock.calls[utilSpy.mock.calls.length - 1];

expect(lastCall[1]).toBe(
"Using the 'bypass' option is deprecated. Please use the 'router' or 'context' options. Read more at https://github.com/chimurai/http-proxy-middleware/tree/v2.0.6#http-proxy-middleware-options",
);
expect(lastCall[2]).toBe(
"DEP_WEBPACK_DEV_SERVER_PROXY_BYPASS_ARGUMENT",
);

utilSpy.mockRestore();
});

it("can rewrite a request path", async () => {
const response = await req.get("/foo/bar.html");

Expand Down Expand Up @@ -295,13 +227,6 @@ describe("proxy option", () => {
expect(response.status).toBe(404);
});

it("should wait if bypass returns promise", async () => {
const response = await req.get("/proxy/async");

expect(response.status).toBe(200);
expect(response.text).toContain("proxy async response");
});

Comment on lines -298 to -304
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neither router nor pathRewrite can send data directly since they don’t have access to the res object. So either it gets removed, or a new route is created in the proxy — but I’m not sure what the point of that would be.

it("should work with the 'target' option", async () => {
const response = await req.get("/bypass-with-target/foo.js");

Expand Down Expand Up @@ -498,13 +423,6 @@ describe("proxy option", () => {
expect(response.text).toContain("from proxy2");
});

it("should allow req, res, and next", async () => {
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const response = await req.get("/api/proxy2?foo=true");

expect(response.statusCode).toBe(200);
expect(response.text).toBe("foo+next+function");
});

it("should not exist multiple close events registered", async () => {
expect(maxServerListeners).toBeLessThanOrEqual(1);
});
Expand Down
Loading