create-react-app icon indicating copy to clipboard operation
create-react-app copied to clipboard

Setting "proxy" in package.json Fails for WebSockets

Open kent-h opened this issue 7 years ago • 49 comments
trafficstars

Expected Behavior

Setting "proxy" in package.json should proxy WebSockets to specified server. (As documented here: "The proxy option supports HTTP, HTTPS and WebSocket connections.")

Actual Behavior

Proxy does not work for WebSocket connections.

Reproducible Demo

No time for this atm. ¯\(ツ)

kent-h avatar Oct 03 '18 19:10 kent-h

Did it work in 1.x? Or is this a regression?

gaearon avatar Oct 03 '18 19:10 gaearon

Current workaround:

Create src/setupProxy.js, with:

const proxy = require("http-proxy-middleware")

module.exports = app => {
  app.use(proxy("/websocket", {target: "http://localhost:8080", ws: true}))
}

kent-h avatar Oct 03 '18 19:10 kent-h

In 1.x, it was possible to specify more advanced options, including "ws":true.

Though I don't know what the handling by the simple "proxy": < url > was.

kent-h avatar Oct 03 '18 19:10 kent-h

OK, sounds like this isn't a regression but a proposal to auto-detect and proxy websockets in the simple proxy mode.

Timer avatar Oct 03 '18 19:10 Timer

At minimum, reality is out of sync with the documentation. (Ended up wasting a couple hours trying to figure out why I couldn't connect.)

Though I would recommend making WebSocket proxying work by default.

kent-h avatar Oct 03 '18 19:10 kent-h

It was supposed to work in 1.x too.

gaearon avatar Oct 04 '18 15:10 gaearon

I reproduce with issue.

  app.use(proxy('/ws', {
    target: 'http://localhost:3007',
    ws: true,
  }));
[HPM] Upgrading to WebSocket
events.js:167
      throw er; // Unhandled 'error' event
      ^

Error: read ECONNRESET
    at TCP.onStreamRead (internal/stream_base_commons.js:111:27)
Emitted 'error' event at:
    at emitErrorNT (internal/streams/destroy.js:82:8)
    at emitErrorAndCloseNT (internal/streams/destroy.js:50:3)
    at process._tickCallback (internal/process/next_tick.js:63:19)

avdeev avatar Oct 09 '18 13:10 avdeev

I can confirm that the following setup worked in 1.x:

"proxy": {
  "/api": {
    "target": "ws://localhost:4000",
     "ws": true
   }
}

but this no longer works in 2.1.1's package.json: "proxy": "ws://localhost:4000"

I get the following error message upon npm start: When "proxy" is specified in package.json it must start with either http:// or https://

That said, I will be happy to test any fix you throw at my direction.

ivosh avatar Nov 08 '18 20:11 ivosh

Hey, I tried to investigate some time to analyze this issue and found a possible fix. Would be great if some of you could test it. Once it works for you too, I will open a PR. https://github.com/mxschmitt/create-react-app/commit/acc5ab13da20d777012f53578b76266d5521cff0 To test it, simply edit the following file: node_modules/react-dev-utils/WebpackDevServerUtils.js and add to line 327 to the return condition req.upgrade || like in the commit above. Best regards Max

mxschmitt avatar Nov 15 '18 07:11 mxschmitt

@mxschmitt, thank you for your email. I've tried your patch but it did not work for me. I was still receiving error message: When "proxy" is specified in package.json it must start with either http:// or https://.

So I've hacked the following fix and it works for my setup: ivosh@1fa22ae7a2c54903f6c69f60f8b29c24487ff812 I've also tried to incorporate your patch into it and observed no difference (works with and also without).

ivosh avatar Nov 15 '18 10:11 ivosh

@ivosh You don't have to use ws:// as protocol for your websocket. Just use http:// which will work fine in my case.

mxschmitt avatar Nov 15 '18 12:11 mxschmitt

@mxschmitt, you are right, thank you for pointing this out. So in my case, even when all communication happens over websockets, specifying "proxy": "http://localhost:4000" is sufficient for the communication to be proxied. Your change is not needed in my setup.

ivosh avatar Nov 15 '18 18:11 ivosh

+1'ing the change @mxschmitt is proposing, this does fix the issue for me.

For background, the problem locally is that firefox is sending 'text/html' in 'Accept' when opening the websocket connection, which causes the default proxy heuristic to -not- proxy the request:

    return (
      req.method !== 'GET' ||
      (mayProxy(pathname) &&
        req.headers.accept &&
        req.headers.accept.indexOf('text/html') === -1)
    );

I agree the presence of the 'upgrade' header should force proxying just like a non-GET request, unless there's a case I'm missing.

bsorbo avatar Nov 16 '18 18:11 bsorbo

Yey, the stale bot has closed my PR: #5841....

mxschmitt avatar Feb 07 '19 08:02 mxschmitt

I've been wrestling with this issue for a while now, and the workarounds are very annoying, especially when you have cookies involved. Is there any reason why #5841 isn't being merged? It looks like one of the builds failed, but it seems to be an issue completely unrelated to the code change in that PR.

@mxschmitt maybe if you add a comment to the code in your PR, the CI will rerun and hopefully there won't be any build issues this time?

jamescostian avatar Feb 20 '19 19:02 jamescostian

Try this: http://saule1508.github.io/create-react-app-proxy-websocket/

dvlpr-eth avatar Aug 27 '19 14:08 dvlpr-eth

@hamidnoei This workaround has already been discussed.

kent-h avatar Sep 05 '19 17:09 kent-h

@avdeev Alexey, have you found a solution for the issue you described in this comment: https://github.com/facebook/create-react-app/issues/5280#issuecomment-428199778 ? I have got the same.

avkonst avatar Dec 18 '19 23:12 avkonst

Any updates on this?

affanshahid avatar Mar 18 '20 07:03 affanshahid

I was having a similar problem of not being able to proxy websocket connections and did a little digging. My finding was that the context property being returned by prepareProxy in WebpackDevServerUtils.js is returning undefined for websocket connections. This is because the standard headers for a websocket created through the WebSocket class constructor do not include an Accept header. Since this function returns undefined, which is equivalent to false, the websocket connection is not proxied. I don't know if this is the place to fix the issue, but I managed to get it working for myself by adding a check for a websocket request. Something like checking for the Upgrade header being set to "websocket":

function(pathname, req) {
  return (
    req.method !== "GET" ||
    (mayProxy(pathname) &&
      ((req.headers.upgrade && req.headers.upgrade === "websocket") ||
        (req.headers.accept && req.headers.accept.indexOf("text/html") === -1)))
  );
}

dylansteele8 avatar Mar 20 '20 23:03 dylansteele8

I had a problem where I couldn't proxy requests (in development) to my own websocket (socket-io) server.

I followed the steps here: create-react-app proxying requests in dev, my setupProxy.js looked like this:

The below did not work for proxying a websocket connection:

const { createProxyMiddleware } = require('http-proxy-middleware');

module.exports = function (app) {
  app.use(
    '/api',
    createProxyMiddleware({
      target: 'http://localhost:5011',
    }),
  );
  app.use(
    '/socket-io',
    createProxyMiddleware({
      target: 'http://localhost:5011',
      ws: true,
    }),
  );
};

However, the hot reloading wouldn't work because somehow this interfered with webpack dev servers websocket connection. I would see this error in the console:

[HPM] Error occurred while trying to proxy request /sockjs-node from localhost:3000 to http://localhost:5011 (ECONNRESET) (https://nodejs.org/api/errors.html#errors_common_system_errors)

I got this working by doing the following:

const isDev = process.env.NODE_ENV === 'development';

const socket = isDev 
? io.connect('http://localhost:5011', { path: '/socket-io' }) 
: io.connect({ path: '/socket-io' });

Hope this helps someone out! Let me know if there's a better way to do this 👍

neilkuumar avatar Apr 08 '20 08:04 neilkuumar

Why is it still not resolved in 2020? Why fixing PR #5841 was rejected? @Timer @gaearon

CyanoFresh avatar Apr 17 '20 18:04 CyanoFresh

+1

najibghadri avatar Apr 19 '20 18:04 najibghadri

+1

goldmont avatar May 03 '20 14:05 goldmont

FYI according to #6497 there was a new PR #6515 but that has been closed without comment after the stale bot flagged it again.

aaroncowie avatar May 07 '20 14:05 aaroncowie

+1

robyn3choi avatar Jun 10 '20 17:06 robyn3choi

Workaround: Use Firefox, for some reason this feature is not broken on Firefox.

I typically do my local development on Firefox, and websocket proxying works there out of the box, and I was surprised to see that this was broken on Chrome, wasted alot of hours on this bug

ferrybig avatar Jun 19 '20 14:06 ferrybig

The docs for proxy linked to in the original description have moved here.

williamstein avatar Jun 27 '20 21:06 williamstein

Verified this is broken in Chrome 77.0.3865.75 (Official Build) (64-bit) and working in FireFox 76.0.1 (64-bit)

Agree with @Kent-H - preferred behavior would be "work out of the box" Interesting its a Chrome only issue

Burned hours thinking it was my code preventing the socket connection.

I'd give big nerd creds to anyone involved in fixing it !

supertick avatar Jul 11 '20 20:07 supertick

Current workaround:

Create src/setupProxy.js, with:

const proxy = require("http-proxy-middleware")

module.exports = app => {
  app.use(proxy("/websocket", {target: "http://localhost:8080", ws: true}))
}

can u tell me how can i use setupProxy.js and i removed proxy from packege.json so how can i redireect my api to proxy

opandey007 avatar Aug 17 '20 14:08 opandey007