Skip to content

Cannot proxy WebSockets from Firefox #690

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

Closed
minrk opened this issue Aug 29, 2014 · 8 comments
Closed

Cannot proxy WebSockets from Firefox #690

minrk opened this issue Aug 29, 2014 · 8 comments

Comments

@minrk
Copy link
Contributor

minrk commented Aug 29, 2014

If I try to connect a websocket through a simple proxy from Firefox, it never triggers the upgrade event, nor proxies data to the server.

The Proxy:

httpProxy.createServer({
  target: 'ws://localhost:8001',
  ws: true
}).listen(8000);

Connect with the following js in Firefox:

var ws = new WebSocket('ws://localhost:8000');

There is no output for a few seconds, no data is proxied, then the websocket fails, sending a request with the following headers, which does get proxied

The 'Connection' header is converted from keep-alive, upgrade to close:

{
     "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", 
     "Accept-Encoding": "gzip, deflate", 
     "Accept-Language": "en-US,en;q=0.5", 
     "Cache-Control": "no-cache", 
     "Connection": "close", 
     "Dnt": "1", 
     "Host": "localhost:8000", 
     "Origin": "http://localhost:8000", 
     "Pragma": "no-cache", 
     "Sec-Websocket-Key": "[base64stuff]==", 
     "Sec-Websocket-Version": "13", 
     "Upgrade": "websocket", 
     "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:31.0) Gecko/20100101 Firefox/31.0"
}

The same request proxies just fine from Chrome and Safari, so there's something different about how Firefox initiates websocket requests.

This is with current master (d16062b) node-http-proxy, and nodejs 0.10.30, and Firefox 31.

Update: Fixed symptom description

@jcrugzz
Copy link
Contributor

jcrugzz commented Aug 29, 2014

@minrk this feels like a bug in firefox's websocket implementation as the tests pass with the ws library that we use for testing. Browsers are notorious for having broken websocket implementations. @3rd-Eden what does this look like to you? (@3rd-Eden is realtime web expert)

@minrk
Copy link
Contributor Author

minrk commented Aug 29, 2014

It might be a bug in FF's websocket implementation, but it's not a bug that affects any other ws app I have encountered, so there does seem to be something uniquely brittle in node-http-proxy. Is there an event that fires on every incoming request, so I can start diagnosing the difference between the working and non-working requests?

@3rd-Eden
Copy link
Contributor

@minrk @jcrugzz Firefox should implement the latest specification of the WebSocket protocol, so I don't see anything obvious that should be wrong here.

@3rd-Eden
Copy link
Contributor

@minrk Are you running firefox without any plugins?

@3rd-Eden
Copy link
Contributor

My test code:

'use strict';

var httpProxy = require('http-proxy')
  , WebSocketServer = require('ws').Server;

var app = new WebSocketServer({ port: 8001 });

app.on('connection', function(ws) {
  ws.on('message', function(message) {
    console.log('received: %s', message);
  });
  ws.send('something');
});

httpProxy.createServer({
  target: 'ws://localhost:8001',
  ws: true
}).listen(8000);

Work fine on my firefox: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:31.0) Gecko/20100101 Firefox/31.0"

@minrk
Copy link
Contributor Author

minrk commented Aug 29, 2014

Intriguing. It appears to be relevant that my websocket server behind the proxy is Tornado. Your test runs fine for me, but when it's my Tornado websocket server behind the proxy on 8001, Firefox connections never succeed. Firefox connections directly to the Tornado server, bypassing the proxy, work just fine, but proxied connections are never established. What seems particularly strange is the proxy's HTTP server 'connection' event never fires in the tornado case. How does the backend server participate in the proxy's connection event? Is there an earlier / lower level event than 'connection' that I can look at?

It's never awesome when it takes the interaction of three projects to cause a problem:

  • Tornado + FF = 🆗
  • Tornado + proxy + (Chrome|Safari) = 🆗
  • node-ws + proxy + FF = 🆗
  • Tornado + proxy +FF = 💣

@minrk
Copy link
Contributor Author

minrk commented Sep 2, 2014

I submitted #691, which should fix the bug here.

A summary of the situation:

Chrome

  1. Browser sends Connection: Upgrade
  2. Server receives Connection: Upgrade
  3. 👍

Firefox

  1. Browser sends Connection: keep-alive, upgrade
  2. Server receives Connection: close
  3. depends on server
    • Tornado doesn't see 'upgrade' in the connection header, and terminates the connection
    • node-ws ignores the "Connection" header, and proceeds as normal

If I understand the websocket protocol correctly, there are bugs in both the nodejs websocket package and node-http-proxy, which cancel each other out.

The bug here:

Proxy shouldn't be setting Connection: close in this case.

The (possible) bug in ws:

Websocket server should probably not accept connections if Connection header doesn't contain upgrade, because that seems to be part of the protocol.

Workaround

I found this workaround, which just forces the value to be 'upgrade', since that's the only value that will be handled correctly.

server.on('upgrade', function (req, res, head) {
    req.headers.connection = 'upgrade';
    proxy.ws(req, res, head);
});

A safer version could split on ',', and check for upgrade.

@minrk
Copy link
Contributor Author

minrk commented Sep 30, 2014

This was fixed by #691

@minrk minrk closed this as completed Sep 30, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants