Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to create cross-domain websocket connection to Node.js socket.io server

According to my searches this shouldn't be that hard, and I have tried many things with no luck so far. I have a Node.js server running on Heroku that creates a socket.io server (I also have tried hosting it on Nodejitsu, which didn't help). The socket.io socket connection works with no problems if I serve the page from the actual Node.js server that I want to create a websocket connection to, but I can't make any of my cross-domain socket connection attempts work.

I have tried to do a cross-domain connect to this socket.io server several ways:

(1) A simple local HTML file with some script that fetches client side socket.io library successfully from node server (using $.getScript), and then tries to create a socket connection (var socket = io.connect('nodejs_server_hostname:port_num');), which fails.

(2) Running rails app server locally (development mode) that serves up a response to clients containing javascript that tries to form a websocket connection in the same fashion as method (1), and fails

(3) Hosting rails app in Heroku, and doing the same thing as method (2). This fails as well. I made sure to enable websockets as specified in the Heroku documentation (https://devcenter.heroku.com/articles/heroku-labs-websockets). Once again, everything works if the request is served from the actual Node.js server that the client attempts to form a socket.io connection with.

The socket.socket.on('error') event is trigged in all three attempts.

I have tried modifying the http response headers in the rails app using the following code:

response.headers['Access-Control-Allow-Origin'] = '*'

This sets the headers correctly (I checked after requesting page from the rails server), but it doesn't fix the socket connection problem.

I created an socket-io authorization callback on the Node.js server that performs logging, and it seems this callback is never even hit at all for the cross-domain socket connection attempts. So it seems like the Node.js server never even sees the websocket connection attempt, which is quite confusing to me.

Below I will paste all the relevant code files.

Node.js server file:

var http = require('http'),
    express = require('express'),
    whiskers = require('whiskers'),
    redis = require('redis'),
    port = process.env.PORT || 5000,
    app = express(),
    server = http.createServer(app).listen(port),
    io = require('socket.io').listen(server);

console.log('---- ' + port);
console.log(port);
console.log('---- ' + port);

io.configure(function () {    
    io.set('authorization', function (handshakeData, callback) {
        console.log("-- inside io.set('authorization, cb)")
        console.log('-- handshakeData:');
        console.log(handshakeData);
        console.log('-- end of handshakeData logging --');
        callback(null, true); // error first callback style 
    });
});

app.configure(function() {
    app.use(express.static(__dirname + '/public'));

    app.set('views', __dirname + '/views');
    app.set('view options', { layout: false });

    app.engine('.html', whiskers.__express);
});


app.get('/', function(req, res) {
    res.render('index.html');
});

io.sockets.on('connection', function(socket) {
    console.log('---- new client connected ----');
    console.log(socket);
    console.log('---- end of socket details logging for new client connection ----');

    socket.emit('news', { hello: 'world' });

    socket.on('my other event', function(data) {
        console.log("-- server -- inside socket.on 'my other event' callback");
        console.log(data);
    });

    socket.on('button clicked', function(data) {
        console.log("-- server -- inside socket.on 'button clicked' callback");
        console.log(data);

        io.sockets.emit('news', { info: 'the button was clicked' });
    });
});

Here is the client-side javascript file from Rails app:

$(document).ready(function() {
    console.log('-- inside chat.js -- document is ready');

    $('#join-chat-container').hide();
    $('#new-message-container').hide();

    //$.getScript('http://node-server-for-rails-chat.herokuapp.com/socket.io/socket.io.js')
    $.getScript('http://node-server-for-rails-chat.jit.su/socket.io/socket.io.js')
        .done(function( script, textStatus ) {
            console.log("-- socket.io.js loaded and executed -- textStatus: " + textStatus);
            setup_socket();
        })
        .fail(function( jqxhr, settings, exception ) {
            console.log("-- socket.io.js load error -- exception:");
            console.log(exception);
            console.log("-- jqxhr: " + jqxhr);
        });
});

function setup_socket() {
    //var socket = io.connect('node-server-for-rails-chat.herokuapp.com', { port: 19364 });
    //var socket = io.connect('node-server-for-rails-chat.jit.su', { port: 5000 });
    var socket = io.connect('node-server-for-rails-chat.herokuapp.com:27305');

    socket.socket.on('error', function (reason) {
        console.log('--- cant connect');
        console.log(reason);
        console.error('Unable to connect Socket.IO', reason);
    });

    socket.on('connect_failed', function() {
        console.log('connect failed');
    });

    socket.on('connect', function() {
        console.log('successfully connected!');
    });


    socket.on('message', function (message_html) {
        console.log("-- client -- inside socket.on('message', cb) callback");
        console.log("-- client -- message_html: " + message_html);

        add_message(message_html);
    });

    if (!socket.socket.connected) {
        console.log(socket);
        console.log("-- Error connecting with socket");
    }
    socket_loaded(socket);
};

function socket_loaded(socket) {
    // this part contains irrelevant (not socket related) code
}

//more irrelevant code excluded for brevity (add_message function, plus other stuff)

This is the stand-alone HTML file I opened locally in browser:

<!DOCTYPE html>
<html>
<head>
    <script type="text/javascript" src="http://code.jquery.com/jquery-git.js"></script>
    <script>
        $(document).ready(function() {
            if (typeof jQuery !==  'undefined') {
                console.log('-- client -- jQuery library loaded successfully');

                $('#myButton').on('click', function() {
                    console.log("-- client -- inside jquery '#myButton' click callback");
                    socket.emit('button clicked', { button: '#myButton' });
                });
            }

            $.getScript('http://node-server-for-rails-chat.herokuapp.com/socket.io/socket.io.js')
                .done(function( script, textStatus ) {
                    console.log("-- socket.io.js loaded and executed -- textStatus: " + textStatus);
                    socket_is_loaded();
                })
                .fail(function( jqxhr, settings, exception ) {
                    console.log("-- socket.io.js load error -- exception:");
                    console.log(exception);
                    console.log("-- jqxhr: " + jqxhr);
                });
        });

        function socket_is_loaded() {
            //var socket = io.connect('node-server-for-rails-chat.jit.su:5000');
            var socket = io.connect('node-server-for-rails-chat.herokuapp.com:27305');

            socket.socket.on('error', function (reason) {
                console.log('--- cant connect');
                console.log(reason);
                console.error('Unable to connect Socket.IO', reason);
            });

            socket.on('connect_failed', function() {
                console.log('connect failed');
            });

            socket.on('connect', function() {
                console.log('successfully connected!');
            });

            socket.on('news', function(data) {
                console.log("-- client -- inside socket.on 'news' callback");
                console.log(data);
                //socket.emit('my other event', { my: 'data' });
            });
        }

    </script>
</head>

<body>
    <h1>Socket.io How-to-use Example Script</h1>
    <input type='button' id='myButton' value='click me!'/>
</body>
</html>

Does anyone have ideas on how I can solve this problem? Thanks a bunch.

like image 397
Daniel Waltrip Avatar asked Oct 20 '22 20:10

Daniel Waltrip


1 Answers

I have a similar setup running and had to configure the following headers on the node express web server.

app = require('express')();
...
app.use(function(req, res, next) {
  res.header('Access-Control-Allow-Origin', '*');
  res.header('Access-Control-Allow-Methods', 'GET, OPTIONS');
  res.header('Access-Control-Allow-Headers', 'Content-Type');
  return next();
});
like image 140
Trevor Johnston Avatar answered Oct 23 '22 23:10

Trevor Johnston