docs: function AccessControl(options)
  {
      EventEmitter.call(this);
  
      var ths = this;
  
      this._pre_subscribe_requested = function (topic, done)
      {
          if (ths._max_topic_length && (topic.length > ths._max_topic_length))
          {
              done(new Error('subscribe topic longer than ' +
                             ths._max_topic_length));
              return ths.emit('subscribe_blocked', topic, this);
          }
  
          if (!allow(ths._matchers.subscribe, topic))
          {
              done(new Error('blocked subscribe to topic: ' + topic));
              return ths.emit('subscribe_blocked', topic, this);
          }
  
          if (ths._max_subscriptions &&
              (this.subs.size >= ths._max_subscriptions) &&
              !this.subs.has(topic))
          {
              done(new Error('subscription limit ' + ths._max_subscriptions +
                             ' already reached: ' + topic));
              return ths.emit('subscribe_blocked', topic, this);
          }
  
          if (!ths.emit('subscribe_requested', this, topic, done) &&
              !this.emit('subscribe_requested', topic, done))
          {
              this.default_subscribe_requested_handler(topic, done);
          }
      };
  
      this._pre_unsubscribe_requested = function (topic, done)
      {
          if (ths._max_topic_length && (topic.length > ths._max_topic_length))
          {
              done(new Error('unsubscribe topic longer than ' +
                             ths._max_topic_length));
              return ths.emit('unsubscribe_blocked', topic, this);
          }
          
          if (!ths.emit('unsubscribe_requested', this, topic, done) &&
              !this.emit('unsubscribe_requested', topic, done))
          {
              this.default_unsubscribe_requested_handler(topic, done);
          }
      };
  
      this._pre_publish_requested = function (topic, duplex, options, done)
      {
          if (ths._max_topic_length && (topic.length > ths._max_topic_length))
          {
              done(new Error('publish topic longer than ' +
                             ths._max_topic_length));
              return ths.emit('publish_blocked', topic, this);
          }
  
          if (!allow(ths._matchers.publish, topic))
          {
              done(new Error('blocked publish to topic: ' + topic));
              return ths.emit('publish_blocked', topic, this);
          }
  
          if (ths._disallow_publish_single && options.single)
          {
              done(new Error('blocked publish (single) to topic: ' + topic));
              return ths.emit('publish_blocked', topic, this);
          }
  
          if (ths._disallow_publish_multi && !options.single)
          {
              done(new Error('blocked publish (multi) to topic: ' + topic));
              return ths.emit('publish_blocked', topic, this);
          }
  
          var server = this;
  
          if (ths._max_publications &&
              (server.mqlobber_access_control_publish_count >=
               ths._max_publications))
          {
              done(new Error('publication limit ' + ths._max_publications +
                             ' already reached: ' + topic));
              return ths.emit('publish_blocked', topic, this);
          }
  
          server.mqlobber_access_control_publish_count += 1;
  
          var decrement = function ()
          {
              server.mqlobber_access_control_publish_count -= 1;
              done.apply(this, arguments);
          };
  
          function done2()
          {
              /*jshint validthis: true */
              var dec = decrement;
              decrement = done; // only decrement once
              dec.apply(this, arguments);
          }
  
          if (ths._max_publish_data_length)
          {
              var t = new Transform(),
                  count = 0;
  
              t.on('error', this.relay_error);
              t.on('error', done2);
              
              t._transform = function (chunk, enc, cont)
              {
                  count += chunk.length;
  
                  if (count > ths._max_publish_data_length)
                  {
                      return cont(new Error('message data exceeded limit ' +
                                            ths._max_publish_data_length + ': ' +
                                            topic));
                  }
  
                  this.push(chunk);
                  cont();
              };
  
              duplex.pipe(t);
              duplex = t;
          }
  
          if (!ths.emit('publish_requested', this, topic, duplex, options, done2) &&
              !this.emit('publish_requested', topic, duplex, options, done2))
          {
              this.default_publish_requested_handler(topic, duplex, options, done2);
          }
      };
  
      this._blocked_topics = [];
      this._blocked_handlers = new Set();
  
      this.reset(options);
  }