chimurai / http-proxy-middleware

:zap: The one-liner node.js http-proxy middleware for connect, express, next.js and more
MIT License
10.71k stars 834 forks source link

express.use(path, proxy()) ignores path for websockets #204

Open ftes opened 7 years ago

ftes commented 7 years ago

Also see expressjs/express#3428.

Expected behavior

The following should listen for websocket requests only under /__webpack_hmr.

express.use('/__webpack_hmr', proxy({ ws: true, target: 'http://...' }));

Websocket requests to other paths (e.g. /socket.io) should not be handled by the proxy.

Actual behavior

The proxy "hijacks" all incoming websocket requests, also those to /socket.io (see socketio/socket.io#3062).

Setup

Fiddle: https://github.com/ftes/socket.io-fiddle/pull/1/files

proxy middleware configuration

proxy({ ws: true, target: 'http://localhost:4000' });

server mounting

var app = express();

app.use('/__webpack_hmr', proxy);
app.listen(3000);

Workaround

Pass path /__webpack_hmr as context to proxy.

const webpackHmrProxy = proxy('/__webpack_hmr', {
  target: 'http://localhost:4000',
  ws: true,
});
app.use(webpackHmrProxy);
chimurai commented 7 years ago

Thanks for reporting this @ftes . I'll have to investigate this.

roccomuso commented 7 years ago

I'm having the same issue.. I kinda fixed it passing the "context" twice:

express.use('/__webpack_hmr', proxy('/__webpack_hmr', { ws: true, target: 'http://...' }))

@ftes give it a try.

mattbrunetti commented 6 years ago

Slightly off topic:

@roccomuso You don't need the first occurrence of "context" (passed to express.use) though do you?

I would have thought it would work the same (with or without passing context to express.use), but for some reason, when I pass the context in both ways, and I restart the server, reconnection in the browser fails with WebSocket connection to 'ws://localhost:3000/api/socket.io/?EIO=3&transport=websocket' failed: Connection closed before receiving a handshake response even after I refresh the page. If I only pass the context to proxy then I get those errors only until I refresh the page. My head hurts. No idea what's going on.

janneh commented 6 years ago

I'm experiencing a similar issue.

Where:

app.use("/example", proxy({ target: "www.example.com" }))

Gives:

[HPM] Proxy created: /  ->  http://www.example.com

And

app.use(proxy("/example", { target: "www.example.com" }))

Gives:

[HPM] Proxy created: /example  ->  http://www.example.com
"node": "8.9.0"
"express": "^4.16.2",
"http-proxy-middleware": "^0.17.4",

Just wanted to put it here since the README example describes it as the first one...

DonovanCharpin commented 5 years ago

Oh my god, thanks @roccomuso!

I was using this code which was working fine when the socket had a successful connection but was throwing an error when the WS_URI was returning an error (404, 500 ...)

server.use(proxy("/ws", {
    target: process.env.WS_URI,
    ws: true
  })
);

I just updated my code with this code and it worked. The second route in the proxy method doesn't seem to be required though. I didn't see my route was not handled by express...

server.use("/ws", proxy("/ws", {
    target: process.env.WS_URI,
    ws: true
  })
);
kdemarest commented 5 years ago

I have the same issue., BUT http-proxy-middleware WORKS when I proxy traffic from "/".

In the broken case, HTTP traffic routes through the proxy, but the proxy fails to upgrade to websockets.

Here is my use() statement. it appears before all other use() statements.:

let wsProxy = Proxy( '/shadowStone', {
    ws: true,
    target: 'ws://127.0.0.1:3000',
    pathRewrite: {
        '^/shadowStone': '',
    },
    logLevel: 'debug',
});
app.use( '/shadowStone', wsProxy );

And, just in case, right after the server.listen() I have put:

server.on('upgrade', wsProxy.upgrade)

The error I am seeing is:

404 error attempting to reach
?EIO=3&transport=polling&t=MgVfhrd
/socket.io

And a later use() statement reports that it is seeing the following:

GET /socket.io/?EIO=3&transport=polling&t=MgVfhrd

When I change my code to read as follows, it WORKS just fine:

    let wsProxy = Proxy( '/', {
        ws: true,
        target: 'ws://127.0.0.1:3000',
//      pathRewrite: {
//          '^/shadowStone': '',
//      },
        logLevel: 'debug',
    })

    app.use( '/', wsProxy );
kdemarest commented 5 years ago

I found my issue. I was using socket.io, which is very particular about how you initialize it. In the target of the proxy, in index.html, I was starting socket.io with:

this.socket = io.connect('http://localhost:3000/shadowStone');

when I should have been starting it with:

this.socket = io.connect('http://localhost:3000', {'path': '/shadowStone/socket.io'});

and the server-side companion code to this is

app.use( '/shadowStone', Express.static('.') )
let server = app.listen(3000)
io = SocketIo(server, {path:'/shadowStone/socket.io'});

In my proxy, the code is quite simple:

let wsProxy = Proxy({
    target: 'ws://localhost:3000',
    logLevel: 'debug',
});

app.use( '/shadowStone', wsProxy );
let server = app.listen(port);
server.on('upgrade', wsProxy.upgrade)

Hope this helps somebody with a similar problem...

rafalp commented 4 years ago

Is there any workaround for this in current version? I'm trying to proxy WS to my server in create-react-app without breaking reloader that listens on different path.

rafalp commented 4 years ago

I've changed this:


module.exports = function(app) {
  app.use(
    '/sockjs-node',
    createProxyMiddleware(
      {
        target: 'ws://localhost:3000',
        ws: true,
      }
    )
  );
  app.use(
    '/graphql',
    createProxyMiddleware(
      {
        target: 'http://localhost:8000',
        changeOrigin: true,
        ws: true,
      }
    )
  );
};

to this:


module.exports = function(app) {
  app.use(
    '/sockjs-node',
    createProxyMiddleware(
      '/sockjs-node',
      {
        target: 'ws://localhost:3000',
        ws: true,
      }
    )
  );
  app.use(
    '/graphql',
    createProxyMiddleware(
      '/graphql',
      {
        target: 'http://localhost:8000',
        changeOrigin: true,
        ws: true,
      }
    )
  );
};

...and its working now :)