text: 'use strict';
  
  var debug = require('diagnostics')('bigpipe:server')
    , Compiler = require('./lib/compiler')
    , fabricate = require('fabricator')
    , Framework = require('bigpipe.js')
    , destroy = require('demolish')
    , Zipline = require('zipline')
    , Temper = require('temper')
    , Supply = require('supply')
    , fuse = require('fusing')
    , async = require('async')
    , path = require('path');
  
  /**
   * Queryable options with merge and fallback functionality.
   *
   * @param {Object} obj
   * @returns {Function}
   * @api private
   */
  function configure(obj) {
    /**
     * Get an option.
     *
     * @param {String} key Name of the opt
     * @param {Mixed} backup Fallback data if key does not exist.
     * @api public
     */
    function get(key, backup) {
      if (key in obj) return obj[key];
      if (backup !== void 0) obj[key] = backup;
  
      return obj[key];
    }
  
    //
    // Allow new options to be be merged in against the original object.
    //
    get.merge = function merge(properties) {
      return BigPipe.predefine.merge(obj, properties);
    };
  
    return get;
  }
  
  /**
   * Our pagelet management.
   *
   * The following options are available:
   *
   * - cache: A object were we store our URL->pagelet mapping.
   * - dist: The pathname for the compiled assets.
   * - pagelets: String or array of pagelets we serve.
   * - parser: Which parser should be used to send data in real-time.
   * - framework: The framework / fitting we want to use.
   *
   * @constructor
   * @param {Server} server HTTP/S based server instance.
   * @param {Object} options Configuration.
   * @api public
   */
  function BigPipe(server, options) {
    if (!this) return new BigPipe(server, options);
    this.fuse();
  
    options = configure(options || {});
  
    this._pagelets = [];                           // Stores our pagelets.
    this._server = server;                         // HTTP server we work with.
    this._options = options;                       // Configure options.
    this._temper = new Temper;                     // Template parser.
    this._plugins = Object.create(null);           // Plugin storage.
    this._cache = options('cache', false);         // Enable URL lookup caching.
    this._statusCodes = Object.create(null);       // Stores error pagelets.
    this._zipline = new Zipline(options);          // Improved gzip compression.
  
    //
    // Setup the asset compiler before pagelets are discovered as they will
    // need to hook in to the compiler to register all assets that are loaded.
    //
    this._compiler = new Compiler(
      options('dist', path.join(process.cwd(), 'dist')), this, {
        pathname: options('static', '/')
    });
  
    //
    // Middleware system, exposed as public so it can
    // easily be called externally.
    //
    this.middleware = new Supply(this);
    this.initialize(options);
  }
  
  //
  // Inherit from EventEmitter3 as we need to emit listen events etc.
  //
  fuse(BigPipe, require('eventemitter3'));
  
  /**
   * Initialize various things of BigPipe.
   *
   * @param {Object} options Optional options.
   * @returns {BigPipe} Fluent interface.
   * @api private
   */
  BigPipe.readable('initialize', function initialize(options) {
    //
    // Add our default middleware layers, this needs to be done before we
    // initialize or add plugins as we want to make sure that OUR middleware is
    // loaded first as it's the most important (at least, in our opinion).
    //
    this.middleware.use('defaults', require('./middleware/defaults'));
    this.middleware.use('zipline', this._zipline.middleware());
    this.middleware.use('compiler', this._compiler.serve);
  
    //
    // Provide a evented metrics API, this way users can hook in their metrics
    // collection modules or easily switch between metrics clients. This gives us
    // as developers a stable API and our users the flexibility that they require.
    //
    this.metrics = options('metrics', {
      increment: this.emits('metrics:increment'),
      decrement: this.emits('metrics:decrement'),
      timing: this.emits('metrics:timing'),
      gauge: this.emits('metrics:gauge'),
      set: this.emits('metrics:set')
    });
  
    //
    // Process the Front-end framework abstractions.
    //
    this.framework(options('framework', Framework));
  
    //
    // Apply the plugins before resolving and transforming the pagelets so the
    // plugins can hook in to our optimization and transformation process.
    //
    return this.pluggable(options('plugins', []));
  });
  
  /**
   * The current version of the library.
   *
   * @type {String}
   * @public
   */
  BigPipe.readable('version', require(__dirname +'/package.json').version);
  
  /**
   * Use a custom framework in BigPipe.
   *
   * @param {Fittings} Framework Framework that we should use.
   * @returns {BigPipe}
   * @api public
   */
  BigPipe.readable('framework', function framework(Framework) {
    if (this._framework) this._framework.destroy();
    this._framework = new Framework(this);
  
    return this;
  });
  
  /**
   * Start listening for incoming requests.
   *
   * @param {Number} port port to listen on
   * @param {Function} done callback
   * @return {BigPipe} fluent interface
   * @api public
   */
  BigPipe.readable('listen', function listen(port, done) {
    var pagelets = this._options('pagelets', path.join(process.cwd(), 'pagelets'))
      , bigpipe = this;
  
    //
    // Make sure we should only start listening on the server once
    // we're actually ready to respond to requests.
    //
    this.define(pagelets, function defined(err) {
      if (err) {
        debug('I failed to listen to the server due to', err.stack);
  
        if (done) return done(err);
        return bigpipe.emit('error', err);
      }
  
      bigpipe._server.on('listening', bigpipe.emits('listening'));
      bigpipe._server.on('request', bigpipe.bind(bigpipe.dispatch));
      bigpipe._server.on('error', bigpipe.emits('error'));
  
      //
      // Start listening on the provided port and return the BigPipe instance.
      //
      debug('Succesfully defined pagelets and assets, starting HTTP server on port %d', port);
      bigpipe._server.listen(port, done);
    });
  
    return bigpipe;
  });
  
  /**
   * Discover if the user supplied us with custom error pagelets so we use that
   * in case we need to handle a 404 or and 500 errors.
   *
   * @param {Function} done Completion callback.
   * @returns {BigPipe} fluent interface
   * @api private
   */
  BigPipe.readable('discover', function discover(done) {
    var local = ['404', '500', 'bootstrap']
      , bigpipe = this
      , childs = [];
  
    debug('Discovering build-in pagelets, filtering out defaults (404, 500, bootstrap)');
    bigpipe._pagelets = bigpipe._pagelets.filter(function filter(Pagelet) {
      var router = Pagelet.router
        , parent = !Pagelet.prototype._parent;
  
      //
      // Crawl all the children for potential routes.
      //
      Pagelet.prototype._children.forEach(function eachChild(pagelet) {
        pagelet = filter(pagelet[0]);
        if (pagelet) childs.push(pagelet);
      });
  
      //
      // Extract 404, 500 and bootstrap pagelets.
      //
      if (parent && router && router.test('/404')) local[0] = Pagelet;
      else if (parent && router && router.test('/500')) local[1] = Pagelet;
      else if (parent && Pagelet.prototype.name === 'bootstrap') local[2] = Pagelet;
      else if (router) return Pagelet;
    }).concat(childs);
  
    async.map(local, function (Pagelet, next) {
      if ('string' !== typeof Pagelet) return next(undefined, Pagelet);
  
      debug('No %s pagelet detected, using default bigpipe %s pagelet', Pagelet, Pagelet);
      require(Pagelet + '-pagelet').optimize({
        bigpipe: bigpipe,
        transform: {
          before: bigpipe.emits('transform:pagelet:before'),
          after: bigpipe.emits('transform:pagelet:after')
        }
      }, next);
    }, function found(error, status) {
      if (error) return done(error);
  
      bigpipe._statusCodes[404] = status[0];
      bigpipe._statusCodes[500] = status[1];
      bigpipe._bootstrap = status[2];
  
      //
      // Also catalog dependencies on status Pagelets and bootstrap.
      // As the developer could have provided custom Pagelets.
      //
      bigpipe._compiler.catalog(bigpipe._pagelets.concat(status), done);
    });
  
    return this;
  });
  
  /**
   * Render a pagelet from our `statusCodes` collection.
   *
   * @param {Pagelet} pagelet Reference to pagelet that invoked status.
   * @param {Number} code The status we should handle.
   * @param {Mixed} data Nothing or something, usually an Error
   * @returns {Pagelet} Generated status pagelet.
   * @api private
   */
  BigPipe.readable('status', function status(pagelet, code, data, bootstrap) {
    if (!(code in this._statusCodes)) {
      return this.emit('error', new Error('Unsupported HTTP code: '+ code +'.'));
    }
  
    //
    // No need to do a complete bootstrap of the pagelet, only return
    // the status code Pagelet, where the name is replaced with the Pagelet
    // that received the error.
    //
    if (!bootstrap) return new this._statusCodes[code]({
      bootstrap: pagelet.bootstrap,
      parent: pagelet._parent,
      req: pagelet._req,
      res: pagelet._res,
      bigpipe: this
    }, data, pagelet.name);
  
    //
    // Do a full initialization of the status code Pagelet.
    //
    this.bootstrap(new this._statusCodes[code]({
      parent: 'bootstrap',
      req: pagelet._req,
      res: pagelet._res,
      bigpipe: this
    }, data));
  });
  
  /**
   * Insert pagelet into collection of pagelets. If pagelet is a manually
   * instantiated Pagelet push it in, otherwise resolve the path, always
   * transform the pagelet. After dependencies are catalogued the callback
   * will be called.
   *
   * @param {Mixed} pagelets array of composed Pagelet objects or file path.
   * @param {Function} done callback
   * @api public
   */
  BigPipe.readable('define', function define(pagelets, done) {
    var bigpipe = this;
  
    async.map(fabricate(pagelets), function map(Pagelet, next) {
      if ('function' !== typeof Pagelet.optimize) return next();
  
      Pagelet.optimize({
        bigpipe: bigpipe,
        transform: {
          before: bigpipe.emits('transform:pagelet:before'),
          after: bigpipe.emits('transform:pagelet:after')
        }
      }, next);
    }, function fabricated(err, pagelets) {
      if (err) return done(err);
  
      bigpipe._pagelets.push.apply(bigpipe._pagelets, pagelets.filter(Boolean));
      bigpipe.discover(done);
    });
  
    return this;
  });
  
  /**
   * Bind performance is horrible. This introduces an extra function call but can
   * be heavily optimized by the V8 engine. Only use this in cases where you would
   * normally use `.bind`.
   *
   * @param {Function} fn A method of bigpipe.
   * @returns {Function}
   * @api private
   */
  BigPipe.readable('bind', function bind(fn) {
    var bigpipe = this;
  
    return function bound(arg1, arg2, arg3) {
      fn.call(bigpipe, arg1, arg2, arg3);
    };
  });
  
  /**
   * Find and initialize pagelets based on a given id or on the pathname of the
   * request.
   *
   * @param {HTTP.Request} req The incoming HTTP request.
   * @param {HTTP.Response} res The outgoing HTTP request.
   * @param {String} id Optional id of pagelet we specifically need.
   * @api private
   */
  BigPipe.readable('router', function router(req, res, id) {
    var key = id ? id : req.method +'@'+ req.uri.pathname
      , cache = this._cache ? this._cache.get(key) || [] : []
      , pagelets = this._pagelets
      , length = pagelets.length
      , bigpipe = this
      , i = 0
      , pagelet;
  
    //
    // Cache is empty.
    //
    if (!cache.length) {
      if (id) for (; i < length; i++) {
        pagelet = pagelets[i];
  
        if (id === pagelet.prototype.id) {
          cache.push(pagelet);
          break;
        }
      } else for (; i < length; i++) {
        pagelet = pagelets[i];
  
        if (!pagelet.router.test(req.uri.pathname)) continue;
        if (pagelet.method.length && !~pagelet.method.indexOf(req.method)) continue;
  
        cache.push(pagelet);
      }
  
      if (this._cache && cache.length) {
        this._cache.set(key, cache);
        debug('Added key %s and its found pagelets to our internal lookup cache', key);
      }
    }
  
    //
    // Add an extra 404 pagelet so we always have a pagelet to display.
    //
    cache.push(this._statusCodes[404]);
  
    //
    // It could be that we have selected a couple of authorized pagelets. Filter
    // those out before sending the initialized pagelet to the callback.
    //
    (function each(pagelets) {
      var Pagelet = pagelets.shift()
        , pagelet = new Pagelet({
            params: Pagelet.router.exec(req.uri.pathname),
            parent: 'bootstrap',
            bigpipe: bigpipe,
            append: true,
            req: req,
            res: res
          });
  
      debug('Iterating over pagelets for %s testing %s atm', req.url, pagelet.path);
  
      //
      // Check if the parent Pagelet is authorized or not. If the Pagelet is
      // not allowed, continue crawling the other routable pagelets.
      //
      if (pagelet.if) {
        return pagelet.conditional(req, function authorize(allowed) {
          debug(
            'Authorization %s for %s',
            allowed ? 'allowed' : 'disallowed',
            pagelet.path
          );
  
          if (allowed) return bigpipe.bootstrap(pagelet, req, res);
          each(pagelets);
        });
      }
  
      debug('Using %s for %s', pagelet.path, req.url);
      bigpipe.bootstrap(pagelet, req, res);
    }(cache.slice(0)));
  
    return this;
  });
  
  /**
   * Run the plugins.
   *
   * @param {Array} plugins List of plugins.
   * @returns {BigPipe} fluent interface
   * @api private
   */
  BigPipe.readable('pluggable', function pluggable(plugins) {
    var bigpipe = this;
  
    plugins.forEach(function plug(plugin) {
      bigpipe.use(plugin);
    });
  
    return this;
  });
  
  /**
   * Dispatch incoming requests.
   *
   * @param {Request} req HTTP request.
   * @param {Response} res HTTP response.
   * @returns {BigPipe} fluent interface
   * @api private
   */
  BigPipe.readable('dispatch', function dispatch(req, res) {
    var bigpipe = this;
  
    return this.middleware.each(req, res, function next(error, early) {
      if (error) return bigpipe.status({ _req: req, _res: res}, 500, error, true);
      if (early) return debug('request was handled by a middleware layer');
  
      bigpipe.router(req, res);
    });
  });
  
  /**
   * Register a new plugin.
   *
   * ```js
   * bigpipe.use('ack', {
   *   //
   *   // Only ran on the server.
   *   //
   *   server: function (bigpipe, options) {
   *      // do stuff
   *   },
   *
   *   //
   *   // Runs on the client, it's automatically bundled.
   *   //
   *   client: function (bigpipe, options) {
   *      // do client stuff
   *   },
   *
   *   //
   *   // Optional library that needs to be bundled on the client. The library
   *   // should be an object having a `path` and `name` property.
   *   //
   *   library: {
   *     path: '/path/to/library',
   *     name: 'name'
   *   },
   *
   *   //
   *   // Optional plugin specific options, will be merged with Bigpipe.options
   *   //
   *   options: {}
   * });
   * ```
   *
   * @param {String} name The name of the plugin.
   * @param {Object} plugin The plugin that contains client and server extensions.
   * @api public
   */
  BigPipe.readable('use', function use(name, plugin) {
    if ('object' === typeof name) {
      plugin = name;
      name = plugin.name;
    }
  
    if (!name) throw new Error('Plugin should be specified with a name.');
    if ('string' !== typeof name) throw new Error('Plugin names should be a string.');
    if ('string' === typeof plugin) plugin = require(plugin);
  
    //
    // Plugin accepts an object or a function only.
    //
    if (!/^(object|function)$/.test(typeof plugin)) {
      throw new Error('Plugin should be an object or function.');
    }
  
    //
    // Plugin require a client, server or both to be specified in the object.
    //
    if (!('server' in plugin || 'client' in plugin)) {
      throw new Error('The plugin is missing a client or server function.');
    }
  
    if (name in this._plugins) {
      throw new Error('The plugin name was already defined. Please select an unique name for each plugin');
    }
  
    //
    // Resolve the path of the plugin as it might be required
    // to resolve dependencies.
    //
    Object.keys(require.cache).forEach(function (key) {
      if (require.cache[key].exports !== plugin) return;
      plugin.path = key;
    });
  
    debug('Added plugin `%s`', name);
  
    this._plugins[name] = plugin;
    if (!plugin.server) return this;
  
    this._options.merge(plugin.options || {});
    plugin.server.call(this, this, this._options);
  
    return this;
  });
  
  /**
   * Redirect the user.
   *
   * @param {String} location Where should we redirect to.
   * @param {Number} status The status number.
   * @api public
   */
  BigPipe.readable('redirect', function redirect(pagelet, location, status, options) {
    options = options || {};
  
    pagelet._res.statusCode = +status || 301;
    pagelet._res.setHeader('Location', location);
  
    //
    // Instruct browsers to not cache the redirect.
    //
    if (options.cache === false) {
      pagelet._res.setHeader('Pragma', 'no-cache');
      pagelet._res.setHeader('Expires', 'Sat, 26 Jul 1997 05:00:00 GMT');
      pagelet._res.setHeader('Cache-Control', [
        'no-store', 'no-cache', 'must-revalidate', 'post-check=0', 'pre-check=0'
      ].join(', '));
    }
  
    pagelet._res.end();
  
    if (pagelet.listeners('end').length) pagelet.emit('end');
    return pagelet.debug('Redirecting to %s', location);
  });
  
  /**
   * Initialize a new Bootstrap Pagelet and return it so the routed Pagelet and
   * its childs can use it as state keeper. The HTML of the bootstrap pagelet is
   * flushed asap to the client.
   *
   * @param {Pagelet} child Pagelet that was found by the Router.
   * @param {ServerRequest} req HTTP server request.
   * @param {ServerResponse} res HTTP server response.
   * @returns {Bootstrap} Bootstrap Pagelet.
   * @api private
   */
  BigPipe.readable('bootstrap', function bootstrap(child, req, res) {
    req = req || child._req;
    res = res || child._res;
  
    //
    // It could be that the initialization handled the page rendering through
    // a `page.redirect()` or a `page.notFound()` call so we should terminate
    // the request once that happens.
    //
    if (res.finished) return this;
  
    //
    // @TODO rel prefetch for resources that are used on the next page?
    // @TODO cache manifest.
    //
    res.statusCode = child.statusCode;
  
    //
    // If we have a `no_pagelet_js` flag, we should force a different
    // rendering mode. This parameter is automatically added when we've
    // detected that someone is browsing the site without JavaScript enabled.
    //
    // In addition to that, the other render modes only work if your browser
    // supports trailing headers which where introduced in HTTP 1.1 so we need
    // to make sure that this is something that the browser understands.
    // Instead of checking just for `1.1` we want to make sure that it just
    // tests for every http version above 1.0 as http 2.0 is just around the
    // corner.
    //
    if (
         'no_pagelet_js' in req.query && +req.query.no_pagelet_js === 1
      || !(req.httpVersionMajor >= 1 && req.httpVersionMinor >= 1)
    ) {
      child.debug('Forcing `sync` instead of %s due lack of HTTP 1.1 or JS', child.mode);
      child.mode = 'sync';
    }
  
    //
    // Create a bootstrap Pagelet, this is a special Pagelet that is flushed
    // as soon as possible to instantiate the client side rendering.
    //
    child.bootstrap = new this._bootstrap({
      dependencies: this._compiler.page(child),
      params: child._params,
      length: child.length,
      child: child.name,
      mode: child.mode,
      bigpipe: this,
      res: res,
      req: req
    });
  
    this.emit('bootstrap', child, req, res);
  
    if (child.initialize) {
      if (child.initialize.length) {
        child.debug('Waiting for `initialize` method before rendering');
        child.initialize(child.init.bind(child));
      } else {
        child.initialize();
        child.init();
      }
    } else {
      child.init();
    }
  
    return this;
  });
  
  /**
   * Mode: Synchronous
   * Output the pagelets fully rendered in the HTML template.
   *
   * @TODO remove pagelet's that have `authorized` set to `false`
   * @TODO Also write the CSS and JavaScript.
   *
   * @param {Pagelet} pagelet Parent pagelet
   * @api private
   */
  BigPipe.readable('sync', function synchronous(pagelet) {
    var bigpipe = this
      , pagelets;
  
    //
    // Because we're synchronously rendering the pagelets we need to discover
    // which one's are enabled before we send the bootstrap code so it can include
    // the CSS files of the enabled pagelets in the HEAD of the page so there is
    // styling available.
    //
    pagelet.bootstrap.render();
    pagelet.once('discover', function discovered() {
      pagelet.debug('Processing the pagelets in `sync` mode');
  
      pagelets = pagelet._enabled.concat(pagelet._disabled, pagelet);
      async.each(pagelets, function render(child, next) {
        pagelet.debug('Invoking pagelet %s/%s render', child.name, child.id);
  
        child.render({ mode: 'sync' }, function rendered(error, content) {
          if (error) return render(bigpipe.capture(error), next);
  
          child.write(content);
          next();
        });
      }, function done() {
        pagelet.bootstrap.reduce().end();
      });
    }).discover();
  });
  
  /**
   * Mode: Asynchronous
   * Output the pagelets as fast as possible.
   *
   * @param {Pagelet} pagelet Parent pagelet
   * @api private
   */
  BigPipe.readable('async', function asynchronous(pagelet) {
    var bigpipe = this
      , pagelets;
  
    //
    // Flush the initial headers asap so the browser can start detect encoding
    // start downloading assets and prepare for rendering additional pagelets.
    //
    pagelet.bootstrap.render().flush(function headers(error) {
      if (error) return bigpipe.capture(error, pagelet, true);
  
      pagelet.once('discover', function discovered() {
        pagelet.debug('Processing the pagelets in `async` mode');
  
        pagelets = pagelet._enabled.concat(pagelet._disabled, pagelet);
        async.each(pagelets, function render(child, next) {
          pagelet.debug('Invoking pagelet %s/%s render', child.name, child.id);
  
          child.render({
            data: bigpipe._compiler.pagelet(child)
          }, function rendered(error, content) {
            if (error) return render(bigpipe.capture(error), child, next);
            child.write(content).flush(next);
          });
        }, function done(error) {
          if (error) return bigpipe.capture(error);
          pagelet.end();
        });
      }).discover();
    });
  });
  
  /**
   * Mode: pipeline
   * Output the pagelets as fast as possible but in order.
   *
   * @param {Pagelet} pagelet Parent pagelet
   * @api private
   */
  BigPipe.readable('pipeline', function pipeline(pagelet) {
    var bigpipe = this
      , pagelets;
  
    //
    // Flush the initial headers asap so the browser can start detect encoding
    // start downloading assets and prepare for rendering additional pagelets.
    //
    pagelet.bootstrap.render().flush(function headers(error) {
      if (error) return bigpipe.capture(error, pagelet, true);
  
      pagelet.once('discover', function discovered() {
        pagelet.debug('Processing the pagelets in `async` mode');
  
        //
        // Concat pagelets and provide order through the pagelet id.
        //
        pagelets = pagelet._enabled
          .concat(pagelet._disabled, pagelet)
          .sort(function sortByPageletId(a, b) {
            if (a.id < b.id) return -1;
            if (a.id > b.id) return 1;
            return 0;
          });
  
        //
        // Keep track of the order of pagelets through their id. Asynchronous
        // render all the different pagelets, but only write and flush in the
        // order that was set by sort.
        //
        var order = pagelets.map(function returnId(pagelet) { return pagelet.id; })
          , output = []
          , i = 0;
  
        async.each(pagelets, function render(child, next) {
          pagelet.debug('Invoking pagelet %s/%s render', child.name, child.id);
  
          child.render({
            data: bigpipe._compiler.pagelet(child)
          }, function rendered(error, content) {
            if (error) return render(bigpipe.capture(error), child, next);
            output[order.indexOf(child.id)] = content;
  
            for (; i < output.length; i++) {
              if (!output[i]) return next();
              child.write(output[i]).flush();
            }
  
            next();
          });
        }, function done(error) {
          if (error) return bigpipe.capture(error);
          pagelet.end();
        });
      }).discover();
    });
  });
  
  /**
   * We've received an error. Close down pagelet and display a 500 error Pagelet.
   *
   * @TODO handle the case when we've already flushed the initial bootstrap code
   * to the client and we're presented with an error.
   *
   * @param {Error} error Optional error argument to trigger the error pagelet.
   * @param {Pagelet} pagelet Reference to the pagelet that triggered the error.
   * @param {Boolean} bootstrap Do full bootstrap if true.
   * @returns {BigPipe} fluent interface.
   * @api private
   */
  BigPipe.readable('capture', function capture(error, pagelet, bootstrap) {
    debug(
      'Captured an error from %s: %s, displaying error pagelet',
      pagelet.name,
      error
    );
  
    return this.status(pagelet, 500, error, bootstrap || false);
  });
  
  /**
   * Completely destroy the instance and remove/release all its references.
   *
   * @type {Function}
   * @api public
   */
  BigPipe.readable('destroy', destroy([
    '_pagelets', '_server', '_options', '_temper', '_plugins', '_cache',
    '_statusCodes', '_zipline', '_compiler', 'middleware'
  ], {
    before: function before() {
      try { this._server.close(); }
      catch (e) {}
    },
    after: 'removeAllListeners'
  }));
  
  /**
   * Create a new Pagelet/BigPipe server.
   *
   * @param {Number} port port to listen on
   * @param {Object} options Configuration.
   * @returns {BigPipe}
   * @api public
   */
  BigPipe.createServer = function createServer(port, options) {
    options = 'object' === typeof port ? port : options || {};
    if ('number' === typeof port || 'string' === typeof port) options.port = +port;
  
    var listen = options.listen === false
      , bigpipe;
  
    //
    // Listening is done by our own .listen method, so we need to tell the
    // createServer module that we don't want it to start listening to our sizzle.
    // This option is forced and should not be override by users configuration.
    //
    options.listen = false;
    options.port = options.port || 8080;
    bigpipe = new BigPipe(require('create-server')(options), options);
  
    //
    // By default the server will listen. Passing options.listen === false
    // is only required if listening needs to be done with a manual call.
    // BigPipe.createServer will pass as argument.
    //
    return listen ? bigpipe : bigpipe.listen(options.port);
  };
  
  //
  // Expose the constructor.
  //
  module.exports = BigPipe;
  