http-proxy-middleware: Proxying websockets to multiple backends doesn't work

Is this a bug report?

Yes

Steps to reproduce

  1. Set up a proxy server with
app.use(
  '/sockjs-node',
  createProxyMiddleware('/sockjs-node', {
    target: 'http://127.0.0.1:3000',
    ws: true,
  })
);
app.use(
  '/socket.io',
  createProxyMiddleware('/socket.io', {
    target: 'http://127.0.0.1:4000',
    ws: true,
  })
);

The /sockjs-node server can be quickly spun up with create-react-app since that is the socket for Webpack hot reload. /socket.io can be quickly spun up with a socket.io example server.

  1. Load up the React app in your browser. The /sockjs-node requests are not proxied correctly. The HPM log output shows that it is trying to proxy the /sockjs-node request to port 4000, resulting in an ECONNREFUSED error.

Setup

  • http-proxy-middleware: 1.0.5
  • http-proxy-middleware configuration: see above
  • server: express 4.17.1

client info

Chrome (latest) / Ubuntu 20.04 (Linux)

target server info

One is a webpack dev server, one is a simple express server with socket.io config

Reproducible Demo

https://github.com/bduffany/hpm-bug

About this issue

  • Original URL
  • State: open
  • Created 4 years ago
  • Reactions: 9
  • Comments: 15

Commits related to this issue

Most upvoted comments

I found that if I change

app.use(
  '/sockjs-node',
  createProxyMiddleware('/sockjs-node', {
    target: 'http://127.0.0.1:3000',
    ws: true,
  })
);

to

app.use(
  createProxyMiddleware('/sockjs-node', {
    target: 'http://127.0.0.1:3000',
    ws: true,
  })
);

Then the issue is fixed. No ideas yet as to why. However, I think this fix results in worse performance, because express is now going to be running that proxy middleware on every request, even ones that don’t match /sockjs-node. It might be OK though if you’re only using this for local development.

You just saved my day with your fix. Spent 3 hours because I had this exact problem.

When proxying to several websocket servers Chrome was showing errors like “Invalid frame header” or “WebSocket connection failed: One or more reserved bits are on: reserved1 = 0, reserved2 = 1, reserved3 = 1” which googling them up led to absolute non-sense.

I think this issue should really be patched up.

Multiple websoket proxies cause self or server errors

const express = require(`express`)
const { createProxyMiddleware } = require(`http-proxy-middleware`)
const p1 = createProxyMiddleware({
  target: `http://127.0.0.1:9000/`,
  changeOrigin: true,
  ws: true,
  logger: console,
  pathRewrite: {
    "^/a": `http://127.0.0.1:9000/asr`, // new WebSocket(`ws://127.0.0.1:3000/a/socket/x`)
  },
})
const p2 = createProxyMiddleware({
  target: `http://127.0.0.1:9000/`,
  changeOrigin: true,
  ws: true,
  logger: console,
  pathRewrite: {
    "^/b": `http://127.0.0.1:9000/im`, // new WebSocket(`ws://127.0.0.1:3000/b/frontend/x`)
  },
})

const app = express()
app.use(`/a`, p1)
app.use(`/b`, p2)

const server = app.listen(3000)
server.on(`upgrade`, p1.upgrade)
server.on(`upgrade`, p2.upgrade)

test

new WebSocket(`ws://127.0.0.1:9000/asr/socket/x`) // raw ok
new WebSocket(`ws://127.0.0.1:3000/a/socket/x`) // err
new WebSocket(`ws://127.0.0.1:9000/asr/socket/x`) // raw err

env

  • node v14.15.5
  • http-proxy-middleware 2.0.6

I have the same issue and the @marcinmajkowski 's comment above really helped me to solve the problem. The difference was only that I have 2 different servers to be proxied so I have to route upgrade requests to proper proxy servers:

const p1 = createProxyMiddleware('http://127.0.0.1:8000/', { ws: false });
const p2 = createProxyMiddleware('http://127.0.0.1:9000/', { ws: false });

const app = express();
app.use('/a', p1);
app.use('/b', p2);

server.on('upgrade', function(req, socket, head) {
    if (req.url.indexOf('/a') === 0) {
        p1.upgrade(req, socket, head);
    }
});
server.on('upgrade', function(req, socket, head) {
    if (req.url.indexOf('/b') === 0) {
        p2.upgrade(req, socket, head);
    }
});

To understand the issue, it is worth to take look at the implementation.

Then one realizes that doing:

const p1 = createProxyMiddleware('http://127.0.0.1:9000/', { ws: true });
const p2 = createProxyMiddleware('http://127.0.0.1:9000/', { ws: true });

const app = express();
app.use(`/a`, p1);
app.use(`/b`, p2);

has same effect as doing:

const p1 = createProxyMiddleware('http://127.0.0.1:9000/', { ws: false });
const p2 = createProxyMiddleware('http://127.0.0.1:9000/', { ws: false });

const app = express();
app.use(`/a`, p1);
app.use(`/b`, p2);

server.on('upgrade', p1.upgrade);
server.on('upgrade', p2.upgrade);

Which makes it clear that in case of WebSockets, express routing is not respected.

Knowing that, in my case, I was able to workaround this by setting ws: false and by handling 'upgrade' routing to correct proxy middleware in my application.

I am not really sure what would be a clean solution to this issue as upgrade requests are not routed in express. WebSocket handling in this library seem a bit hacky already as (to achieve something which seems not supported in express) it has to access server through req:

https://github.com/chimurai/http-proxy-middleware/blob/master/src/http-proxy-middleware.ts#L61

Setting ws: false and doing this explicitly in application seem like a cleaner solution as it also makes it possible to unregister the listener in case proxy is used dynamically (e.g. there is an if selecting different proxy instance on some condition at runtime).

+1

FYI added a reproducible demo repro here: https://github.com/bduffany/hpm-bug

One liner you can throw in your terminal to get it up and running, ready to debug in Chrome:

git clone https://github.com/bduffany/hpm-bug && cd hpm-bug && ((sleep 8 && google-chrome --auto-open-devtools-for-tabs http://localhost:8080) &) ; ./run.sh

I am having the same issue(s) reported here - webpack-dev-server and thus webpack/angular are using this library (see related bug). Is there a plan to fix this issue anytime in the future?