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

websocket problems 502 #780

Open
jmls opened this issue Aug 21, 2015 · 30 comments
Open

websocket problems 502 #780

jmls opened this issue Aug 21, 2015 · 30 comments

Comments

@jmls
Copy link

jmls commented Aug 21, 2015

I've logged an issue here (chimurai/http-proxy-middleware#15) where I am having a problem with browser-sync, http-proxy-middleware and websockets

In a nutshell, I have this code

var server = {
    baseDir: baseDir,
    routes: routes
  };

  var proxies = [];

  proxies.push(proxyMiddleware('/socket.io', { 
        target: 'http://localhost:5000/' , ws: true
  }));

  server.middleware = proxies;

  browserSync.instance = browserSync.init({
    startPath: '/',
    server: server,
    browser: browser
  });

but get these errors :

woot! connection
[HPM] Upgrading to WebSocket
[HPM] Client disconnected

and the client console has

WebSocket connection to 'ws://mysite.io/socket.io/?EIO=3&transport=websocket&sid=5_BeDfck0LFYcYxPAAAA' failed: Error during WebSocket handshake: Unexpected response code: 502

and obviously, I can't get my websocket connected.

Am I missing something or is it a bug ?

thanks

@xak2000
Copy link

xak2000 commented Aug 21, 2015

Same problem with similar configuration but using sock.js.
I also tried to use raw websockets.
Also I tried using different proxy middlewares: proxy-middleware, http-proxy-middleware.
I even tried using express + http-proxy-middleware as middleware of browser-sync but with no luck. All http requests proxying fine with all this configurations. But not Websocket request.

This is example of my try using express + http-proxy-middleware:

var express = require('express')
  , proxy = require('http-proxy-middleware');

    $.browserSync({
      host: config.host,
      open: 'external',
      port: config.port,
      ws: true,
      server: {
        baseDir: config.buildDir,
        middleware: [
          (function () {
            var app = express()
              , context = '/api'
              , options = {
              target: 'http://localhost:8080',
              changeOrigin: true,
              ws: true, // proxy websockets
              pathRewrite: {
                '^/api': ''
              }
            };
            app.use(proxy(context, options));
            return app;
          })()
        ],
        ws: true
      }
    });

The error is

WebSocket connection to 'ws://192.168.250.3:3000/api/ws/023/xddwmfnu/websocket' failed: Connection closed before receiving a handshake response

@chimurai
Copy link

Websockets are fixed in proxy mode BrowserSync v2.8.1. (40017b4)

Wondering if it is the same issue in server mode.

Background info on websocket fix in proxy mode: #625 (comment)

@shakyShane
Copy link
Contributor

@jmls or @xak2000 Can someone provide a small sample app showing this problem, this will enable me to debug it much faster

@xak2000
Copy link

xak2000 commented Aug 31, 2015

I cloned http-proxy-middleware repository and add example with browser-sync + websocket together.

To run it:

$ git clone https://github.com/xak2000/http-proxy-middleware.git
$ cd http-proxy-middleware
$ npm install

Then run browser-sync-ws example:

$ node examples/browser-sync-ws

You can open browser console and see the error:

WebSocket connection to 'ws://localhost:3000/ws' failed: Connection closed before receiving a handshake response

This example proxies all requests to ws://localhost:3000/ws to echo.websocket.org.

@micheledisalvatore
Copy link

I've the same problem, any news about a fix? Here my code, that works except for the error in object

var proxyMiddleware = require('http-proxy-middleware');

[...]
    browserSync: {
      server: {
        bsFiles: {
          src: ['./dist/{**/*,*}.js', './dist/{**/*,*}.html', './dist/{**/*,*}.css']
        },
        options: {
          watchTask: true,
          logLevel: "info",
          logConnections: true,
          open: true,
          notify: false,
          port: 9000,
          logFileChanges: false,
          server: {
            baseDir: './dist',
            middleware: [
              proxyMiddleware('/appserve/api', {
                target: 'http://127.0.0.1:8080',
                changeOrigin: false
              }),
              proxyMiddleware('/ws', {
                target: 'http://127.0.0.1:3000',
                changeOrigin: false,
                ws: true
              })
            ]
          }
        }
      }

    }
[...]

WebSocket connection to 'ws://localhost:9000/ws/?EIO=3&transport=websocket&sid=H75IeRaFmZqKFgqwAA' failed: Connection closed before receiving a handshake response

@micheledisalvatore
Copy link

@xak2000 @jmls have you found a solution?

@xak2000
Copy link

xak2000 commented Sep 16, 2015

No. But I am using SockJS and when it cannot connet through websocket it emulates it through backup transports (xhr_streaming in my case). Not too convinient in dev mode as I don't see data frames in Chrome's Network tab. But until this bug will be fixed I have no alternatives.

@shakyShane, any progress of this issue? Do my example of this bug suit your needs?

@micheledisalvatore
Copy link

I've seen that using the soket.io it works, also with the error above. So for the development environment is not a problem. In production, I use nginx.

@xak2000
Copy link

xak2000 commented Sep 17, 2015

socket.io also uses backup transports so if it works for you, it uses them instead of native websocket. But native websockets still doesn't work.

@micheledisalvatore
Copy link

yes, I know...

@redi-madhava
Copy link

This issue has been thorny for me too: chimurai/http-proxy-middleware#37
As you see @chimurai already linked the issue.

Due to this issue, I'm now thinking of using something else other than brower-sync.

@aaronchar
Copy link

I had to move on from browsersync at work because of this issue. Would be nice to see a resolution.

@redi-madhava
Copy link

I tried starting up two browersync instances: one for WS only and the other for file serving, reloading etc, but that ended up in port conflicts. Next option would be to start a node server only to forward WS traffic, but I'm researching on how to do this.
Websockets are mainstream, and a simpler & default support for WS forwarding/proxying is very desired. No idea why browersync folks are not prioritizing this.

@shakyShane
Copy link
Contributor

@redi-madhava the problem is that Browsersync is intercepting the upgrade server event.

so this works

var WebSocketServer = require('ws').Server;
var wss = new WebSocketServer({ port: 8000 });

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

/**
 * Setup proxy
 * @type {*|exports|module.exports}
 */
var httpProxy = require('http-proxy');
var http = require('http');
var proxy = new httpProxy.createProxyServer({
    target: {
        host: 'localhost',
        port: 8000
    }
});

/**
 * Setup static file server + proxy websockets
 */
var connect = require('connect')();
connect.use(require('serve-static')('app'));
var http = require('http');
var server = http.createServer(connect);
server.on('upgrade', function (req, socket, head) {
    proxy.ws(req, socket, head);
});
server.listen(3000);

But swap the last chunk for

var browserSync = require('browser-sync');
browserSync({
    server: "app",
    files: ["app/*.html", "app/css/*.css"]
}, function (err, bs) {
    bs.server.on('upgrade', function (req, socket, head) {
        proxy.ws(req, socket, head);
    });
});

and it all just breaks!

@shakyShane
Copy link
Contributor

using the example above, 1 way would be to simply call the correct websocket url and not expect Browsersync to proxy it

// browser
var websocket = new WebSocket("ws://0.0.0.0:8000"); // don't use the same port as Browsersync

var int = setInterval(x => {
    if (websocket.readyState === 1) {
        clearInterval(int);
        wsReady();
    }
}, 1000);

function wsReady () {
    websocket.send(JSON.stringify({
        id: "client1"
    }));
}

@redi-madhava
Copy link

@shakyShane Thanks! I removed browser-sync and used above code to make it work!

@jmls
Copy link
Author

jmls commented Nov 17, 2015

@redi-madhava @shakyShane : sorry, I must be dumb - what do I need to change where to make this work ? thanks!

@redi-madhava
Copy link

We use gulp, so my gulp task file for serving up html5 app looks like below based on the tips from @shakyShane above.

When "gulp serve" is run, it starts a node server listening on port 3000 and forwards websocket traffic to a websocket server running on localhost at 9090. It grabs 'upgrade' event in websocket handshake to use proxy created for localhost:9090.

conf below just contains some constants, for eg conf.paths.tmp=/.tmp, conf.paths.src=src and bower_components folder is at the same level as src folder.

Hope that helps!

'use strict';

var gulp = require('gulp'),
  conf = require('./conf'),
  gutil = require('gulp-util'),
  http = require('http'),
  httpProxy = require('http-proxy');

gulp.task('serve', ['watch'], function () {
  httpProxyInit(conf.paths.tmp + '/serve');
});

var proxy = httpProxy.createProxyServer({
  target: {
    host: 'localhost',
    port: 9090
  }
});

proxy.on('error', function(e) {
  gutil.log('error on proxying websocket');
  gutil.log(e);
})

var httpProxyInit = function (baseDir) {

  var connect = require('connect')();
  connect.use(require('serve-static')(baseDir));
  connect.use(require('serve-static')(conf.paths.src));
  connect.use('/bower_components', require('serve-static')(conf.paths.src + '/../bower_components'));
  var server = http.createServer(connect);

  server.on('upgrade', function (req, socket, head) {
    proxy.ws(req, socket, head);
  });

  server.listen(3000);
};

@xak2000
Copy link

xak2000 commented Nov 17, 2015

I doesn't understand where is browser-sync in this solution? The main problem is that browser-sync doesn't work with websocket proxy. Browser-sync is needed to auto-refresh page in browser after js-files was changed. Without browser-sync we could just use connect + http-proxy-middleware and all working fine except auto-refresh of browser.

To make it cleaner:
In my application I have:

  1. a client - bunch of static html files + js-files.
  2. a server - java application that running on some port (just one port for both http and websocket requests). So the server url is http://localhost:SERVER_PORT

In production client files just hosted by apache http server.
Also in apache config there are proxy_pass from /api/** url to http://localhost:SERVER_PORT/** url.
This proxy_pass working fine for http requests and for ws requests.
So js-client can do http and ws requests to port 80 and transparently proxy_passed to java server.

On my development machine I made the same configuration but with browser-sync instead of apache to auto refresh browser page in case of some html/js files changed. Instead of apache's proxy_pass I used http-proxy-middleware and all working fine except proxying of websocket.

@Toub
Copy link

Toub commented Jan 22, 2016

As BrowserSync does not support WebSockets, a simple approach (apart of removing BS) is to serve WS server directly in dev mode.

e.g. with socket.io:

var ioSocket = io('http://localhost:9000', {
    path: '/socket.io'
});

I hope this to be fixed soon.

@bryantp
Copy link

bryantp commented Nov 21, 2016

Hi,

I am also having an issue with websockets in server mode. Here is my gulp config:

var proxyMiddleware = require('http-proxy-middleware');

module.exports = function () {
  return {
    injectChanges: true,
    port: 3002,
    server: {
      baseDir: [
        '.tmp',
        'src
      ],
      routes: {
        '/bower_components': 'bower_components'
      },
	  middleware: [
        proxyMiddleware('/api/**', {target: 'http://localhost:8081', changeOrigin: true}),
        proxyMiddleware('/ws/**', {target: 'http://localhost:8081', changeOrigin: false, ws: true, logLevel: 'debug'}),
      ]
    },
    open: false
  };
};

It usually terminates with this error:

Connection closed before receiving a handshake response

@apinder
Copy link

apinder commented Dec 4, 2016

It's a shame to leave browser-sync behind but this one's a blocker for me, the solution to provide websockets on a separate port causes issues for me as it breaks the same origin policy on cookies and prevents authentication over ws.

@Boscop
Copy link

Boscop commented Mar 18, 2018

Any update on this?

@ibc
Copy link

ibc commented Mar 18, 2018

Use http-proxy-middleware as WebSocket proxy. It works with browser-sync.

@Boscop
Copy link

Boscop commented Mar 18, 2018

But this issue suggests it doesn't work with websockets?
chimurai/http-proxy-middleware#15
How did you make it work?

@ibc
Copy link

ibc commented Mar 18, 2018

const browserSync = require('browser-sync');
const proxy = require('http-proxy-middleware');

browserSync(
  {
    open      : 'external',
    host      : MY_DOMAIN,
    server    :
    {
      baseDir : MY_OUTPUT_DIR,
      middleware :
      [
        // Proxy WebSocket requests (identified by their /websocket pathname)
        proxy(
          '/websocket',
          {
            target : MY_WEBSOCKET_SERVER_URL,
            ws     : true
          }
        )
      ]
    }
  });

@Boscop
Copy link

Boscop commented Mar 19, 2018

Thanks a lot!

Can browser sync also work if the json api and websockets are on the same port of my server, that I want to proxy to?

How would it look like then? :)

I have '/api' for api requests and '/ws' for websockets.

@daniel-shuy
Copy link

@ibc your example does not work! Notice that your example is effectively the same as the OP's.

@daniel-shuy
Copy link

Still does not work with browser-sync 2.26.x

@ibc
Copy link

ibc commented Dec 27, 2018

@ibc your example does not work! Notice that your example is effectively the same as the OP's.

Yes, it does work.

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