docs: function Bobsled(opts)
  {
    require('http').Server.call(this);
  
    if (typeof(opts) == "undefined") opts = {};
  
    this.port = ("port" in opts) ? opts.port : 8085; // reads like BOBS on an old calculator
  
    if ("env" in opts) this.env = opts.env;
    if ("uid" in opts) this.uid = opts.uid;
    if ("gid" in opts) this.gid = opts.gid;
  
    this.template_compiler = ("template_compiler" in opts) ? opts.template_compiler : function () { throw new Error("no template compiler configured"); };
  
    // TODO: support other template engines besides doT?
    //
    this.template_config = {
      evaluate:    /<%([\s\S]+?)%>/g, // some erb like delimiters
      interpolate: /<%=([\s\S]+?)%>/g,
      varname: ["Page", "Server"],
      strip: false,
      append: true
    };
  
    this.templates = {};
  
    // compile all templates, useful for checking correctness on startup
    this.compileTemplates = function() {
      for (var template_name in this.templates) this.templates[template_name].compile();
    };
  
    this.controllers = {
      template: function(pathinfo, request, response) {
        var template_name = pathinfo.basename || "index"
        if (template_name in this.templates) {
          this.templateResponder(template_name, pathinfo, response);
        } else {
          this.emit("notFound", pathinfo, request, response);
        }
      }.bind(this),
      "static": function(pathinfo, request, response) {
        var filename = "." + pathinfo.dirname + "/" + pathinfo.basename + pathinfo.extname;
        if (!fs.statSync(filename).isFile()) {
          this.emit("notFound", pathinfo, request, response);
        } else {
          response.writeHead(200, {"content-type": "text/css"}); // HACK TODO mimetype
          fs.readFile(filename, function (err, data) { response.end(data); });
        }
      }.bind(this),
      favicon: function(pathinfo, request, response) {
        response.writeHead(200, {"content-type": "image/x-icon"}); // TODO: set expires
        fs.readFile("favicon.ico", function (err, data) { response.end(data); });
      },
      notFound: function (pathinfo, request, response) {
        if ("404" in this.templates) {
          this.templateResponder("404", { statusCode: 404, pathinfo: pathinfo, request: request }, response);
        } else {
          response.writeHead(404, {"content-type": "text/plain"});
          response.end(request.url + " not found");
        }
      }.bind(this)
    };
  
    // TODO: config/recipe for default routes
    this.routes = {
      GET: {
        "/": {
          "favicon.ico": this.controllers.favicon,
          "*.html": this.controllers.template
        },
      },
      POST: { }
    };
  
    // setup static routes
    if (true) { // TODO: opts.static? (could pass in path?)
      var static_folders = {"css": "/css", "img": "/img", "js": "/js"}; // TODO: pass in opts?
      for (folder in static_folders) {
        if (fs.existsSync(folder) && fs.statSync(folder).isDirectory()) {
          this.routes.GET[static_folders[folder]] = { "*": this.controllers.static };
        }
      }
    }
  
    if (opts.env) {
      // TODO: make path part of config?
      this.routes.GET["/"]["env"] = function(pathinfo, request, response) {
        response.writeHead(200, {"content-type": "application/json"}); // TODO switch to HTML on extname
        var data = { env: process.env, uid: process.getuid(), os: {} };
        var os = require("os");
        ["hostname", "type", "platform", "arch", "release", "networkInterfaces", "cpus"].forEach(function (a) {
          data.os[a] = os[a]();
        });
        if (process.env.HOME) data.home = fs.readdirSync(process.env.HOME);
        // data.passwd = require("getpw").getpwuid(data.uid); (TODO: pass to constructor to eliminate hard dep)
        response.end(JSON.stringify(data, null, "  "));
      }
    }
  
    this.redirect = function(new_url, response) {
        response.writeHead(302, { 'Location': new_url });
        response.end();
    };
  
    this.on("notFound", this.controllers.notFound);
  
    this.route = function(request, response, body) {
      var parsed_url = url.parse(request.url, true);
      var method = request.method,
          dirname = path.dirname(parsed_url.pathname).replace(/\/*$/, ''), // strip any trailing slash
          extname = path.extname(parsed_url.pathname), // TODO: should have a default for extname?
          basename = path.basename(parsed_url.pathname, extname) || "index"; // TODO: configurable default basename?
  
      console.log("%s %s/%s%s", method, dirname, basename, extname); // TODO: add an on/off switch for this logging
  
      if (dirname == "") dirname = "/"; // special case root routes, where the leading and trailing slash are the same
  
      // TODO: add a switch for case (in)sensitivity
      // dirname = dirname.toLowerCase();
      // basename = basename.toLowerCase();
  
      var controller = this.controllers.notFound;
      if (this.routes[method] && this.routes[method][dirname]) {
         // find the most specific match possible
         var candidate, candidates = [basename + extname, basename, "*" + extname, "*"];
         while (candidate = candidates.shift()) {
           if (candidate in this.routes[method][dirname]) {
               controller = this.routes[method][dirname][candidate];
               break; // while
           }
         }
      }
      return controller({ dirname: dirname, basename: basename, extname: extname, query: parsed_url.query }, request, response, body);
    };
  }