docs: function (server, version, socket, options) {
    var self = this;
    self.id = Random.id();
  
    self.server = server;
    self.version = version;
  
    self.initialized = false;
    self.socket = socket;
  
    // set to null when the session is destroyed. multiple places below
    // use this to determine if the session is alive or not.
    self.inQueue = [];
  
    self.blocked = false;
    self.workerRunning = false;
  
    // Sub objects for active subscriptions
    self._namedSubs = {};
    self._universalSubs = [];
  
    self.userId = null;
  
    self.collectionViews = {};
  
    // Set this to false to not send messages when collectionViews are
    // modified. This is done when rerunning subs in _setUserId and those messages
    // are calculated via a diff instead.
    self._isSending = true;
  
    // If this is true, don't start a newly-created universal publisher on this
    // session. The session will take care of starting it when appropriate.
    self._dontStartNewUniversalSubs = false;
  
    // when we are rerunning subscriptions, any ready messages
    // we want to buffer up for when we are done rerunning subscriptions
    self._pendingReady = [];
  
    // List of callbacks to call when this connection is closed.
    self._closeCallbacks = [];
  
  
    // XXX HACK: If a sockjs connection, save off the URL. This is
    // temporary and will go away in the near future.
    self._socketUrl = socket.url;
  
    // Allow tests to disable responding to pings.
    self._respondToPings = options.respondToPings;
  
    // This object is the public interface to the session. In the public
    // API, it is called the `connection` object.  Internally we call it
    // a `connectionHandle` to avoid ambiguity.
    self.connectionHandle = {
      id: self.id,
      close: function () {
        self.close();
      },
      onClose: function (fn) {
        var cb = Meteor.bindEnvironment(fn, "connection onClose callback");
        if (self.inQueue) {
          self._closeCallbacks.push(cb);
        } else {
          // if we're already closed, call the callback.
          Meteor.defer(cb);
        }
      },
      clientAddress: self._clientAddress(),
      httpHeaders: self.socket.headers
    };
  
    socket.send(stringifyDDP({msg: 'connected',
                              session: self.id}));
    // On initial connect, spin up all the universal publishers.
    Fiber(function () {
      self.startUniversalSubs();
    }).run();
  
    if (version !== 'pre1' && options.heartbeatInterval !== 0) {
      self.heartbeat = new Heartbeat({
        heartbeatInterval: options.heartbeatInterval,
        heartbeatTimeout: options.heartbeatTimeout,
        onTimeout: function () {
          self.close();
        },
        sendPing: function () {
          self.send({msg: 'ping'});
        }
      });
      self.heartbeat.start();
    }
  
    Package.facts && Package.facts.Facts.incrementServerFact(
      "livedata", "sessions", 1);
  }