/node_modules/express/lib/request.js
JavaScript | 484 lines | 147 code | 51 blank | 286 comment | 40 complexity | b58c2e8e3b1a37f9291a937ea312c863 MD5 | raw file
Possible License(s): Apache-2.0, MIT
1 2/** 3 * Module dependencies. 4 */ 5 6var http = require('http') 7 , utils = require('./utils') 8 , connect = require('connect') 9 , fresh = require('fresh') 10 , parseRange = require('range-parser') 11 , parse = connect.utils.parseUrl 12 , mime = connect.mime; 13 14/** 15 * Request prototype. 16 */ 17 18var req = exports = module.exports = { 19 __proto__: http.IncomingMessage.prototype 20}; 21 22/** 23 * Return request header. 24 * 25 * The `Referrer` header field is special-cased, 26 * both `Referrer` and `Referer` are interchangeable. 27 * 28 * Examples: 29 * 30 * req.get('Content-Type'); 31 * // => "text/plain" 32 * 33 * req.get('content-type'); 34 * // => "text/plain" 35 * 36 * req.get('Something'); 37 * // => undefined 38 * 39 * Aliased as `req.header()`. 40 * 41 * @param {String} name 42 * @return {String} 43 * @api public 44 */ 45 46req.get = 47req.header = function(name){ 48 switch (name = name.toLowerCase()) { 49 case 'referer': 50 case 'referrer': 51 return this.headers.referrer 52 || this.headers.referer; 53 default: 54 return this.headers[name]; 55 } 56}; 57 58/** 59 * Check if the given `type(s)` is acceptable, returning 60 * the best match when true, otherwise `undefined`, in which 61 * case you should respond with 406 "Not Acceptable". 62 * 63 * The `type` value may be a single mime type string 64 * such as "application/json", the extension name 65 * such as "json", a comma-delimted list such as "json, html, text/plain", 66 * or an array `["json", "html", "text/plain"]`. When a list 67 * or array is given the _best_ match, if any is returned. 68 * 69 * Examples: 70 * 71 * // Accept: text/html 72 * req.accepts('html'); 73 * // => "html" 74 * 75 * // Accept: text/*, application/json 76 * req.accepts('html'); 77 * // => "html" 78 * req.accepts('text/html'); 79 * // => "text/html" 80 * req.accepts('json, text'); 81 * // => "json" 82 * req.accepts('application/json'); 83 * // => "application/json" 84 * 85 * // Accept: text/*, application/json 86 * req.accepts('image/png'); 87 * req.accepts('png'); 88 * // => undefined 89 * 90 * // Accept: text/*;q=.5, application/json 91 * req.accepts(['html', 'json']); 92 * req.accepts('html, json'); 93 * // => "json" 94 * 95 * @param {String|Array} type(s) 96 * @return {String} 97 * @api public 98 */ 99 100req.accepts = function(type){ 101 return utils.accepts(type, this.get('Accept')); 102}; 103 104/** 105 * Check if the given `charset` is acceptable, 106 * otherwise you should respond with 406 "Not Acceptable". 107 * 108 * @param {String} charset 109 * @return {Boolean} 110 * @api public 111 */ 112 113req.acceptsCharset = function(charset){ 114 var accepted = this.acceptedCharsets; 115 return accepted.length 116 ? ~accepted.indexOf(charset) 117 : true; 118}; 119 120/** 121 * Check if the given `lang` is acceptable, 122 * otherwise you should respond with 406 "Not Acceptable". 123 * 124 * @param {String} lang 125 * @return {Boolean} 126 * @api public 127 */ 128 129req.acceptsLanguage = function(lang){ 130 var accepted = this.acceptedLanguages; 131 return accepted.length 132 ? ~accepted.indexOf(lang) 133 : true; 134}; 135 136/** 137 * Parse Range header field, 138 * capping to the given `size`. 139 * 140 * Unspecified ranges such as "0-" require 141 * knowledge of your resource length. In 142 * the case of a byte range this is of course 143 * the total number of bytes. If the Range 144 * header field is not given `null` is returned, 145 * `-1` when unsatisfiable, `-2` when syntactically invalid. 146 * 147 * NOTE: remember that ranges are inclusive, so 148 * for example "Range: users=0-3" should respond 149 * with 4 users when available, not 3. 150 * 151 * @param {Number} size 152 * @return {Array} 153 * @api public 154 */ 155 156req.range = function(size){ 157 var range = this.get('Range'); 158 if (!range) return; 159 return parseRange(size, range); 160}; 161 162/** 163 * Return an array of Accepted media types 164 * ordered from highest quality to lowest. 165 * 166 * Examples: 167 * 168 * [ { value: 'application/json', 169 * quality: 1, 170 * type: 'application', 171 * subtype: 'json' }, 172 * { value: 'text/html', 173 * quality: 0.5, 174 * type: 'text', 175 * subtype: 'html' } ] 176 * 177 * @return {Array} 178 * @api public 179 */ 180 181req.__defineGetter__('accepted', function(){ 182 var accept = this.get('Accept'); 183 return accept 184 ? utils.parseAccept(accept) 185 : []; 186}); 187 188/** 189 * Return an array of Accepted languages 190 * ordered from highest quality to lowest. 191 * 192 * Examples: 193 * 194 * Accept-Language: en;q=.5, en-us 195 * ['en-us', 'en'] 196 * 197 * @return {Array} 198 * @api public 199 */ 200 201req.__defineGetter__('acceptedLanguages', function(){ 202 var accept = this.get('Accept-Language'); 203 return accept 204 ? utils 205 .parseQuality(accept) 206 .map(function(obj){ 207 return obj.value; 208 }) 209 : []; 210}); 211 212/** 213 * Return an array of Accepted charsets 214 * ordered from highest quality to lowest. 215 * 216 * Examples: 217 * 218 * Accept-Charset: iso-8859-5;q=.2, unicode-1-1;q=0.8 219 * ['unicode-1-1', 'iso-8859-5'] 220 * 221 * @return {Array} 222 * @api public 223 */ 224 225req.__defineGetter__('acceptedCharsets', function(){ 226 var accept = this.get('Accept-Charset'); 227 return accept 228 ? utils 229 .parseQuality(accept) 230 .map(function(obj){ 231 return obj.value; 232 }) 233 : []; 234}); 235 236/** 237 * Return the value of param `name` when present or `defaultValue`. 238 * 239 * - Checks route placeholders, ex: _/user/:id_ 240 * - Checks body params, ex: id=12, {"id":12} 241 * - Checks query string params, ex: ?id=12 242 * 243 * To utilize request bodies, `req.body` 244 * should be an object. This can be done by using 245 * the `connect.bodyParser()` middleware. 246 * 247 * @param {String} name 248 * @param {Mixed} defaultValue 249 * @return {String} 250 * @api public 251 */ 252 253req.param = function(name, defaultValue){ 254 var params = this.params || {}; 255 var body = this.body || {}; 256 var query = this.query || {}; 257 if (null != params[name] && params.hasOwnProperty(name)) return params[name]; 258 if (null != body[name]) return body[name]; 259 if (null != query[name]) return query[name]; 260 return defaultValue; 261}; 262 263/** 264 * Check if the incoming request contains the "Content-Type" 265 * header field, and it contains the give mime `type`. 266 * 267 * Examples: 268 * 269 * // With Content-Type: text/html; charset=utf-8 270 * req.is('html'); 271 * req.is('text/html'); 272 * req.is('text/*'); 273 * // => true 274 * 275 * // When Content-Type is application/json 276 * req.is('json'); 277 * req.is('application/json'); 278 * req.is('application/*'); 279 * // => true 280 * 281 * req.is('html'); 282 * // => false 283 * 284 * @param {String} type 285 * @return {Boolean} 286 * @api public 287 */ 288 289req.is = function(type){ 290 var ct = this.get('Content-Type'); 291 if (!ct) return false; 292 ct = ct.split(';')[0]; 293 if (!~type.indexOf('/')) type = mime.lookup(type); 294 if (~type.indexOf('*')) { 295 type = type.split('/'); 296 ct = ct.split('/'); 297 if ('*' == type[0] && type[1] == ct[1]) return true; 298 if ('*' == type[1] && type[0] == ct[0]) return true; 299 return false; 300 } 301 return !! ~ct.indexOf(type); 302}; 303 304/** 305 * Return the protocol string "http" or "https" 306 * when requested with TLS. When the "trust proxy" 307 * setting is enabled the "X-Forwarded-Proto" header 308 * field will be trusted. If you're running behind 309 * a reverse proxy that supplies https for you this 310 * may be enabled. 311 * 312 * @return {String} 313 * @api public 314 */ 315 316req.__defineGetter__('protocol', function(){ 317 var trustProxy = this.app.get('trust proxy'); 318 return this.connection.encrypted 319 ? 'https' 320 : trustProxy 321 ? (this.get('X-Forwarded-Proto') || 'http') 322 : 'http'; 323}); 324 325/** 326 * Short-hand for: 327 * 328 * req.protocol == 'https' 329 * 330 * @return {Boolean} 331 * @api public 332 */ 333 334req.__defineGetter__('secure', function(){ 335 return 'https' == this.protocol; 336}); 337 338/** 339 * Return the remote address, or when 340 * "trust proxy" is `true` return 341 * the upstream addr. 342 * 343 * @return {String} 344 * @api public 345 */ 346 347req.__defineGetter__('ip', function(){ 348 return this.ips[0] || this.connection.remoteAddress; 349}); 350 351/** 352 * When "trust proxy" is `true`, parse 353 * the "X-Forwarded-For" ip address list. 354 * 355 * For example if the value were "client, proxy1, proxy2" 356 * you would receive the array `["client", "proxy1", "proxy2"]` 357 * where "proxy2" is the furthest down-stream. 358 * 359 * @return {Array} 360 * @api public 361 */ 362 363req.__defineGetter__('ips', function(){ 364 var trustProxy = this.app.get('trust proxy'); 365 var val = this.get('X-Forwarded-For'); 366 return trustProxy && val 367 ? val.split(/ *, */) 368 : []; 369}); 370 371/** 372 * Return basic auth credentials. 373 * 374 * Examples: 375 * 376 * // http://tobi:hello@example.com 377 * req.auth 378 * // => { username: 'tobi', password: 'hello' } 379 * 380 * @return {Object} 381 * @api public 382 */ 383 384req.__defineGetter__('auth', function(){ 385 // missing 386 var auth = this.get('Authorization'); 387 if (!auth) return {}; 388 389 // malformed 390 auth = auth.split(' ')[1]; 391 if (!auth) return {}; 392 393 // credentials 394 auth = new Buffer(auth, 'base64').toString().split(':'); 395 return { username: auth[0], password: auth[1] }; 396}); 397 398/** 399 * Return subdomains as an array. 400 * 401 * For example "tobi.ferrets.example.com" 402 * would provide `["ferrets", "tobi"]`. 403 * 404 * @return {Array} 405 * @api public 406 */ 407 408req.__defineGetter__('subdomains', function(){ 409 return this.get('Host') 410 .split('.') 411 .slice(0, -2) 412 .reverse(); 413}); 414 415/** 416 * Short-hand for `url.parse(req.url).pathname`. 417 * 418 * @return {String} 419 * @api public 420 */ 421 422req.__defineGetter__('path', function(){ 423 return parse(this).pathname; 424}); 425 426/** 427 * Parse the "Host" header field hostname. 428 * 429 * @return {String} 430 * @api public 431 */ 432 433req.__defineGetter__('host', function(){ 434 return this.get('Host').split(':')[0]; 435}); 436 437/** 438 * Check if the request is fresh, aka 439 * Last-Modified and/or the ETag 440 * still match. 441 * 442 * @return {Boolean} 443 * @api public 444 */ 445 446req.__defineGetter__('fresh', function(){ 447 var method = this.method; 448 var s = this.res.statusCode; 449 450 // GET or HEAD for weak freshness validation only 451 if ('GET' != method && 'HEAD' != method) return false; 452 453 // 2xx or 304 as per rfc2616 14.26 454 if ((s >= 200 && s < 300) || 304 == s) { 455 return fresh(this.headers, this.res._headers); 456 } 457 458 return false; 459}); 460 461/** 462 * Check if the request is stale, aka 463 * "Last-Modified" and / or the "ETag" for the 464 * resource has changed. 465 * 466 * @return {Boolean} 467 * @api public 468 */ 469 470req.__defineGetter__('stale', function(){ 471 return !this.fresh; 472}); 473 474/** 475 * Check if the request was an _XMLHttpRequest_. 476 * 477 * @return {Boolean} 478 * @api public 479 */ 480 481req.__defineGetter__('xhr', function(){ 482 var val = this.get('X-Requested-With') || ''; 483 return 'xmlhttprequest' == val.toLowerCase(); 484});