layer.js (3296B)
1 /*! 2 * express 3 * Copyright(c) 2009-2013 TJ Holowaychuk 4 * Copyright(c) 2013 Roman Shtylman 5 * Copyright(c) 2014-2015 Douglas Christopher Wilson 6 * MIT Licensed 7 */ 8 9 'use strict'; 10 11 /** 12 * Module dependencies. 13 * @private 14 */ 15 16 var pathRegexp = require('path-to-regexp'); 17 var debug = require('debug')('express:router:layer'); 18 19 /** 20 * Module variables. 21 * @private 22 */ 23 24 var hasOwnProperty = Object.prototype.hasOwnProperty; 25 26 /** 27 * Module exports. 28 * @public 29 */ 30 31 module.exports = Layer; 32 33 function Layer(path, options, fn) { 34 if (!(this instanceof Layer)) { 35 return new Layer(path, options, fn); 36 } 37 38 debug('new %o', path) 39 var opts = options || {}; 40 41 this.handle = fn; 42 this.name = fn.name || '<anonymous>'; 43 this.params = undefined; 44 this.path = undefined; 45 this.regexp = pathRegexp(path, this.keys = [], opts); 46 47 // set fast path flags 48 this.regexp.fast_star = path === '*' 49 this.regexp.fast_slash = path === '/' && opts.end === false 50 } 51 52 /** 53 * Handle the error for the layer. 54 * 55 * @param {Error} error 56 * @param {Request} req 57 * @param {Response} res 58 * @param {function} next 59 * @api private 60 */ 61 62 Layer.prototype.handle_error = function handle_error(error, req, res, next) { 63 var fn = this.handle; 64 65 if (fn.length !== 4) { 66 // not a standard error handler 67 return next(error); 68 } 69 70 try { 71 fn(error, req, res, next); 72 } catch (err) { 73 next(err); 74 } 75 }; 76 77 /** 78 * Handle the request for the layer. 79 * 80 * @param {Request} req 81 * @param {Response} res 82 * @param {function} next 83 * @api private 84 */ 85 86 Layer.prototype.handle_request = function handle(req, res, next) { 87 var fn = this.handle; 88 89 if (fn.length > 3) { 90 // not a standard request handler 91 return next(); 92 } 93 94 try { 95 fn(req, res, next); 96 } catch (err) { 97 next(err); 98 } 99 }; 100 101 /** 102 * Check if this route matches `path`, if so 103 * populate `.params`. 104 * 105 * @param {String} path 106 * @return {Boolean} 107 * @api private 108 */ 109 110 Layer.prototype.match = function match(path) { 111 var match 112 113 if (path != null) { 114 // fast path non-ending match for / (any path matches) 115 if (this.regexp.fast_slash) { 116 this.params = {} 117 this.path = '' 118 return true 119 } 120 121 // fast path for * (everything matched in a param) 122 if (this.regexp.fast_star) { 123 this.params = {'0': decode_param(path)} 124 this.path = path 125 return true 126 } 127 128 // match the path 129 match = this.regexp.exec(path) 130 } 131 132 if (!match) { 133 this.params = undefined; 134 this.path = undefined; 135 return false; 136 } 137 138 // store values 139 this.params = {}; 140 this.path = match[0] 141 142 var keys = this.keys; 143 var params = this.params; 144 145 for (var i = 1; i < match.length; i++) { 146 var key = keys[i - 1]; 147 var prop = key.name; 148 var val = decode_param(match[i]) 149 150 if (val !== undefined || !(hasOwnProperty.call(params, prop))) { 151 params[prop] = val; 152 } 153 } 154 155 return true; 156 }; 157 158 /** 159 * Decode param value. 160 * 161 * @param {string} val 162 * @return {string} 163 * @private 164 */ 165 166 function decode_param(val) { 167 if (typeof val !== 'string' || val.length === 0) { 168 return val; 169 } 170 171 try { 172 return decodeURIComponent(val); 173 } catch (err) { 174 if (err instanceof URIError) { 175 err.message = 'Failed to decode param \'' + val + '\''; 176 err.status = err.statusCode = 400; 177 } 178 179 throw err; 180 } 181 }