WebSocket connection failed: Error during WebSocket handshake: Unexpected response code: 400

Problem

I am trying to integrate Socket.io with Angular and I'm having difficulties making a connection from the client-side to the server. I've looked through other related questions but my issue is happening locally, so there's no web server in the middle.

This is what my server code looks like:

const app = express();
const server = http.createServer(app);
const io = require('socket.io').listen(server);

io.on('connection', function(socket) {
  socket.emit('greet', { hello: 'Hey, Mr.Client!' });

  socket.on('respond', function(data) {
    console.log(data);
  });
  socket.on('disconnect', function() {
    console.log('Socket disconnected');
  });
});

I'm loading the client side JavaScript files using Grunt in the following order:

dist: {
    src: [
        public/bower_components/angular/angular.min.js,
        ...
        public/bower_components/socket.io-client/dist/socket.io.min.js,
        public/bower_components/angular-socket-io/socket.min.js,
        ...
    ]
}

Then in my controller:

function MyController($scope) {

    let socket = io.connect(window.location.href);
    socket.connect('http://localhost:3000');
    socket.on('greet', function(data) {
      console.log(data);
      socket.emit('respond', { message: 'Hello to you too, Mr.Server!' });
    });

    ...
}

Before actually using the btford/angular-socket-io library, I want to make sure that I can get a connection correctly, but I get the following error in the console:

socket io connection error message

The interesting thing is that if I restart the Node.js server process, it does manage to send the message but using polling instead of websockets.

after restart

polling message

I tried all sorts of different options in the socket.connect call, but nothing worked.

Any help would be appreciated.


UPDATE (30/12/2016):

I just realized that websockets is working partially. I see a 101 Switching Protocols request in the Chrome developer console. However the only frames I see there are the engine.io protocol packets (ping, pong). However my application socket messages still fall back to polling for some reason...

engine.io packets

Problem courtesy of: ashe540

Solution

Problem solved! I just figured out how to solve the issue, but I would still like to know if this is normal behavior or not.

It seems that even though the Websocket connection establishes correctly (indicated by the 101 Switching Protocols request), it still defaults to long-polling. The fix was as simple as adding this option to the Socket.io connection function:

{transports: ['websocket']}

So the code finally looks like this:

const app = express();
const server = http.createServer(app);
var io = require('socket.io')(server);

io.on('connection', function(socket) {
  console.log('connected socket!');

  socket.on('greet', function(data) {
    console.log(data);
    socket.emit('respond', { hello: 'Hey, Mr.Client!' });
  });
  socket.on('disconnect', function() {
    console.log('Socket disconnected');
  });
});

and on the client:

var socket = io('ws://localhost:3000', {transports: ['websocket']});
socket.on('connect', function () {
  console.log('connected!');
  socket.emit('greet', { message: 'Hello Mr.Server!' });
});

socket.on('respond', function (data) {
  console.log(data);
});

And the messages now appear as frames:

working websockets

This Github issue pointed me in the right direction. Thanks to everyone who helped out!

Solution courtesy of: ashe540

Discussion

Judging from the messages you send via Socket.IO socket.emit('greet', { hello: 'Hey, Mr.Client!' });, it seems that you are using the hackathon-starter boilerplate. If so, the issue might be that express-status-monitor module is creating its own socket.io instance, as per: https://github.com/RafalWilinski/express-status-monitor#using-module-with-socketio-in-project

You can either:

  1. Remove that module
  2. Pass in your socket.io instance and port as websocket when you create the expressStatusMonitor instance like below:

    const server = require('http').Server(app);
    const io = require('socket.io')(server);
    ...
    app.use(expressStatusMonitor({ websocket: io, port: app.get('port') })); 
    
Discussion courtesy of: Kuriakin Zeng

I think you should define your origins for client side as bellow:

//server.js
const socket = require('socket.io');
const app = require('express')();
const server = app.listen('port');

const io = socket().attach(server);
io.origins("your_domain:port www.your_domain:port your_IP:port your_domain:*")

io.on('connection', (socket) => {
  console.log('connected a new client');
});

//client.js
var socket = io('ws://:port');

Discussion courtesy of: Saeid Alidadi

In your controller, you are using an http scheme, but I think you should be using a ws scheme, as you are using websockets. Try to use ws://localhost:3000 in your connect function.

Discussion courtesy of: TeaMonkie

This recipe can be found in it's original form on Stack Over Flow.