Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Setting "proxy" in package.json Fails for WebSockets #5280

Open
kent-h opened this issue Oct 3, 2018 · 51 comments
Open

Setting "proxy" in package.json Fails for WebSockets #5280

kent-h opened this issue Oct 3, 2018 · 51 comments
Milestone

Comments

@kent-h
Copy link

kent-h commented Oct 3, 2018

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. ¯\(ツ)

@gaearon
Copy link
Contributor

gaearon commented Oct 3, 2018

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

@kent-h
Copy link
Author

kent-h commented Oct 3, 2018

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}))
}

@Timer Timer added this to the 2.x milestone Oct 3, 2018
@kent-h
Copy link
Author

kent-h commented Oct 3, 2018

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.

@Timer
Copy link
Contributor

Timer commented Oct 3, 2018

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

@kent-h
Copy link
Author

kent-h commented Oct 3, 2018

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.

@gaearon
Copy link
Contributor

gaearon commented Oct 4, 2018

It was supposed to work in 1.x too.

@Timer Timer modified the milestones: 2.x, 2.0.x Oct 4, 2018
@avdeev
Copy link

avdeev commented Oct 9, 2018

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)

@ivosh
Copy link

ivosh commented Nov 8, 2018

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.

@mxschmitt
Copy link

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

@ivosh
Copy link

ivosh commented Nov 15, 2018

@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).

@mxschmitt
Copy link

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

@ivosh
Copy link

ivosh commented Nov 15, 2018

@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.

@bsorbo
Copy link

bsorbo commented Nov 16, 2018

+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.

@mxschmitt
Copy link

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

@jamescostian
Copy link

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?

@dvlpr-eth
Copy link

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

@kent-h
Copy link
Author

kent-h commented Sep 5, 2019

@hamidnoei This workaround has already been discussed.

@avkonst
Copy link

avkonst commented Dec 18, 2019

@avdeev Alexey, have you found a solution for the issue you described in this comment: #5280 (comment) ? I have got the same.

@ferrybig
Copy link

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

@williamstein
Copy link

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

@supertick
Copy link

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 !

@opandey007
Copy link

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

@davidmwhynot
Copy link

I can also confirm this is broken in chrome but working in firefox. This is probably unrelated, but I have HTTPS=true, which is working for wss on firefox. I say it's unrelated b/c chrome breaks when using ws or wss.

At the very least, the docs should be updated to indicate that this is currently broken for websockets in chrome. It's a little misleading to say that it works websockets when that's only true for certain browsers.

@MikeDev96
Copy link

I also have the same issue, proxy not working for Chrome but is for Firefox.

@gregorbg
Copy link

gregorbg commented Oct 1, 2020

+1

@ovasylenko
Copy link

any news on the issue?

@thecactoos
Copy link

+1

@DominicTobias-b1
Copy link

DominicTobias-b1 commented Feb 22, 2021

The issue for me is that the websocket request never reaches the proxy, e.g.

module.exports = app => {
  app.use((req, res, next) => {
    console.log(req.url)
    next()
  })
}

I see HTTP requests there but none on the localhost:{port} for ws or wss?

The only solution I can think of atm is to start another local server in setupProxy.js just to proxy websocket requests on another port since they aren't passed through here?

@lanshunfang
Copy link

According to the doc, this works for me:

    app.use(
        createProxyMiddleware(
           "/subscriptions",
            {
                target: 'http://localhost:4000',
                ws: true
            })
    );

Note that, the "/subscriptions", should be moved into createProxyMiddleware, not within app.use directly.

@floriancargoet
Copy link

floriancargoet commented May 5, 2021

In WebpackDevServerUtils.js, the context() function, which decides if a request must be proxied, returns a different result between Chrome and Firefox.
It's because in the case of a WebSocket, Chrome doesn't send the "Accept" header but Firefox does (at least in my tests with socket.io).
If I change the function to return true when there's no accept header, my websocket connection is proxied as expected.

@floriancargoet
Copy link

My solution was to use both techniques:

  • add "proxy": "http://localhost:8000" in package.json, to handle most cases
  • add a src/setupProxyFile.js just for socket.io :
const createProxyMiddleware = require("http-proxy-middleware");

module.exports = function (app) {
  app.use(
    createProxyMiddleware("/socket.io/", {
      target: "http://localhost:8000/",
      ws: true,
    })
  );
};

Hope it helps someone.

@no1melman
Copy link

When I did the above steps my websocket to my dotnet app worked a treat - however, now my hot reloading isn't working with this error

webpackHotDevClient.js:60 WebSocket connection to 'ws://localhost:3000/sockjs-node' failed: Invalid frame header

Anyone with an idea on what is happening?

my setup

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

module.exports = function (app) {
  app.use(
    "/api",
    proxy({
      target: "http://localhost:5000",
      changeOrigin: true,
    })
  );
  app.use(
    "/ws",
    proxy({ target: "http://localhost:5000", ws: true, changeOrigin: true })
  );
};

@krodyrobi
Copy link

@no1melman I also see the same issue as you.
What I could tell is that somehow when the /ws proxy gets upgraded it also tries to upgrade /sockjs-node or something along these lines.

Adding a ws: true proxy in setupProxy,js and using the path will cause the live reload socket to fail.
One does not need to have an outgoing ws to the path any request will cause the issue.
Paths and rewrites seem to be of no significance.

Socket implementation: socket.io v4

@krodyrobi
Copy link

@no1melman not sure if this helps but for some reason the following no longer affects sockjs and forwards as expected..
Not even sure how it differs (stumbled upon it randomly on stackoverflow) but it does, you can also try it.

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

module.exports = function (app) {
  app.use(
    proxy("/api", {        // <-- notice the pattern is not in use but in the proxy method
      target: "http://localhost:5000",
      changeOrigin: true,
    })
  );
  app.use(
    proxy("/ws", {          // <-- notice the pattern is not in use but in the proxy method
      target: "http://localhost:5000",
      ws: true,
      changeOrigin: true
    })
  );
};

@sancelot
Copy link

sancelot commented Jul 8, 2021

My question may be silly, but I have multiple websockets in my backend :
a broadcast server on eg port 5000
a raw socket on port 5001
and json rpc websocket on port 5002

How to deal with all these ??????

@Eliarh
Copy link

Eliarh commented Aug 1, 2021

+1

@lilrooness
Copy link

+1

@AaronNGray
Copy link

with :-

  "proxy": {
    "/graphql": {
      "target": "http://localhost:3030",
      "ws": true
    }
  }

I am getting the following :-

$ react-scripts start
(node:16776) [DEP0111] DeprecationWarning: Access to process.binding('http_parser') is deprecated.
(Use `node --trace-deprecation ...` to show where the warning was created)
When specified, "proxy" in package.json must be a string.
Instead, the type of "proxy" was "object".
Either remove "proxy" from package.json, or make it a string.
error Command failed with exit code 1.

@AaronNGray
Copy link

AaronNGray commented May 21, 2022

With Chrome and ApolloServer and plain "proxy": "http://localhost:3030" I am getting :-

WebSocket connection to 'ws://localhost:3000/sockjs-node/023/0qlhbfvd/websocket' failed: WebSocket is closed before the connection is established.
push../node_modules/sockjs-client/lib/transport/websocket.js.WebSocketTransport.close @ websocket.js:80

Similar on FireFox :-

The connection to ws://localhost:3000/sockjs-node/521/go41gmjr/websocket was interrupted while the page was loading.

@AaronNGray
Copy link

AaronNGray commented May 21, 2022

Okay I tried #5280 (comment) and it worked with latest subscriptions implementation on Apollo Server on FireFox and Chrome, but not on MS Edge. I will have to look into that later its not throwing up any errors, but is not working on MS Edge, it might be my code seeing as how near Chrome and Edge are.
On adding credentials: 'include' to new HttpLink MS Edge started working too. But found out my subscriptions were not going through websockets anyway : |

@Diaver
Copy link

Diaver commented Sep 2, 2022

My Finally workable version for ASP.NET Core 6:

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

const target = env.ASPNETCORE_HTTPS_PORT
    ? `https://localhost:${env.ASPNETCORE_HTTPS_PORT}`
    : env.ASPNETCORE_URLS ? env.ASPNETCORE_URLS.split(';')[0] : 'http://localhost:40239';


const context = [
    "/test"
];

module.exports = function (app) {

    app.use(createProxyMiddleware(context, {
        target: target,
        secure: false,
        headers: {
            Connection: 'Keep-Alive'
        }
    }));

    app.use(createProxyMiddleware("/hubs/", {
        target: target,
        ws: true,
        secure: false
    }))
};

@yairyairyair
Copy link

i also saw that the problem is still active and is on the context function on react-dev-utils/WebpackDevServerUtils
i think this line should be added: "(req.headers.upgrade === 'websocket' && !pathname.startsWith('/ws')) ||"
thereby solving the websocket problem by returning true for
added this locally and it works,

should create a PR for it?

@ThatCanadianGuy
Copy link

+1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.