docs: function Gateway(session, login, opts) {
    EventEmitter.call(this);
  
    var _this = this;
  
    login = login || basicLogin;
    session = session || basicSession;
  
    _this.socks = {};
    _this.heartbeatFreq = opts.heartbeatFreq;
    _this.clientTimeout = opts.clientTimeout;
  
    var sockOptions = {
      log: function (level, message) {
        sockjsLogger[level](message);
      }
    };
    _this.sock = sockjs.createServer(sockOptions);
  
    _this.sock.on('connection', function (sock) {
      logger.debug('connection from ' + sock.remoteAddress + ' (using ' + sock.protocol + ')');
  
      _this.socks[sock.id] = sock;
      sock.loginTimeout = setTimeout(function () {
        logger.debug('authentication delay timeout !');
        logout(sock);
      }, loginTimeout);
      sock.hb = Date.now();
  
      sock.on('data', function (data) {
        if (data === 'hb') {
          sock.hb = Date.now();
          return;
        }
  
        var msg = decode(data);
        if (msg && msg.type) {
          switch (msg.type) {
            case 'req':
              sock.identity ? processReq(sock, msg) : enqueueReq(sock, msg);
              break;
            case 'res':
              sock.identity && processRes(sock, msg);
              break;
            case 'login':
              processLogin(sock, msg.authData);
              break;
            case 'negotiate':
              sock.write(encode({type: 'negotiate', heartbeatFreq:_this.heartbeatFreq}));
              break;
            default:
              logger.warn('received unknown message type', msg);
          }
        } else {
          logger.warn('received malformat data', msg);
        }
      });
  
      sock.on('close', function () {
        if (sock.id) delete _this.socks[sock.id];
        logout(sock);
      });
    });
  
    function processLogin(sock, data) {
      if (sock.identity) logout(sock);
      login(sock, data, function (err, identity) {
        if (!err) {
          sock.identity = identity + '/' + hubiquitus.utils.uuid();
          clearTimeout(sock.loginTimeout);
          logger.debug('login success from ' + sock.remoteAddress + '; identifier : ' + sock.identity);
          var feedBack = {type: 'login', content: {id: sock.identity}};
          sock.write(encode(feedBack));
          _this.emit('connected', sock.identity);
          processQueue(sock);
          hubiquitus.addActor(sock.identity, session, {sock: sock});
        } else {
          logger.debug('login error', err);
          logout(sock);
        }
      });
    }
  
    function processReq(sock, req) {
      if (tv4.validate(req, schemas.message)) {
        logger.trace('processing request', req);
        var cb = null;
        if (req.cb) {
          cb = function (err, res) {
            if (err && err.code === 'TIMEOUT') return; // timeout is delegated to client side
            res.id = req.id;
            res.type = 'res';
            sock.write(encode(res));
          };
        }
        hubiquitus.send(sock.identity, req.to, req.content, cb);
      } else {
        logger.warn('received malformat request', {err: tv4.error, req: req});
      }
    }
  
    function processRes(sock, res) {
      logger.debug('processing response', res);
      if (tv4.validate(res, schemas.message)) {
        events.emit('res|' + sock.identity + '|' + res.id, res);
      } else {
        logger.warn('received malformat response', {err: tv4.error, res: res});
      }
    }
  
    function enqueueReq(sock, req) {
      sock.queue = sock.queue || [];
      sock.queue.push(req);
    }
  
    function processQueue(sock) {
      if (!sock.queue) return;
      logger.debug('processing ' + sock.identity + ' queue (' + sock.queue.length + ' elements)');
      sock.queue && _.forEach(sock.queue, function (req) {
        processReq(sock, req);
      });
    }
  
    function logout(sock) {
      if (sock.identity) {
        _this.emit('disconnected', sock.identity);
        hubiquitus.removeActor(sock.identity);
        delete sock.identity;
      }
      sock.close();
    }
  
    /**
     * Send a heartbeat to all opened sockets
     */
    function sendHeartbeat() {
      _.forOwn(_this.socks, function (sock) {
        sock.write('hb');
      });
      setTimeout(sendHeartbeat, _this.heartbeatFreq);
    }
    sendHeartbeat();
  
    /**
     * Check that all clients respond to the heartbeat in time
     */
    function checkClientsHeartbeat() {
      var now = Date.now();
      _.forOwn(_this.socks, function (sock) {
        if (sock.hb + _this.clientTimeout < now) {
          logout(sock);
        }
      });
      setTimeout(checkClientsHeartbeat, 1000)
    }
    checkClientsHeartbeat();
  }