eleanor
eleanor

Reputation: 1534

Socket.IO Maximum call stack size exceeded

I've written a small Socket.IO server, which works fine, I can connect to it, I can send/receive messages, so everything is working ok. Just the relevant part of the code is presented here:

var RedisStore = require('socket.io/lib/stores/redis');
const pub = redis.createClient('127.0.0.1', 6379);
const sub = redis.createClient('127.0.0.1', 6379);
const store = redis.createClient('127.0.0.1', 6379); 

io.configure(function() {
  io.set('store', new RedisStore({
    redisPub    : pub, 
    redisSub    : sub, 
    redisClient : store
  }));
});

io.sockets.on('connection', function(socket) {
 socket.on('message', function(msg) {
    pub.publish("lobby", msg);
  });


  /*
   * Subscribe to the lobby and receive messages.
   */
  var sub = redis.createClient('127.0.0.1', 6379);
  sub.subscribe("lobby");
  sub.on('message', function(channel, msg) {
    socket.send(msg);
  });
});

I've also written a script presented below that connects to the server and spawns connections in the setInterval function, which spawns a new connection each 10milisecons, so it's spawning quite a lot of connections.

#!/usr/bin/env node

var io = require('socket.io-client');
var reconn = {'force new connection': true};
var sockets = [];

var num = 1000;

function startSocket(i) {
  sockets[i] = io.connect("http://127.0.0.1:8080", reconn);
  sockets[i].on('connect', function() {
    console.log("Socket["+i+"] connected.");
  });
  sockets[i].on('message', function(msg) {
    console.log("Socket["+i+"] Message received: "+msg);
  });
}



/*
 * Start number of sockets.
 */
for(var i=0; i<num; i++) {
  startSocket(i);
}



/*
 * Send messages forever.
 */
setInterval(function() {
    for(var i=0; i<num; i++) {
      sockets[i].send("Hello from socket "+i+".");
    }
}, 10);

This script is a benchmark tool spawning 1000 connections to the server, but when running for several minutes, the server dies with the following error message:

node.js:0 // Copyright Joyent, Inc. and other Node contributors. ^ RangeError: Maximum call stack size exceeded

I know that there's not enough stack space available so the exception occurs and the process is terminated, but even if I enlarge the stack with the --stack-size variable, this doesn't actually solve the problem, because I can always spawn more connections, which will eventually kill the server.

My question is: how can I prevent this. This is an effective DoS scenario, where anybody can hack together this little script and force the node server to terminate, but I would like to prevent this from happening. I would like Node server to never terminate, just process messages slowly.

Any ideas if this can be prevented. I'm not sure that I would like to block IPs, since I would also like mobile phones to login to the system, where many of them use the same IP, so the node server can mistakenly think a DoS is being in place by one mobile network operation and blocks its IP.

Thank you

Upvotes: 2

Views: 2121

Answers (1)

whadar
whadar

Reputation: 4511

If you would like your node server to run forever, no matter what, use https://github.com/nodejitsu/forever

As for the Exception - My hunch is that var sub = redis.createClient('127.0.0.1', 6379); may allocate a variable in the stack each time a connection is established.

I would first try to put var subs = [] in the global scope and subs[socket.id] = redis.createClient('127.0.0.1', 6379);

Or something like socket.sub = redis.createClient('127.0.0.1', 6379); to piggyback the existing, hopefully heap based, socket.io data structures.

If not working, try to isolate the problem by removing the use of Redis...

Upvotes: 0

Related Questions