How to use socket.io only on specific page in my website?

Problem

Let's suppose I have a website that DOESN'T require web-sockets on the home page, but DOES require it on some other relative path ('/new' for example).

Suppose I need the 'connection' event in order to count users that login to the 'home' page and to the 'new' page.

I've tried to configure socket.io 'connection' event in the relative path like:

app.get('/new',
         function(req,res) {
              io.sockets.on('connection', function (socket) {
                .....
              }
         });

BUT:

  1. It doesn't perform well. When a lot of users connected, that event gets raised when it shouldn't.
  2. I didn't see any example on the web like the one above. All of the socket.io events are configured in the main file (app.js) once.

How can it be done ?

Problem courtesy of: ohadinho

Solution

Answering your questions:

  1. Obviously, because when a user hits /new its handler fires and adds connection handler to io. It's a huge memory leak.

  2. That's related to the one above. You don't want to add a connection handler every time a user hits /new.

Now to solve your problem I think that the best idea is to call (on the browser side)

io.connect("http://localhost/new");

and then in your Node.js app:

io.of("/new").on("connection", function (socket) {
    // here are connections from /new
});

The code should be placed at the top level, not inside a route handler. If you want it to be more dynamic, then you can create your own sub-protocol:

  1. Connect to the io server: io.connect("http://localhost);
  2. Send a message I'm in /new;
  3. On the server get that message and store that connection in an appropriate list.

Of course you can specify in your route handler whether you want your client to connect to socket.io server or not (simply by passing some kind of flag).

Solution courtesy of: freakish

Discussion

If you don't choose to selectively put the socket.io client per page, then you can use the authorization event and check which page they connected from:

io.configure(function () {
  io.set('authorization', function (handshakeData, callback) {
    if (handshakeData.url == '/new') {
      //allow the socket to connect
      callback(null, true);
    } else {
      //prevent the socket handshake with an error
      callback('socket.io is not accepting connections from this page', false);
    }
  });
});

You shouldn't be using the io.sockets.on object more than once, because in your code, you're adding listeners to the connection event every time someone loads that page. Besides, when a handshake is turned down, the connection event is never fired, so you could then use a setup like so:

io.sockets.on('connection', function(socket) {
  //this shouldn't ever happen, it's the same object that was checked before
  if (socket.handshake.url != '/new') {
    socket.disconnect();
    return;
  }

  //do something on page '/new'
}

app.get('/new', function(req, res) {
  //sockets can successfully connect with this page
});

app.get('/foo', function(req, res) {
  //sockets from this page will fail handshakes and be disconnected
});

If you need to count the amount of users on a page, you listen to both the connection and disconnection events of a socket. This is one way it could be done:

var pages = [];
io.sockets.on('connection', function(socket) {
  if (pages[socket.handshake.url] == null) {
    pages[socket.handshake.url] = 0;
  }
  pages[socket.handshake.url]++;

  socket.on('disconnect', function() {
    pages[socket.handshake.url]--;
  });
}

If people opened multiple tabs, then you could store connected session cookies and look for matches, also using the socket.handshake object.

Discussion courtesy of: hexacyanide

Socket.io connection is not initialized in your handler method like that.
It will be initialized after the page is rendered, the browser load the client-side script and io.connect is called (in your client-side script).

Calling io.sockets.on('connection') anywhere in your server-side script just register another listener (server listener) to socket connected event.
Placing it in the request handler means anytime your send a GET request to '/new', another listener is registered. (You can place some console.log there, hit refresh a few time and see when a new socket connects)

If you don't want socket.io connection on some page, you can do it like this

On your serverside script

app.all("*", function(req, res, next) {
    res.locals({
        needSocketIo: true
    });
}

app.get('/some-page-you-do-not-want-socket-io', function(req, res) {
    res.render('some-view', {needSocketIo: false});
})

On your view/layout, it's just an if/else clause to or to not include the javascript file with your socket.io logic.

And if you want your server to know from where the socket connects, you can emit some message from the client

socket.on('connect', function () {
    socket.emit('from', {page: 'your-page'});
});
Discussion courtesy of: hope_is_grim

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