docs: function(options) {
    if (typeof options !== 'object') {
      options = {};
    }
    this._options = options = _.defaults(options, {
      trustedProxies: ['127.0.0.1'],
      clickjacking: 'deny',
      noMimeSniffing: true,
      log: console.log,
      maxInputSize: 1024 * 1024
    });
  
    this._app = connect();
  
    this._app.use(function(req, res, next) {
      // Set body to stream if posting data not yet processed by the bodyparser.
      if (['GET', 'HEAD'].indexOf(req.method) < 0 && typeof req.headers['content-type'] === 'string') {
        req.body = new Readable();
        req.body.wrap(req);
        // Parse the boundary for multipart data.
        var boundary = req.headers['content-type'].match(/^multipart\/.+?(?:; boundary=(?:(?:"(.+)")|(?:([^\s]+))))$/i);
        req.multipartBoundary = boundary ? boundary[1] || boundary[2] : null;
      }
      next();
    });
    this._app.use(bodyParser.urlencoded({extended: false}));
    this._app.use(bodyParser.json({limit: options.maxInputSize}));
    this._app.use(bodyParser.text({type: 'text/*', limit: this._options.maxInputSize}));
    this._app.use(cookieParser());
    this._app.use(compression());
  
    this.phases = [
      {name: 'initialize', concurrent: true, error: false, errorStatus: 500},
      {name: 'authentication', concurrent: false, error: false, errorStatus: 401},
      {name: 'authorisation', concurrent: false, error: false, errorStatus: 403},
      {name: 'prevalidation', concurrent: true, error: false, errorStatus: 400},
      {name: 'preprocess', concurrent: false, error: false, errorStatus: 500},
      {name: 'postvalidation', concurrent: true, error: false, errorStatus: 400},
      {name: 'process', concurrent: false, error: false, errorStatus: 500},
      {name: 'postprocess', concurrent: false, error: false, errorStatus: 500},
      {name: '_send', concurrent: false, error: false},
      {name: 'after', concurrent: true, error: false},
      {name: 'error', concurrent: false, error: true, errorStatus: 500},
      {name: '_senderror', concurrent: false, error: true},
      {name: 'aftererror', concurrent: true, error: true}
    ];
    this.addRegisterFunctions(this);
  
    this._types = {
      'alpha': '[a-z]+',
      'alphanum': '[a-z0-9]+',
      'bool': '(?:1|0|true|false)',
      'float': '\\-?[0-9\\.]+',
      'int': '[1-9][0-9]*',
      'path': '.+?',
      'signed': '\\-?[0-9]+',
      'string': '[^\\/]+',
      'unsigned': '[0-9]+',
      'uuid': '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'
    };
  
    this._send(sendHandler);
    this.error(errorHandler);
    this._senderror(sendHandler);
  
    var log = this._options.log;
    if (typeof log === 'function') {
      this.after(function() { logHandler.apply(this, [log]); });
      this.aftererror(function() { logHandler.apply(this, [log]); });
    }
  }