diff options
Diffstat (limited to 'node_modules/express/lib')
| -rw-r--r-- | node_modules/express/lib/application.js | 531 | ||||
| -rw-r--r-- | node_modules/express/lib/express.js | 92 | ||||
| -rw-r--r-- | node_modules/express/lib/middleware.js | 33 | ||||
| -rw-r--r-- | node_modules/express/lib/request.js | 526 | ||||
| -rw-r--r-- | node_modules/express/lib/response.js | 756 | ||||
| -rw-r--r-- | node_modules/express/lib/router/index.js | 273 | ||||
| -rw-r--r-- | node_modules/express/lib/router/route.js | 72 | ||||
| -rw-r--r-- | node_modules/express/lib/utils.js | 313 | ||||
| -rw-r--r-- | node_modules/express/lib/view.js | 76 | 
9 files changed, 2672 insertions, 0 deletions
| diff --git a/node_modules/express/lib/application.js b/node_modules/express/lib/application.js new file mode 100644 index 0000000..e841c12 --- /dev/null +++ b/node_modules/express/lib/application.js @@ -0,0 +1,531 @@ +/** + * Module dependencies. + */ + +var connect = require('connect') +  , Router = require('./router') +  , methods = require('methods') +  , middleware = require('./middleware') +  , debug = require('debug')('express:application') +  , locals = require('./utils').locals +  , View = require('./view') +  , utils = connect.utils +  , path = require('path') +  , http = require('http') +  , join = path.join; + +/** + * Application prototype. + */ + +var app = exports = module.exports = {}; + +/** + * Initialize the server. + * + *   - setup default configuration + *   - setup default middleware + *   - setup route reflection methods + * + * @api private + */ + +app.init = function(){ +  this.cache = {}; +  this.settings = {}; +  this.engines = {}; +  this.defaultConfiguration(); +}; + +/** + * Initialize application configuration. + * + * @api private + */ + +app.defaultConfiguration = function(){ +  // default settings +  this.enable('x-powered-by'); +  this.set('env', process.env.NODE_ENV || 'development'); +  this.set('subdomain offset', 2); +  debug('booting in %s mode', this.get('env')); + +  // implicit middleware +  this.use(connect.query()); +  this.use(middleware.init(this)); + +  // inherit protos +  this.on('mount', function(parent){ +    this.request.__proto__ = parent.request; +    this.response.__proto__ = parent.response; +    this.engines.__proto__ = parent.engines; +    this.settings.__proto__ = parent.settings; +  }); + +  // router +  this._router = new Router(this); +  this.routes = this._router.map; +  this.__defineGetter__('router', function(){ +    this._usedRouter = true; +    this._router.caseSensitive = this.enabled('case sensitive routing'); +    this._router.strict = this.enabled('strict routing'); +    return this._router.middleware; +  }); + +  // setup locals +  this.locals = locals(this); + +  // default locals +  this.locals.settings = this.settings; + +  // default configuration +  this.set('view', View); +  this.set('views', process.cwd() + '/views'); +  this.set('jsonp callback name', 'callback'); + +  this.configure('development', function(){ +    this.set('json spaces', 2); +  }); + +  this.configure('production', function(){ +    this.enable('view cache'); +  }); +}; + +/** + * Proxy `connect#use()` to apply settings to + * mounted applications. + * + * @param {String|Function|Server} route + * @param {Function|Server} fn + * @return {app} for chaining + * @api public + */ + +app.use = function(route, fn){ +  var app; + +  // default route to '/' +  if ('string' != typeof route) fn = route, route = '/'; + +  // express app +  if (fn.handle && fn.set) app = fn; + +  // restore .app property on req and res +  if (app) { +    app.route = route; +    fn = function(req, res, next) { +      var orig = req.app; +      app.handle(req, res, function(err){ +        req.app = res.app = orig; +        req.__proto__ = orig.request; +        res.__proto__ = orig.response; +        next(err); +      }); +    }; +  } + +  connect.proto.use.call(this, route, fn); + +  // mounted an app +  if (app) { +    app.parent = this; +    app.emit('mount', this); +  } + +  return this; +}; + +/** + * Register the given template engine callback `fn` + * as `ext`. + * + * By default will `require()` the engine based on the + * file extension. For example if you try to render + * a "foo.jade" file Express will invoke the following internally: + * + *     app.engine('jade', require('jade').__express); + * + * For engines that do not provide `.__express` out of the box, + * or if you wish to "map" a different extension to the template engine + * you may use this method. For example mapping the EJS template engine to + * ".html" files: + * + *     app.engine('html', require('ejs').renderFile); + * + * In this case EJS provides a `.renderFile()` method with + * the same signature that Express expects: `(path, options, callback)`, + * though note that it aliases this method as `ejs.__express` internally + * so if you're using ".ejs" extensions you dont need to do anything. + * + * Some template engines do not follow this convention, the + * [Consolidate.js](https://github.com/visionmedia/consolidate.js) + * library was created to map all of node's popular template + * engines to follow this convention, thus allowing them to + * work seamlessly within Express. + * + * @param {String} ext + * @param {Function} fn + * @return {app} for chaining + * @api public + */ + +app.engine = function(ext, fn){ +  if ('function' != typeof fn) throw new Error('callback function required'); +  if ('.' != ext[0]) ext = '.' + ext; +  this.engines[ext] = fn; +  return this; +}; + +/** + * Map the given param placeholder `name`(s) to the given callback(s). + * + * Parameter mapping is used to provide pre-conditions to routes + * which use normalized placeholders. For example a _:user_id_ parameter + * could automatically load a user's information from the database without + * any additional code, + * + * The callback uses the samesignature as middleware, the only differencing + * being that the value of the placeholder is passed, in this case the _id_ + * of the user. Once the `next()` function is invoked, just like middleware + * it will continue on to execute the route, or subsequent parameter functions. + * + *      app.param('user_id', function(req, res, next, id){ + *        User.find(id, function(err, user){ + *          if (err) { + *            next(err); + *          } else if (user) { + *            req.user = user; + *            next(); + *          } else { + *            next(new Error('failed to load user')); + *          } + *        }); + *      }); + * + * @param {String|Array} name + * @param {Function} fn + * @return {app} for chaining + * @api public + */ + +app.param = function(name, fn){ +  var self = this +    , fns = [].slice.call(arguments, 1); + +  // array +  if (Array.isArray(name)) { +    name.forEach(function(name){ +      fns.forEach(function(fn){ +        self.param(name, fn); +      }); +    }); +  // param logic +  } else if ('function' == typeof name) { +    this._router.param(name); +  // single +  } else { +    if (':' == name[0]) name = name.substr(1); +    fns.forEach(function(fn){ +      self._router.param(name, fn); +    }); +  } + +  return this; +}; + +/** + * Assign `setting` to `val`, or return `setting`'s value. + * + *    app.set('foo', 'bar'); + *    app.get('foo'); + *    // => "bar" + * + * Mounted servers inherit their parent server's settings. + * + * @param {String} setting + * @param {String} val + * @return {Server} for chaining + * @api public + */ + +app.set = function(setting, val){ +  if (1 == arguments.length) { +    return this.settings[setting]; +  } else { +    this.settings[setting] = val; +    return this; +  } +}; + +/** + * Return the app's absolute pathname + * based on the parent(s) that have + * mounted it. + * + * For example if the application was + * mounted as "/admin", which itself + * was mounted as "/blog" then the + * return value would be "/blog/admin". + * + * @return {String} + * @api private + */ + +app.path = function(){ +  return this.parent +    ? this.parent.path() + this.route +    : ''; +}; + +/** + * Check if `setting` is enabled (truthy). + * + *    app.enabled('foo') + *    // => false + * + *    app.enable('foo') + *    app.enabled('foo') + *    // => true + * + * @param {String} setting + * @return {Boolean} + * @api public + */ + +app.enabled = function(setting){ +  return !!this.set(setting); +}; + +/** + * Check if `setting` is disabled. + * + *    app.disabled('foo') + *    // => true + * + *    app.enable('foo') + *    app.disabled('foo') + *    // => false + * + * @param {String} setting + * @return {Boolean} + * @api public + */ + +app.disabled = function(setting){ +  return !this.set(setting); +}; + +/** + * Enable `setting`. + * + * @param {String} setting + * @return {app} for chaining + * @api public + */ + +app.enable = function(setting){ +  return this.set(setting, true); +}; + +/** + * Disable `setting`. + * + * @param {String} setting + * @return {app} for chaining + * @api public + */ + +app.disable = function(setting){ +  return this.set(setting, false); +}; + +/** + * Configure callback for zero or more envs, + * when no `env` is specified that callback will + * be invoked for all environments. Any combination + * can be used multiple times, in any order desired. + * + * Examples: + * + *    app.configure(function(){ + *      // executed for all envs + *    }); + * + *    app.configure('stage', function(){ + *      // executed staging env + *    }); + * + *    app.configure('stage', 'production', function(){ + *      // executed for stage and production + *    }); + * + * Note: + * + *  These callbacks are invoked immediately, and + *  are effectively sugar for the following: + * + *     var env = process.env.NODE_ENV || 'development'; + * + *      switch (env) { + *        case 'development': + *          ... + *          break; + *        case 'stage': + *          ... + *          break; + *        case 'production': + *          ... + *          break; + *      } + * + * @param {String} env... + * @param {Function} fn + * @return {app} for chaining + * @api public + */ + +app.configure = function(env, fn){ +  var envs = 'all' +    , args = [].slice.call(arguments); +  fn = args.pop(); +  if (args.length) envs = args; +  if ('all' == envs || ~envs.indexOf(this.settings.env)) fn.call(this); +  return this; +}; + +/** + * Delegate `.VERB(...)` calls to `router.VERB(...)`. + */ + +methods.forEach(function(method){ +  app[method] = function(path){ +    if ('get' == method && 1 == arguments.length) return this.set(path); + +    // if no router attached yet, attach the router +    if (!this._usedRouter) this.use(this.router); + +    // setup route +    this._router[method].apply(this._router, arguments); +    return this; +  }; +}); + +/** + * Special-cased "all" method, applying the given route `path`, + * middleware, and callback to _every_ HTTP method. + * + * @param {String} path + * @param {Function} ... + * @return {app} for chaining + * @api public + */ + +app.all = function(path){ +  var args = arguments; +  methods.forEach(function(method){ +    app[method].apply(this, args); +  }, this); +  return this; +}; + +// del -> delete alias + +app.del = app.delete; + +/** + * Render the given view `name` name with `options` + * and a callback accepting an error and the + * rendered template string. + * + * Example: + * + *    app.render('email', { name: 'Tobi' }, function(err, html){ + *      // ... + *    }) + * + * @param {String} name + * @param {String|Function} options or fn + * @param {Function} fn + * @api public + */ + +app.render = function(name, options, fn){ +  var opts = {} +    , cache = this.cache +    , engines = this.engines +    , view; + +  // support callback function as second arg +  if ('function' == typeof options) { +    fn = options, options = {}; +  } + +  // merge app.locals +  utils.merge(opts, this.locals); + +  // merge options._locals +  if (options._locals) utils.merge(opts, options._locals); + +  // merge options +  utils.merge(opts, options); + +  // set .cache unless explicitly provided +  opts.cache = null == opts.cache +    ? this.enabled('view cache') +    : opts.cache; + +  // primed cache +  if (opts.cache) view = cache[name]; + +  // view +  if (!view) { +    view = new (this.get('view'))(name, { +      defaultEngine: this.get('view engine'), +      root: this.get('views'), +      engines: engines +    }); + +    if (!view.path) { +      var err = new Error('Failed to lookup view "' + name + '"'); +      err.view = view; +      return fn(err); +    } + +    // prime the cache +    if (opts.cache) cache[name] = view; +  } + +  // render +  try { +    view.render(opts, fn); +  } catch (err) { +    fn(err); +  } +}; + +/** + * Listen for connections. + * + * A node `http.Server` is returned, with this + * application (which is a `Function`) as its + * callback. If you wish to create both an HTTP + * and HTTPS server you may do so with the "http" + * and "https" modules as shown here: + * + *    var http = require('http') + *      , https = require('https') + *      , express = require('express') + *      , app = express(); + * + *    http.createServer(app).listen(80); + *    https.createServer({ ... }, app).listen(443); + * + * @return {http.Server} + * @api public + */ + +app.listen = function(){ +  var server = http.createServer(this); +  return server.listen.apply(server, arguments); +}; diff --git a/node_modules/express/lib/express.js b/node_modules/express/lib/express.js new file mode 100644 index 0000000..ab55889 --- /dev/null +++ b/node_modules/express/lib/express.js @@ -0,0 +1,92 @@ +/** + * Module dependencies. + */ + +var connect = require('connect') +  , proto = require('./application') +  , Route = require('./router/route') +  , Router = require('./router') +  , req = require('./request') +  , res = require('./response') +  , utils = connect.utils; + +/** + * Expose `createApplication()`. + */ + +exports = module.exports = createApplication; + +/** + * Framework version. + */ + +exports.version = '3.2.0'; + +/** + * Expose mime. + */ + +exports.mime = connect.mime; + +/** + * Create an express application. + * + * @return {Function} + * @api public + */ + +function createApplication() { +  var app = connect(); +  utils.merge(app, proto); +  app.request = { __proto__: req }; +  app.response = { __proto__: res }; +  app.init(); +  return app; +} + +/** + * Expose connect.middleware as express.* + * for example `express.logger` etc. + */ + +for (var key in connect.middleware) { +  Object.defineProperty( +      exports +    , key +    , Object.getOwnPropertyDescriptor(connect.middleware, key)); +} + +/** + * Error on createServer(). + */ + +exports.createServer = function(){ +  console.warn('Warning: express.createServer() is deprecated, express'); +  console.warn('applications no longer inherit from http.Server,'); +  console.warn('please use:'); +  console.warn(''); +  console.warn('  var express = require("express");'); +  console.warn('  var app = express();'); +  console.warn(''); +  return createApplication(); +}; + +/** + * Expose the prototypes. + */ + +exports.application = proto; +exports.request = req; +exports.response = res; + +/** + * Expose constructors. + */ + +exports.Route = Route; +exports.Router = Router; + +// Error handler title + +exports.errorHandler.title = 'Express'; + diff --git a/node_modules/express/lib/middleware.js b/node_modules/express/lib/middleware.js new file mode 100644 index 0000000..308c5bb --- /dev/null +++ b/node_modules/express/lib/middleware.js @@ -0,0 +1,33 @@ + +/** + * Module dependencies. + */ + +var utils = require('./utils'); + +/** + * Initialization middleware, exposing the + * request and response to eachother, as well + * as defaulting the X-Powered-By header field. + * + * @param {Function} app + * @return {Function} + * @api private + */ + +exports.init = function(app){ +  return function expressInit(req, res, next){ +    req.app = res.app = app; +    if (app.enabled('x-powered-by')) res.setHeader('X-Powered-By', 'Express'); +    req.res = res; +    res.req = req; +    req.next = next; + +    req.__proto__ = app.request; +    res.__proto__ = app.response; + +    res.locals = res.locals || utils.locals(res); + +    next(); +  } +}; diff --git a/node_modules/express/lib/request.js b/node_modules/express/lib/request.js new file mode 100644 index 0000000..fd201e3 --- /dev/null +++ b/node_modules/express/lib/request.js @@ -0,0 +1,526 @@ + +/** + * Module dependencies. + */ + +var http = require('http') +  , utils = require('./utils') +  , connect = require('connect') +  , fresh = require('fresh') +  , parseRange = require('range-parser') +  , parse = connect.utils.parseUrl +  , mime = connect.mime; + +/** + * Request prototype. + */ + +var req = exports = module.exports = { +  __proto__: http.IncomingMessage.prototype +}; + +/** + * Return request header. + * + * The `Referrer` header field is special-cased, + * both `Referrer` and `Referer` are interchangeable. + * + * Examples: + * + *     req.get('Content-Type'); + *     // => "text/plain" + * + *     req.get('content-type'); + *     // => "text/plain" + * + *     req.get('Something'); + *     // => undefined + * + * Aliased as `req.header()`. + * + * @param {String} name + * @return {String} + * @api public + */ + +req.get = +req.header = function(name){ +  switch (name = name.toLowerCase()) { +    case 'referer': +    case 'referrer': +      return this.headers.referrer +        || this.headers.referer; +    default: +      return this.headers[name]; +  } +}; + +/** + * Check if the given `type(s)` is acceptable, returning + * the best match when true, otherwise `undefined`, in which + * case you should respond with 406 "Not Acceptable". + * + * The `type` value may be a single mime type string + * such as "application/json", the extension name + * such as "json", a comma-delimted list such as "json, html, text/plain", + * or an array `["json", "html", "text/plain"]`. When a list + * or array is given the _best_ match, if any is returned. + * + * Examples: + * + *     // Accept: text/html + *     req.accepts('html'); + *     // => "html" + * + *     // Accept: text/*, application/json + *     req.accepts('html'); + *     // => "html" + *     req.accepts('text/html'); + *     // => "text/html" + *     req.accepts('json, text'); + *     // => "json" + *     req.accepts('application/json'); + *     // => "application/json" + * + *     // Accept: text/*, application/json + *     req.accepts('image/png'); + *     req.accepts('png'); + *     // => undefined + * + *     // Accept: text/*;q=.5, application/json + *     req.accepts(['html', 'json']); + *     req.accepts('html, json'); + *     // => "json" + * + * @param {String|Array} type(s) + * @return {String} + * @api public + */ + +req.accepts = function(type){ +  return utils.accepts(type, this.get('Accept')); +}; + +/** + * Check if the given `encoding` is accepted. + * + * @param {String} encoding + * @return {Boolean} + * @api public + */ + +req.acceptsEncoding = function(encoding){ +  return ~this.acceptedEncodings.indexOf(encoding); +}; + +/** + * Check if the given `charset` is acceptable, + * otherwise you should respond with 406 "Not Acceptable". + * + * @param {String} charset + * @return {Boolean} + * @api public + */ + +req.acceptsCharset = function(charset){ +  var accepted = this.acceptedCharsets; +  return accepted.length +    ? ~accepted.indexOf(charset) +    : true; +}; + +/** + * Check if the given `lang` is acceptable, + * otherwise you should respond with 406 "Not Acceptable". + * + * @param {String} lang + * @return {Boolean} + * @api public + */ + +req.acceptsLanguage = function(lang){ +  var accepted = this.acceptedLanguages; +  return accepted.length +    ? ~accepted.indexOf(lang) +    : true; +}; + +/** + * Parse Range header field, + * capping to the given `size`. + * + * Unspecified ranges such as "0-" require + * knowledge of your resource length. In + * the case of a byte range this is of course + * the total number of bytes. If the Range + * header field is not given `null` is returned, + * `-1` when unsatisfiable, `-2` when syntactically invalid. + * + * NOTE: remember that ranges are inclusive, so + * for example "Range: users=0-3" should respond + * with 4 users when available, not 3. + * + * @param {Number} size + * @return {Array} + * @api public + */ + +req.range = function(size){ +  var range = this.get('Range'); +  if (!range) return; +  return parseRange(size, range); +}; + +/** + * Return an array of encodings. + * + * Examples: + * + *     ['gzip', 'deflate'] + * + * @return {Array} + * @api public + */ + +req.__defineGetter__('acceptedEncodings', function(){ +  var accept = this.get('Accept-Encoding'); +  return accept +    ? accept.trim().split(/ *, */) +    : []; +}); + +/** + * Return an array of Accepted media types + * ordered from highest quality to lowest. + * + * Examples: + * + *     [ { value: 'application/json', + *         quality: 1, + *         type: 'application', + *         subtype: 'json' }, + *       { value: 'text/html', + *         quality: 0.5, + *         type: 'text', + *         subtype: 'html' } ] + * + * @return {Array} + * @api public + */ + +req.__defineGetter__('accepted', function(){ +  var accept = this.get('Accept'); +  return accept +    ? utils.parseAccept(accept) +    : []; +}); + +/** + * Return an array of Accepted languages + * ordered from highest quality to lowest. + * + * Examples: + * + *     Accept-Language: en;q=.5, en-us + *     ['en-us', 'en'] + * + * @return {Array} + * @api public + */ + +req.__defineGetter__('acceptedLanguages', function(){ +  var accept = this.get('Accept-Language'); +  return accept +    ? utils +      .parseParams(accept) +      .map(function(obj){ +        return obj.value; +      }) +    : []; +}); + +/** + * Return an array of Accepted charsets + * ordered from highest quality to lowest. + * + * Examples: + * + *     Accept-Charset: iso-8859-5;q=.2, unicode-1-1;q=0.8 + *     ['unicode-1-1', 'iso-8859-5'] + * + * @return {Array} + * @api public + */ + +req.__defineGetter__('acceptedCharsets', function(){ +  var accept = this.get('Accept-Charset'); +  return accept +    ? utils +      .parseParams(accept) +      .map(function(obj){ +        return obj.value; +      }) +    : []; +}); + +/** + * Return the value of param `name` when present or `defaultValue`. + * + *  - Checks route placeholders, ex: _/user/:id_ + *  - Checks body params, ex: id=12, {"id":12} + *  - Checks query string params, ex: ?id=12 + * + * To utilize request bodies, `req.body` + * should be an object. This can be done by using + * the `connect.bodyParser()` middleware. + * + * @param {String} name + * @param {Mixed} [defaultValue] + * @return {String} + * @api public + */ + +req.param = function(name, defaultValue){ +  var params = this.params || {}; +  var body = this.body || {}; +  var query = this.query || {}; +  if (null != params[name] && params.hasOwnProperty(name)) return params[name]; +  if (null != body[name]) return body[name]; +  if (null != query[name]) return query[name]; +  return defaultValue; +}; + +/** + * Check if the incoming request contains the "Content-Type" + * header field, and it contains the give mime `type`. + * + * Examples: + * + *      // With Content-Type: text/html; charset=utf-8 + *      req.is('html'); + *      req.is('text/html'); + *      req.is('text/*'); + *      // => true + * + *      // When Content-Type is application/json + *      req.is('json'); + *      req.is('application/json'); + *      req.is('application/*'); + *      // => true + * + *      req.is('html'); + *      // => false + * + * @param {String} type + * @return {Boolean} + * @api public + */ + +req.is = function(type){ +  var ct = this.get('Content-Type'); +  if (!ct) return false; +  ct = ct.split(';')[0]; +  if (!~type.indexOf('/')) type = mime.lookup(type); +  if (~type.indexOf('*')) { +    type = type.split('/'); +    ct = ct.split('/'); +    if ('*' == type[0] && type[1] == ct[1]) return true; +    if ('*' == type[1] && type[0] == ct[0]) return true; +    return false; +  } +  return !! ~ct.indexOf(type); +}; + +/** + * Return the protocol string "http" or "https" + * when requested with TLS. When the "trust proxy" + * setting is enabled the "X-Forwarded-Proto" header + * field will be trusted. If you're running behind + * a reverse proxy that supplies https for you this + * may be enabled. + * + * @return {String} + * @api public + */ + +req.__defineGetter__('protocol', function(){ +  var trustProxy = this.app.get('trust proxy'); +  return this.connection.encrypted +    ? 'https' +    : trustProxy +      ? (this.get('X-Forwarded-Proto') || 'http') +      : 'http'; +}); + +/** + * Short-hand for: + * + *    req.protocol == 'https' + * + * @return {Boolean} + * @api public + */ + +req.__defineGetter__('secure', function(){ +  return 'https' == this.protocol; +}); + +/** + * Return the remote address, or when + * "trust proxy" is `true` return + * the upstream addr. + * + * @return {String} + * @api public + */ + +req.__defineGetter__('ip', function(){ +  return this.ips[0] || this.connection.remoteAddress; +}); + +/** + * When "trust proxy" is `true`, parse + * the "X-Forwarded-For" ip address list. + * + * For example if the value were "client, proxy1, proxy2" + * you would receive the array `["client", "proxy1", "proxy2"]` + * where "proxy2" is the furthest down-stream. + * + * @return {Array} + * @api public + */ + +req.__defineGetter__('ips', function(){ +  var trustProxy = this.app.get('trust proxy'); +  var val = this.get('X-Forwarded-For'); +  return trustProxy && val +    ? val.split(/ *, */) +    : []; +}); + +/** + * Return basic auth credentials. + * + * Examples: + * + *    // http://tobi:hello@example.com + *    req.auth + *    // => { username: 'tobi', password: 'hello' } + * + * @return {Object} or undefined + * @api public + */ + +req.__defineGetter__('auth', function(){ +  // missing +  var auth = this.get('Authorization'); +  if (!auth) return; + +  // malformed +  var parts = auth.split(' '); +  if ('basic' != parts[0].toLowerCase()) return; +  if (!parts[1]) return; +  auth = parts[1]; + +  // credentials +  auth = new Buffer(auth, 'base64').toString().match(/^([^:]*):(.*)$/); +  if (!auth) return; +  return { username: auth[1], password: auth[2] }; +}); + +/** + * Return subdomains as an array. + * + * Subdomains are the dot-separated parts of the host before the main domain of + * the app. By default, the domain of the app is assumed to be the last two + * parts of the host. This can be changed by setting "subdomain offset". + * + * For example, if the domain is "tobi.ferrets.example.com": + * If "subdomain offset" is not set, req.subdomains is `["ferrets", "tobi"]`. + * If "subdomain offset" is 3, req.subdomains is `["tobi"]`. + * + * @return {Array} + * @api public + */ + +req.__defineGetter__('subdomains', function(){ +  var offset = this.app.get('subdomain offset'); +  return this.get('Host') +    .split('.') +    .reverse() +    .slice(offset); +}); + +/** + * Short-hand for `url.parse(req.url).pathname`. + * + * @return {String} + * @api public + */ + +req.__defineGetter__('path', function(){ +  return parse(this).pathname; +}); + +/** + * Parse the "Host" header field hostname. + * + * @return {String} + * @api public + */ + +req.__defineGetter__('host', function(){ +  var trustProxy = this.app.get('trust proxy'); +  var host = trustProxy && this.get('X-Forwarded-Host'); +  host = host || this.get('Host'); +  return host.split(':')[0]; +}); + +/** + * Check if the request is fresh, aka + * Last-Modified and/or the ETag + * still match. + * + * @return {Boolean} + * @api public + */ + +req.__defineGetter__('fresh', function(){ +  var method = this.method; +  var s = this.res.statusCode; + +  // GET or HEAD for weak freshness validation only +  if ('GET' != method && 'HEAD' != method) return false; + +  // 2xx or 304 as per rfc2616 14.26 +  if ((s >= 200 && s < 300) || 304 == s) { +    return fresh(this.headers, this.res._headers); +  } + +  return false; +}); + +/** + * Check if the request is stale, aka + * "Last-Modified" and / or the "ETag" for the + * resource has changed. + * + * @return {Boolean} + * @api public + */ + +req.__defineGetter__('stale', function(){ +  return !this.fresh; +}); + +/** + * Check if the request was an _XMLHttpRequest_. + * + * @return {Boolean} + * @api public + */ + +req.__defineGetter__('xhr', function(){ +  var val = this.get('X-Requested-With') || ''; +  return 'xmlhttprequest' == val.toLowerCase(); +}); diff --git a/node_modules/express/lib/response.js b/node_modules/express/lib/response.js new file mode 100644 index 0000000..50fcd05 --- /dev/null +++ b/node_modules/express/lib/response.js @@ -0,0 +1,756 @@ +/** + * Module dependencies. + */ + +var http = require('http') +  , path = require('path') +  , connect = require('connect') +  , utils = connect.utils +  , sign = require('cookie-signature').sign +  , normalizeType = require('./utils').normalizeType +  , normalizeTypes = require('./utils').normalizeTypes +  , etag = require('./utils').etag +  , statusCodes = http.STATUS_CODES +  , cookie = require('cookie') +  , send = require('send') +  , mime = connect.mime +  , basename = path.basename +  , extname = path.extname +  , join = path.join; + +/** + * Response prototype. + */ + +var res = module.exports = { +  __proto__: http.ServerResponse.prototype +}; + +/** + * Set status `code`. + * + * @param {Number} code + * @return {ServerResponse} + * @api public + */ + +res.status = function(code){ +  this.statusCode = code; +  return this; +}; + +/** + * Set Link header field with the given `links`. + * + * Examples: + * + *    res.links({ + *      next: 'http://api.example.com/users?page=2', + *      last: 'http://api.example.com/users?page=5' + *    }); + * + * @param {Object} links + * @return {ServerResponse} + * @api public + */ + +res.links = function(links){ +  return this.set('Link', Object.keys(links).map(function(rel){ +    return '<' + links[rel] + '>; rel="' + rel + '"'; +  }).join(', ')); +}; + +/** + * Send a response. + * + * Examples: + * + *     res.send(new Buffer('wahoo')); + *     res.send({ some: 'json' }); + *     res.send('<p>some html</p>'); + *     res.send(404, 'Sorry, cant find that'); + *     res.send(404); + * + * @param {Mixed} body or status + * @param {Mixed} body + * @return {ServerResponse} + * @api public + */ + +res.send = function(body){ +  var req = this.req +    , head = 'HEAD' == req.method +    , len; + +  // allow status / body +  if (2 == arguments.length) { +    // res.send(body, status) backwards compat +    if ('number' != typeof body && 'number' == typeof arguments[1]) { +      this.statusCode = arguments[1]; +    } else { +      this.statusCode = body; +      body = arguments[1]; +    } +  } + +  switch (typeof body) { +    // response status +    case 'number': +      this.get('Content-Type') || this.type('txt'); +      this.statusCode = body; +      body = http.STATUS_CODES[body]; +      break; +    // string defaulting to html +    case 'string': +      if (!this.get('Content-Type')) { +        this.charset = this.charset || 'utf-8'; +        this.type('html'); +      } +      break; +    case 'boolean': +    case 'object': +      if (null == body) { +        body = ''; +      } else if (Buffer.isBuffer(body)) { +        this.get('Content-Type') || this.type('bin'); +      } else { +        return this.json(body); +      } +      break; +  } + +  // populate Content-Length +  if (undefined !== body && !this.get('Content-Length')) { +    this.set('Content-Length', len = Buffer.isBuffer(body) +      ? body.length +      : Buffer.byteLength(body)); +  } + +  // ETag support +  // TODO: W/ support +  if (len > 1024) { +    if (!this.get('ETag')) { +      this.set('ETag', etag(body)); +    } +  } + +  // freshness +  if (req.fresh) this.statusCode = 304; + +  // strip irrelevant headers +  if (204 == this.statusCode || 304 == this.statusCode) { +    this.removeHeader('Content-Type'); +    this.removeHeader('Content-Length'); +    this.removeHeader('Transfer-Encoding'); +    body = ''; +  } + +  // respond +  this.end(head ? null : body); +  return this; +}; + +/** + * Send JSON response. + * + * Examples: + * + *     res.json(null); + *     res.json({ user: 'tj' }); + *     res.json(500, 'oh noes!'); + *     res.json(404, 'I dont have that'); + * + * @param {Mixed} obj or status + * @param {Mixed} obj + * @return {ServerResponse} + * @api public + */ + +res.json = function(obj){ +  // allow status / body +  if (2 == arguments.length) { +    // res.json(body, status) backwards compat +    if ('number' == typeof arguments[1]) { +      this.statusCode = arguments[1]; +    } else { +      this.statusCode = obj; +      obj = arguments[1]; +    } +  } + +  // settings +  var app = this.app; +  var replacer = app.get('json replacer'); +  var spaces = app.get('json spaces'); +  var body = JSON.stringify(obj, replacer, spaces); + +  // content-type +  this.charset = this.charset || 'utf-8'; +  this.get('Content-Type') || this.set('Content-Type', 'application/json'); + +  return this.send(body); +}; + +/** + * Send JSON response with JSONP callback support. + * + * Examples: + * + *     res.jsonp(null); + *     res.jsonp({ user: 'tj' }); + *     res.jsonp(500, 'oh noes!'); + *     res.jsonp(404, 'I dont have that'); + * + * @param {Mixed} obj or status + * @param {Mixed} obj + * @return {ServerResponse} + * @api public + */ + +res.jsonp = function(obj){ +  // allow status / body +  if (2 == arguments.length) { +    // res.json(body, status) backwards compat +    if ('number' == typeof arguments[1]) { +      this.statusCode = arguments[1]; +    } else { +      this.statusCode = obj; +      obj = arguments[1]; +    } +  } + +  // settings +  var app = this.app; +  var replacer = app.get('json replacer'); +  var spaces = app.get('json spaces'); +  var body = JSON.stringify(obj, replacer, spaces) +    .replace(/\u2028/g, '\\u2028') +    .replace(/\u2029/g, '\\u2029'); +  var callback = this.req.query[app.get('jsonp callback name')]; + +  // content-type +  this.charset = this.charset || 'utf-8'; +  this.set('Content-Type', 'application/json'); + +  // jsonp +  if (callback) { +    this.set('Content-Type', 'text/javascript'); +    var cb = callback.replace(/[^\[\]\w$.]/g, ''); +    body = cb + ' && ' + cb + '(' + body + ');'; +  } + +  return this.send(body); +}; + +/** + * Transfer the file at the given `path`. + * + * Automatically sets the _Content-Type_ response header field. + * The callback `fn(err)` is invoked when the transfer is complete + * or when an error occurs. Be sure to check `res.sentHeader` + * if you wish to attempt responding, as the header and some data + * may have already been transferred. + * + * Options: + * + *   - `maxAge` defaulting to 0 + *   - `root`   root directory for relative filenames + * + * Examples: + * + *  The following example illustrates how `res.sendfile()` may + *  be used as an alternative for the `static()` middleware for + *  dynamic situations. The code backing `res.sendfile()` is actually + *  the same code, so HTTP cache support etc is identical. + * + *     app.get('/user/:uid/photos/:file', function(req, res){ + *       var uid = req.params.uid + *         , file = req.params.file; + * + *       req.user.mayViewFilesFrom(uid, function(yes){ + *         if (yes) { + *           res.sendfile('/uploads/' + uid + '/' + file); + *         } else { + *           res.send(403, 'Sorry! you cant see that.'); + *         } + *       }); + *     }); + * + * @param {String} path + * @param {Object|Function} options or fn + * @param {Function} fn + * @api public + */ + +res.sendfile = function(path, options, fn){ +  var self = this +    , req = self.req +    , next = this.req.next +    , options = options || {} +    , done; + +  // support function as second arg +  if ('function' == typeof options) { +    fn = options; +    options = {}; +  } + +  // socket errors +  req.socket.on('error', error); + +  // errors +  function error(err) { +    if (done) return; +    done = true; + +    // clean up +    cleanup(); +    if (!self.headerSent) self.removeHeader('Content-Disposition'); + +    // callback available +    if (fn) return fn(err); + +    // list in limbo if there's no callback +    if (self.headerSent) return; + +    // delegate +    next(err); +  } + +  // streaming +  function stream() { +    if (done) return; +    cleanup(); +    if (fn) self.on('finish', fn); +  } + +  // cleanup +  function cleanup() { +    req.socket.removeListener('error', error); +  } + +  // transfer +  var file = send(req, path); +  if (options.root) file.root(options.root); +  file.maxage(options.maxAge || 0); +  file.on('error', error); +  file.on('directory', next); +  file.on('stream', stream); +  file.pipe(this); +  this.on('finish', cleanup); +}; + +/** + * Transfer the file at the given `path` as an attachment. + * + * Optionally providing an alternate attachment `filename`, + * and optional callback `fn(err)`. The callback is invoked + * when the data transfer is complete, or when an error has + * ocurred. Be sure to check `res.headerSent` if you plan to respond. + * + * This method uses `res.sendfile()`. + * + * @param {String} path + * @param {String|Function} filename or fn + * @param {Function} fn + * @api public + */ + +res.download = function(path, filename, fn){ +  // support function as second arg +  if ('function' == typeof filename) { +    fn = filename; +    filename = null; +  } + +  filename = filename || path; +  this.set('Content-Disposition', 'attachment; filename="' + basename(filename) + '"'); +  return this.sendfile(path, fn); +}; + +/** + * Set _Content-Type_ response header with `type` through `mime.lookup()` + * when it does not contain "/", or set the Content-Type to `type` otherwise. + * + * Examples: + * + *     res.type('.html'); + *     res.type('html'); + *     res.type('json'); + *     res.type('application/json'); + *     res.type('png'); + * + * @param {String} type + * @return {ServerResponse} for chaining + * @api public + */ + +res.contentType = +res.type = function(type){ +  return this.set('Content-Type', ~type.indexOf('/') +    ? type +    : mime.lookup(type)); +}; + +/** + * Respond to the Acceptable formats using an `obj` + * of mime-type callbacks. + * + * This method uses `req.accepted`, an array of + * acceptable types ordered by their quality values. + * When "Accept" is not present the _first_ callback + * is invoked, otherwise the first match is used. When + * no match is performed the server responds with + * 406 "Not Acceptable". + * + * Content-Type is set for you, however if you choose + * you may alter this within the callback using `res.type()` + * or `res.set('Content-Type', ...)`. + * + *    res.format({ + *      'text/plain': function(){ + *        res.send('hey'); + *      }, + * + *      'text/html': function(){ + *        res.send('<p>hey</p>'); + *      }, + * + *      'appliation/json': function(){ + *        res.send({ message: 'hey' }); + *      } + *    }); + * + * In addition to canonicalized MIME types you may + * also use extnames mapped to these types: + * + *    res.format({ + *      text: function(){ + *        res.send('hey'); + *      }, + * + *      html: function(){ + *        res.send('<p>hey</p>'); + *      }, + * + *      json: function(){ + *        res.send({ message: 'hey' }); + *      } + *    }); + * + * By default Express passes an `Error` + * with a `.status` of 406 to `next(err)` + * if a match is not made. If you provide + * a `.default` callback it will be invoked + * instead. + * + * @param {Object} obj + * @return {ServerResponse} for chaining + * @api public + */ + +res.format = function(obj){ +  var req = this.req +    , next = req.next; + +  var fn = obj.default; +  if (fn) delete obj.default; +  var keys = Object.keys(obj); + +  var key = req.accepts(keys); + +  this.set('Vary', 'Accept'); + +  if (key) { +    this.set('Content-Type', normalizeType(key).value); +    obj[key](req, this, next); +  } else if (fn) { +    fn(); +  } else { +    var err = new Error('Not Acceptable'); +    err.status = 406; +    err.types = normalizeTypes(keys).map(function(o){ return o.value }); +    next(err); +  } + +  return this; +}; + +/** + * Set _Content-Disposition_ header to _attachment_ with optional `filename`. + * + * @param {String} filename + * @return {ServerResponse} + * @api public + */ + +res.attachment = function(filename){ +  if (filename) this.type(extname(filename)); +  this.set('Content-Disposition', filename +    ? 'attachment; filename="' + basename(filename) + '"' +    : 'attachment'); +  return this; +}; + +/** + * Set header `field` to `val`, or pass + * an object of header fields. + * + * Examples: + * + *    res.set('Foo', ['bar', 'baz']); + *    res.set('Accept', 'application/json'); + *    res.set({ Accept: 'text/plain', 'X-API-Key': 'tobi' }); + * + * Aliased as `res.header()`. + * + * @param {String|Object|Array} field + * @param {String} val + * @return {ServerResponse} for chaining + * @api public + */ + +res.set = +res.header = function(field, val){ +  if (2 == arguments.length) { +    if (Array.isArray(val)) val = val.map(String); +    else val = String(val); +    this.setHeader(field, val); +  } else { +    for (var key in field) { +      this.set(key, field[key]); +    } +  } +  return this; +}; + +/** + * Get value for header `field`. + * + * @param {String} field + * @return {String} + * @api public + */ + +res.get = function(field){ +  return this.getHeader(field); +}; + +/** + * Clear cookie `name`. + * + * @param {String} name + * @param {Object} options + * @param {ServerResponse} for chaining + * @api public + */ + +res.clearCookie = function(name, options){ +  var opts = { expires: new Date(1), path: '/' }; +  return this.cookie(name, '', options +    ? utils.merge(opts, options) +    : opts); +}; + +/** + * Set cookie `name` to `val`, with the given `options`. + * + * Options: + * + *    - `maxAge`   max-age in milliseconds, converted to `expires` + *    - `signed`   sign the cookie + *    - `path`     defaults to "/" + * + * Examples: + * + *    // "Remember Me" for 15 minutes + *    res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly: true }); + * + *    // save as above + *    res.cookie('rememberme', '1', { maxAge: 900000, httpOnly: true }) + * + * @param {String} name + * @param {String|Object} val + * @param {Options} options + * @api public + */ + +res.cookie = function(name, val, options){ +  options = utils.merge({}, options); +  var secret = this.req.secret; +  var signed = options.signed; +  if (signed && !secret) throw new Error('connect.cookieParser("secret") required for signed cookies'); +  if ('object' == typeof val) val = 'j:' + JSON.stringify(val); +  if (signed) val = 's:' + sign(val, secret); +  if ('maxAge' in options) { +    options.expires = new Date(Date.now() + options.maxAge); +    options.maxAge /= 1000; +  } +  if (null == options.path) options.path = '/'; +  this.set('Set-Cookie', cookie.serialize(name, String(val), options)); +  return this; +}; + + +/** + * Set the location header to `url`. + * + * The given `url` can also be the name of a mapped url, for + * example by default express supports "back" which redirects + * to the _Referrer_ or _Referer_ headers or "/". + * + * Examples: + * + *    res.location('/foo/bar').; + *    res.location('http://example.com'); + *    res.location('../login'); // /blog/post/1 -> /blog/login + * + * Mounting: + * + *   When an application is mounted and `res.location()` + *   is given a path that does _not_ lead with "/" it becomes + *   relative to the mount-point. For example if the application + *   is mounted at "/blog", the following would become "/blog/login". + * + *      res.location('login'); + * + *   While the leading slash would result in a location of "/login": + * + *      res.location('/login'); + * + * @param {String} url + * @api public + */ + +res.location = function(url){ +  var app = this.app +    , req = this.req; + +  // setup redirect map +  var map = { back: req.get('Referrer') || '/' }; + +  // perform redirect +  url = map[url] || url; + +  // relative +  if (!~url.indexOf('://') && 0 != url.indexOf('//')) { +    var path + +    // relative to path +    if ('.' == url[0]) { +      path = req.originalUrl.split('?')[0] +      url =  path + ('/' == path[path.length - 1] ? '' : '/') + url; +      // relative to mount-point +    } else if ('/' != url[0]) { +      path = app.path(); +      url = path + '/' + url; +    } +  } + +  // Respond +  this.set('Location', url); +  return this; +}; + +/** + * Redirect to the given `url` with optional response `status` + * defaulting to 302. + * + * The resulting `url` is determined by `res.location()`, so + * it will play nicely with mounted apps, relative paths, + * `"back"` etc. + * + * Examples: + * + *    res.redirect('/foo/bar'); + *    res.redirect('http://example.com'); + *    res.redirect(301, 'http://example.com'); + *    res.redirect('http://example.com', 301); + *    res.redirect('../login'); // /blog/post/1 -> /blog/login + * + * @param {String} url + * @param {Number} code + * @api public + */ + +res.redirect = function(url){ +  var app = this.app +    , head = 'HEAD' == this.req.method +    , status = 302 +    , body; + +  // allow status / url +  if (2 == arguments.length) { +    if ('number' == typeof url) { +      status = url; +      url = arguments[1]; +    } else { +      status = arguments[1]; +    } +  } + +  // Set location header +  this.location(url); +  url = this.get('Location'); + +  // Support text/{plain,html} by default +  this.format({ +    text: function(){ +      body = statusCodes[status] + '. Redirecting to ' + encodeURI(url); +    }, + +    html: function(){ +      var u = utils.escape(url); +      body = '<p>' + statusCodes[status] + '. Redirecting to <a href="' + u + '">' + u + '</a></p>'; +    }, + +    default: function(){ +      body = ''; +    } +  }); + +  // Respond +  this.statusCode = status; +  this.set('Content-Length', Buffer.byteLength(body)); +  this.end(head ? null : body); +}; + +/** + * Render `view` with the given `options` and optional callback `fn`. + * When a callback function is given a response will _not_ be made + * automatically, otherwise a response of _200_ and _text/html_ is given. + * + * Options: + * + *  - `cache`     boolean hinting to the engine it should cache + *  - `filename`  filename of the view being rendered + * + * @param  {String} view + * @param  {Object|Function} options or callback function + * @param  {Function} fn + * @api public + */ + +res.render = function(view, options, fn){ +  var self = this +    , options = options || {} +    , req = this.req +    , app = req.app; + +  // support callback function as second arg +  if ('function' == typeof options) { +    fn = options, options = {}; +  } + +  // merge res.locals +  options._locals = self.locals; + +  // default callback to respond +  fn = fn || function(err, str){ +    if (err) return req.next(err); +    self.send(str); +  }; + +  // render +  app.render(view, options, fn); +}; diff --git a/node_modules/express/lib/router/index.js b/node_modules/express/lib/router/index.js new file mode 100644 index 0000000..662dc29 --- /dev/null +++ b/node_modules/express/lib/router/index.js @@ -0,0 +1,273 @@ +/** + * Module dependencies. + */ + +var Route = require('./route') +  , utils = require('../utils') +  , methods = require('methods') +  , debug = require('debug')('express:router') +  , parse = require('connect').utils.parseUrl; + +/** + * Expose `Router` constructor. + */ + +exports = module.exports = Router; + +/** + * Initialize a new `Router` with the given `options`. + * + * @param {Object} options + * @api private + */ + +function Router(options) { +  options = options || {}; +  var self = this; +  this.map = {}; +  this.params = {}; +  this._params = []; +  this.caseSensitive = options.caseSensitive; +  this.strict = options.strict; +  this.middleware = function router(req, res, next){ +    self._dispatch(req, res, next); +  }; +} + +/** + * Register a param callback `fn` for the given `name`. + * + * @param {String|Function} name + * @param {Function} fn + * @return {Router} for chaining + * @api public + */ + +Router.prototype.param = function(name, fn){ +  // param logic +  if ('function' == typeof name) { +    this._params.push(name); +    return; +  } + +  // apply param functions +  var params = this._params +    , len = params.length +    , ret; + +  for (var i = 0; i < len; ++i) { +    if (ret = params[i](name, fn)) { +      fn = ret; +    } +  } + +  // ensure we end up with a +  // middleware function +  if ('function' != typeof fn) { +    throw new Error('invalid param() call for ' + name + ', got ' + fn); +  } + +  (this.params[name] = this.params[name] || []).push(fn); +  return this; +}; + +/** + * Route dispatcher aka the route "middleware". + * + * @param {IncomingMessage} req + * @param {ServerResponse} res + * @param {Function} next + * @api private + */ + +Router.prototype._dispatch = function(req, res, next){ +  var params = this.params +    , self = this; + +  debug('dispatching %s %s (%s)', req.method, req.url, req.originalUrl); + +  // route dispatch +  (function pass(i, err){ +    var paramCallbacks +      , paramIndex = 0 +      , paramVal +      , route +      , keys +      , key; + +    // match next route +    function nextRoute(err) { +      pass(req._route_index + 1, err); +    } + +    // match route +    req.route = route = self.matchRequest(req, i); + +    // no route +    if (!route) return next(err); +    debug('matched %s %s', route.method, route.path); + +    // we have a route +    // start at param 0 +    req.params = route.params; +    keys = route.keys; +    i = 0; + +    // param callbacks +    function param(err) { +      paramIndex = 0; +      key = keys[i++]; +      paramVal = key && req.params[key.name]; +      paramCallbacks = key && params[key.name]; + +      try { +        if ('route' == err) { +          nextRoute(); +        } else if (err) { +          i = 0; +          callbacks(err); +        } else if (paramCallbacks && undefined !== paramVal) { +          paramCallback(); +        } else if (key) { +          param(); +        } else { +          i = 0; +          callbacks(); +        } +      } catch (err) { +        param(err); +      } +    }; + +    param(err); + +    // single param callbacks +    function paramCallback(err) { +      var fn = paramCallbacks[paramIndex++]; +      if (err || !fn) return param(err); +      fn(req, res, paramCallback, paramVal, key.name); +    } + +    // invoke route callbacks +    function callbacks(err) { +      var fn = route.callbacks[i++]; +      try { +        if ('route' == err) { +          nextRoute(); +        } else if (err && fn) { +          if (fn.length < 4) return callbacks(err); +          fn(err, req, res, callbacks); +        } else if (fn) { +          if (fn.length < 4) return fn(req, res, callbacks); +          callbacks(); +        } else { +          nextRoute(err); +        } +      } catch (err) { +        callbacks(err); +      } +    } +  })(0); +}; + +/** + * Attempt to match a route for `req` + * with optional starting index of `i` + * defaulting to 0. + * + * @param {IncomingMessage} req + * @param {Number} i + * @return {Route} + * @api private + */ + +Router.prototype.matchRequest = function(req, i, head){ +  var method = req.method.toLowerCase() +    , url = parse(req) +    , path = url.pathname +    , routes = this.map +    , i = i || 0 +    , route; + +  // HEAD support +  if (!head && 'head' == method) { +    route = this.matchRequest(req, i, true); +    if (route) return route; +     method = 'get'; +  } + +  // routes for this method +  if (routes = routes[method]) { + +    // matching routes +    for (var len = routes.length; i < len; ++i) { +      route = routes[i]; +      if (route.match(path)) { +        req._route_index = i; +        return route; +      } +    } +  } +}; + +/** + * Attempt to match a route for `method` + * and `url` with optional starting + * index of `i` defaulting to 0. + * + * @param {String} method + * @param {String} url + * @param {Number} i + * @return {Route} + * @api private + */ + +Router.prototype.match = function(method, url, i, head){ +  var req = { method: method, url: url }; +  return  this.matchRequest(req, i, head); +}; + +/** + * Route `method`, `path`, and one or more callbacks. + * + * @param {String} method + * @param {String} path + * @param {Function} callback... + * @return {Router} for chaining + * @api private + */ + +Router.prototype.route = function(method, path, callbacks){ +  var method = method.toLowerCase() +    , callbacks = utils.flatten([].slice.call(arguments, 2)); + +  // ensure path was given +  if (!path) throw new Error('Router#' + method + '() requires a path'); + +  // ensure all callbacks are functions +  callbacks.forEach(function(fn, i){ +    if ('function' == typeof fn) return; +    var type = {}.toString.call(fn); +    var msg = '.' + method + '() requires callback functions but got a ' + type; +    throw new Error(msg); +  }); + +  // create the route +  debug('defined %s %s', method, path); +  var route = new Route(method, path, callbacks, { +    sensitive: this.caseSensitive, +    strict: this.strict +  }); + +  // add it +  (this.map[method] = this.map[method] || []).push(route); +  return this; +}; + +methods.forEach(function(method){ +  Router.prototype[method] = function(path){ +    var args = [method].concat([].slice.call(arguments)); +    this.route.apply(this, args); +    return this; +  }; +}); diff --git a/node_modules/express/lib/router/route.js b/node_modules/express/lib/router/route.js new file mode 100644 index 0000000..c1a0b5e --- /dev/null +++ b/node_modules/express/lib/router/route.js @@ -0,0 +1,72 @@ + +/** + * Module dependencies. + */ + +var utils = require('../utils'); + +/** + * Expose `Route`. + */ + +module.exports = Route; + +/** + * Initialize `Route` with the given HTTP `method`, `path`, + * and an array of `callbacks` and `options`. + * + * Options: + * + *   - `sensitive`    enable case-sensitive routes + *   - `strict`       enable strict matching for trailing slashes + * + * @param {String} method + * @param {String} path + * @param {Array} callbacks + * @param {Object} options. + * @api private + */ + +function Route(method, path, callbacks, options) { +  options = options || {}; +  this.path = path; +  this.method = method; +  this.callbacks = callbacks; +  this.regexp = utils.pathRegexp(path +    , this.keys = [] +    , options.sensitive +    , options.strict); +} + +/** + * Check if this route matches `path`, if so + * populate `.params`. + * + * @param {String} path + * @return {Boolean} + * @api private + */ + +Route.prototype.match = function(path){ +  var keys = this.keys +    , params = this.params = [] +    , m = this.regexp.exec(path); + +  if (!m) return false; + +  for (var i = 1, len = m.length; i < len; ++i) { +    var key = keys[i - 1]; + +    var val = 'string' == typeof m[i] +      ? decodeURIComponent(m[i]) +      : m[i]; + +    if (key) { +      params[key.name] = val; +    } else { +      params.push(val); +    } +  } + +  return true; +}; diff --git a/node_modules/express/lib/utils.js b/node_modules/express/lib/utils.js new file mode 100644 index 0000000..fd245a8 --- /dev/null +++ b/node_modules/express/lib/utils.js @@ -0,0 +1,313 @@ + +/** + * Module dependencies. + */ + +var mime = require('connect').mime +  , crc32 = require('buffer-crc32'); + +/** + * toString ref. + */ + +var toString = {}.toString; + +/** + * Return ETag for `body`. + * + * @param {String|Buffer} body + * @return {String} + * @api private + */ + +exports.etag = function(body){ +  return '"' + crc32.signed(body) + '"'; +}; + +/** + * Make `locals()` bound to the given `obj`. + * + * This is used for `app.locals` and `res.locals`. + * + * @param {Object} obj + * @return {Function} + * @api private + */ + +exports.locals = function(obj){ +  function locals(obj){ +    for (var key in obj) locals[key] = obj[key]; +    return obj; +  }; + +  return locals; +}; + +/** + * Check if `path` looks absolute. + * + * @param {String} path + * @return {Boolean} + * @api private + */ + +exports.isAbsolute = function(path){ +  if ('/' == path[0]) return true; +  if (':' == path[1] && '\\' == path[2]) return true; +}; + +/** + * Flatten the given `arr`. + * + * @param {Array} arr + * @return {Array} + * @api private + */ + +exports.flatten = function(arr, ret){ +  var ret = ret || [] +    , len = arr.length; +  for (var i = 0; i < len; ++i) { +    if (Array.isArray(arr[i])) { +      exports.flatten(arr[i], ret); +    } else { +      ret.push(arr[i]); +    } +  } +  return ret; +}; + +/** + * Normalize the given `type`, for example "html" becomes "text/html". + * + * @param {String} type + * @return {Object} + * @api private + */ + +exports.normalizeType = function(type){ +  return ~type.indexOf('/') +    ? acceptParams(type) +    : { value: mime.lookup(type), params: {} }; +}; + +/** + * Normalize `types`, for example "html" becomes "text/html". + * + * @param {Array} types + * @return {Array} + * @api private + */ + +exports.normalizeTypes = function(types){ +  var ret = []; + +  for (var i = 0; i < types.length; ++i) { +    ret.push(exports.normalizeType(types[i])); +  } + +  return ret; +}; + +/** + * Return the acceptable type in `types`, if any. + * + * @param {Array} types + * @param {String} str + * @return {String} + * @api private + */ + +exports.acceptsArray = function(types, str){ +  // accept anything when Accept is not present +  if (!str) return types[0]; + +  // parse +  var accepted = exports.parseAccept(str) +    , normalized = exports.normalizeTypes(types) +    , len = accepted.length; + +  for (var i = 0; i < len; ++i) { +    for (var j = 0, jlen = types.length; j < jlen; ++j) { +      if (exports.accept(normalized[j], accepted[i])) { +        return types[j]; +      } +    } +  } +}; + +/** + * Check if `type(s)` are acceptable based on + * the given `str`. + * + * @param {String|Array} type(s) + * @param {String} str + * @return {Boolean|String} + * @api private + */ + +exports.accepts = function(type, str){ +  if ('string' == typeof type) type = type.split(/ *, */); +  return exports.acceptsArray(type, str); +}; + +/** + * Check if `type` array is acceptable for `other`. + * + * @param {Object} type + * @param {Object} other + * @return {Boolean} + * @api private + */ + +exports.accept = function(type, other){ +  var t = type.value.split('/'); +  return (t[0] == other.type || '*' == other.type) +    && (t[1] == other.subtype || '*' == other.subtype) +    && paramsEqual(type.params, other.params); +}; + +/** + * Check if accept params are equal. + * + * @param {Object} a + * @param {Object} b + * @return {Boolean} + * @api private + */ + +function paramsEqual(a, b){ +  return !Object.keys(a).some(function(k) { +    return a[k] != b[k]; +  }); +} + +/** + * Parse accept `str`, returning + * an array objects containing + * `.type` and `.subtype` along + * with the values provided by + * `parseQuality()`. + * + * @param {Type} name + * @return {Type} + * @api private + */ + +exports.parseAccept = function(str){ +  return exports +    .parseParams(str) +    .map(function(obj){ +      var parts = obj.value.split('/'); +      obj.type = parts[0]; +      obj.subtype = parts[1]; +      return obj; +    }); +}; + +/** + * Parse quality `str`, returning an + * array of objects with `.value`, + * `.quality` and optional `.params` + * + * @param {String} str + * @return {Array} + * @api private + */ + +exports.parseParams = function(str){ +  return str +    .split(/ *, */) +    .map(acceptParams) +    .filter(function(obj){ +      return obj.quality; +    }) +    .sort(function(a, b){ +      if (a.quality === b.quality) { +        return a.originalIndex - b.originalIndex; +      } else { +        return b.quality - a.quality; +      } +    }); +}; + +/** + * Parse accept params `str` returning an + * object with `.value`, `.quality` and `.params`. + * also includes `.originalIndex` for stable sorting + * + * @param {String} str + * @return {Object} + * @api private + */ + +function acceptParams(str, index) { +  var parts = str.split(/ *; */); +  var ret = { value: parts[0], quality: 1, params: {}, originalIndex: index }; + +  for (var i = 1; i < parts.length; ++i) { +    var pms = parts[i].split(/ *= */); +    if ('q' == pms[0]) { +      ret.quality = parseFloat(pms[1]); +    } else { +      ret.params[pms[0]] = pms[1]; +    } +  } + +  return ret; +} + +/** + * Escape special characters in the given string of html. + * + * @param  {String} html + * @return {String} + * @api private + */ + +exports.escape = function(html) { +  return String(html) +    .replace(/&/g, '&') +    .replace(/"/g, '"') +    .replace(/</g, '<') +    .replace(/>/g, '>'); +}; + +/** + * Normalize the given path string, + * returning a regular expression. + * + * An empty array should be passed, + * which will contain the placeholder + * key names. For example "/user/:id" will + * then contain ["id"]. + * + * @param  {String|RegExp|Array} path + * @param  {Array} keys + * @param  {Boolean} sensitive + * @param  {Boolean} strict + * @return {RegExp} + * @api private + */ + +exports.pathRegexp = function(path, keys, sensitive, strict) { +  if (toString.call(path) == '[object RegExp]') return path; +  if (Array.isArray(path)) path = '(' + path.join('|') + ')'; +  path = path +    .concat(strict ? '' : '/?') +    .replace(/\/\(/g, '(?:/') +    .replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?(\*)?/g, function(_, slash, format, key, capture, optional, star){ +      keys.push({ name: key, optional: !! optional }); +      slash = slash || ''; +      return '' +        + (optional ? '' : slash) +        + '(?:' +        + (optional ? slash : '') +        + (format || '') + (capture || (format && '([^/.]+?)' || '([^/]+?)')) + ')' +        + (optional || '') +        + (star ? '(/*)?' : ''); +    }) +    .replace(/([\/.])/g, '\\$1') +    .replace(/\*/g, '(.*)'); +  return new RegExp('^' + path + '$', sensitive ? '' : 'i'); +} diff --git a/node_modules/express/lib/view.js b/node_modules/express/lib/view.js new file mode 100644 index 0000000..ae20b17 --- /dev/null +++ b/node_modules/express/lib/view.js @@ -0,0 +1,76 @@ +/** + * Module dependencies. + */ + +var path = require('path') +  , fs = require('fs') +  , utils = require('./utils') +  , dirname = path.dirname +  , basename = path.basename +  , extname = path.extname +  , exists = fs.existsSync || path.existsSync +  , join = path.join; + +/** + * Expose `View`. + */ + +module.exports = View; + +/** + * Initialize a new `View` with the given `name`. + * + * Options: + * + *   - `defaultEngine` the default template engine name + *   - `engines` template engine require() cache + *   - `root` root path for view lookup + * + * @param {String} name + * @param {Object} options + * @api private + */ + +function View(name, options) { +  options = options || {}; +  this.name = name; +  this.root = options.root; +  var engines = options.engines; +  this.defaultEngine = options.defaultEngine; +  var ext = this.ext = extname(name); +  if (!ext) name += (ext = this.ext = ('.' != this.defaultEngine[0] ? '.' : '') + this.defaultEngine); +  this.engine = engines[ext] || (engines[ext] = require(ext.slice(1)).__express); +  this.path = this.lookup(name); +} + +/** + * Lookup view by the given `path` + * + * @param {String} path + * @return {String} + * @api private + */ + +View.prototype.lookup = function(path){ +  var ext = this.ext; + +  // <path>.<engine> +  if (!utils.isAbsolute(path)) path = join(this.root, path); +  if (exists(path)) return path; + +  // <path>/index.<engine> +  path = join(dirname(path), basename(path, ext), 'index' + ext); +  if (exists(path)) return path; +}; + +/** + * Render with the given `options` and callback `fn(err, str)`. + * + * @param {Object} options + * @param {Function} fn + * @api private + */ + +View.prototype.render = function(options, fn){ +  this.engine(this.path, options, fn); +}; | 
