The LM Control website. Simple yet efficient.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

request.js 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  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. 'use strict';
  9. /**
  10. * Module dependencies.
  11. * @private
  12. */
  13. var accepts = require('accepts');
  14. var deprecate = require('depd')('express');
  15. var isIP = require('net').isIP;
  16. var typeis = require('type-is');
  17. var http = require('http');
  18. var fresh = require('fresh');
  19. var parseRange = require('range-parser');
  20. var parse = require('parseurl');
  21. var proxyaddr = require('proxy-addr');
  22. /**
  23. * Request prototype.
  24. * @public
  25. */
  26. var req = Object.create(http.IncomingMessage.prototype)
  27. /**
  28. * Module exports.
  29. * @public
  30. */
  31. module.exports = req
  32. /**
  33. * Return request header.
  34. *
  35. * The `Referrer` header field is special-cased,
  36. * both `Referrer` and `Referer` are interchangeable.
  37. *
  38. * Examples:
  39. *
  40. * req.get('Content-Type');
  41. * // => "text/plain"
  42. *
  43. * req.get('content-type');
  44. * // => "text/plain"
  45. *
  46. * req.get('Something');
  47. * // => undefined
  48. *
  49. * Aliased as `req.header()`.
  50. *
  51. * @param {String} name
  52. * @return {String}
  53. * @public
  54. */
  55. req.get =
  56. req.header = function header(name) {
  57. if (!name) {
  58. throw new TypeError('name argument is required to req.get');
  59. }
  60. if (typeof name !== 'string') {
  61. throw new TypeError('name must be a string to req.get');
  62. }
  63. var lc = name.toLowerCase();
  64. switch (lc) {
  65. case 'referer':
  66. case 'referrer':
  67. return this.headers.referrer
  68. || this.headers.referer;
  69. default:
  70. return this.headers[lc];
  71. }
  72. };
  73. /**
  74. * To do: update docs.
  75. *
  76. * Check if the given `type(s)` is acceptable, returning
  77. * the best match when true, otherwise `undefined`, in which
  78. * case you should respond with 406 "Not Acceptable".
  79. *
  80. * The `type` value may be a single MIME type string
  81. * such as "application/json", an extension name
  82. * such as "json", a comma-delimited list such as "json, html, text/plain",
  83. * an argument list such as `"json", "html", "text/plain"`,
  84. * or an array `["json", "html", "text/plain"]`. When a list
  85. * or array is given, the _best_ match, if any is returned.
  86. *
  87. * Examples:
  88. *
  89. * // Accept: text/html
  90. * req.accepts('html');
  91. * // => "html"
  92. *
  93. * // Accept: text/*, application/json
  94. * req.accepts('html');
  95. * // => "html"
  96. * req.accepts('text/html');
  97. * // => "text/html"
  98. * req.accepts('json, text');
  99. * // => "json"
  100. * req.accepts('application/json');
  101. * // => "application/json"
  102. *
  103. * // Accept: text/*, application/json
  104. * req.accepts('image/png');
  105. * req.accepts('png');
  106. * // => undefined
  107. *
  108. * // Accept: text/*;q=.5, application/json
  109. * req.accepts(['html', 'json']);
  110. * req.accepts('html', 'json');
  111. * req.accepts('html, json');
  112. * // => "json"
  113. *
  114. * @param {String|Array} type(s)
  115. * @return {String|Array|Boolean}
  116. * @public
  117. */
  118. req.accepts = function(){
  119. var accept = accepts(this);
  120. return accept.types.apply(accept, arguments);
  121. };
  122. /**
  123. * Check if the given `encoding`s are accepted.
  124. *
  125. * @param {String} ...encoding
  126. * @return {String|Array}
  127. * @public
  128. */
  129. req.acceptsEncodings = function(){
  130. var accept = accepts(this);
  131. return accept.encodings.apply(accept, arguments);
  132. };
  133. req.acceptsEncoding = deprecate.function(req.acceptsEncodings,
  134. 'req.acceptsEncoding: Use acceptsEncodings instead');
  135. /**
  136. * Check if the given `charset`s are acceptable,
  137. * otherwise you should respond with 406 "Not Acceptable".
  138. *
  139. * @param {String} ...charset
  140. * @return {String|Array}
  141. * @public
  142. */
  143. req.acceptsCharsets = function(){
  144. var accept = accepts(this);
  145. return accept.charsets.apply(accept, arguments);
  146. };
  147. req.acceptsCharset = deprecate.function(req.acceptsCharsets,
  148. 'req.acceptsCharset: Use acceptsCharsets instead');
  149. /**
  150. * Check if the given `lang`s are acceptable,
  151. * otherwise you should respond with 406 "Not Acceptable".
  152. *
  153. * @param {String} ...lang
  154. * @return {String|Array}
  155. * @public
  156. */
  157. req.acceptsLanguages = function(){
  158. var accept = accepts(this);
  159. return accept.languages.apply(accept, arguments);
  160. };
  161. req.acceptsLanguage = deprecate.function(req.acceptsLanguages,
  162. 'req.acceptsLanguage: Use acceptsLanguages instead');
  163. /**
  164. * Parse Range header field, capping to the given `size`.
  165. *
  166. * Unspecified ranges such as "0-" require knowledge of your resource length. In
  167. * the case of a byte range this is of course the total number of bytes. If the
  168. * Range header field is not given `undefined` is returned, `-1` when unsatisfiable,
  169. * and `-2` when syntactically invalid.
  170. *
  171. * When ranges are returned, the array has a "type" property which is the type of
  172. * range that is required (most commonly, "bytes"). Each array element is an object
  173. * with a "start" and "end" property for the portion of the range.
  174. *
  175. * The "combine" option can be set to `true` and overlapping & adjacent ranges
  176. * will be combined into a single range.
  177. *
  178. * NOTE: remember that ranges are inclusive, so for example "Range: users=0-3"
  179. * should respond with 4 users when available, not 3.
  180. *
  181. * @param {number} size
  182. * @param {object} [options]
  183. * @param {boolean} [options.combine=false]
  184. * @return {number|array}
  185. * @public
  186. */
  187. req.range = function range(size, options) {
  188. var range = this.get('Range');
  189. if (!range) return;
  190. return parseRange(size, range, options);
  191. };
  192. /**
  193. * Return the value of param `name` when present or `defaultValue`.
  194. *
  195. * - Checks route placeholders, ex: _/user/:id_
  196. * - Checks body params, ex: id=12, {"id":12}
  197. * - Checks query string params, ex: ?id=12
  198. *
  199. * To utilize request bodies, `req.body`
  200. * should be an object. This can be done by using
  201. * the `bodyParser()` middleware.
  202. *
  203. * @param {String} name
  204. * @param {Mixed} [defaultValue]
  205. * @return {String}
  206. * @public
  207. */
  208. req.param = function param(name, defaultValue) {
  209. var params = this.params || {};
  210. var body = this.body || {};
  211. var query = this.query || {};
  212. var args = arguments.length === 1
  213. ? 'name'
  214. : 'name, default';
  215. deprecate('req.param(' + args + '): Use req.params, req.body, or req.query instead');
  216. if (null != params[name] && params.hasOwnProperty(name)) return params[name];
  217. if (null != body[name]) return body[name];
  218. if (null != query[name]) return query[name];
  219. return defaultValue;
  220. };
  221. /**
  222. * Check if the incoming request contains the "Content-Type"
  223. * header field, and it contains the given mime `type`.
  224. *
  225. * Examples:
  226. *
  227. * // With Content-Type: text/html; charset=utf-8
  228. * req.is('html');
  229. * req.is('text/html');
  230. * req.is('text/*');
  231. * // => true
  232. *
  233. * // When Content-Type is application/json
  234. * req.is('json');
  235. * req.is('application/json');
  236. * req.is('application/*');
  237. * // => true
  238. *
  239. * req.is('html');
  240. * // => false
  241. *
  242. * @param {String|Array} types...
  243. * @return {String|false|null}
  244. * @public
  245. */
  246. req.is = function is(types) {
  247. var arr = types;
  248. // support flattened arguments
  249. if (!Array.isArray(types)) {
  250. arr = new Array(arguments.length);
  251. for (var i = 0; i < arr.length; i++) {
  252. arr[i] = arguments[i];
  253. }
  254. }
  255. return typeis(this, arr);
  256. };
  257. /**
  258. * Return the protocol string "http" or "https"
  259. * when requested with TLS. When the "trust proxy"
  260. * setting trusts the socket address, the
  261. * "X-Forwarded-Proto" header field will be trusted
  262. * and used if present.
  263. *
  264. * If you're running behind a reverse proxy that
  265. * supplies https for you this may be enabled.
  266. *
  267. * @return {String}
  268. * @public
  269. */
  270. defineGetter(req, 'protocol', function protocol(){
  271. var proto = this.connection.encrypted
  272. ? 'https'
  273. : 'http';
  274. var trust = this.app.get('trust proxy fn');
  275. if (!trust(this.connection.remoteAddress, 0)) {
  276. return proto;
  277. }
  278. // Note: X-Forwarded-Proto is normally only ever a
  279. // single value, but this is to be safe.
  280. var header = this.get('X-Forwarded-Proto') || proto
  281. var index = header.indexOf(',')
  282. return index !== -1
  283. ? header.substring(0, index).trim()
  284. : header.trim()
  285. });
  286. /**
  287. * Short-hand for:
  288. *
  289. * req.protocol === 'https'
  290. *
  291. * @return {Boolean}
  292. * @public
  293. */
  294. defineGetter(req, 'secure', function secure(){
  295. return this.protocol === 'https';
  296. });
  297. /**
  298. * Return the remote address from the trusted proxy.
  299. *
  300. * The is the remote address on the socket unless
  301. * "trust proxy" is set.
  302. *
  303. * @return {String}
  304. * @public
  305. */
  306. defineGetter(req, 'ip', function ip(){
  307. var trust = this.app.get('trust proxy fn');
  308. return proxyaddr(this, trust);
  309. });
  310. /**
  311. * When "trust proxy" is set, trusted proxy addresses + client.
  312. *
  313. * For example if the value were "client, proxy1, proxy2"
  314. * you would receive the array `["client", "proxy1", "proxy2"]`
  315. * where "proxy2" is the furthest down-stream and "proxy1" and
  316. * "proxy2" were trusted.
  317. *
  318. * @return {Array}
  319. * @public
  320. */
  321. defineGetter(req, 'ips', function ips() {
  322. var trust = this.app.get('trust proxy fn');
  323. var addrs = proxyaddr.all(this, trust);
  324. // reverse the order (to farthest -> closest)
  325. // and remove socket address
  326. addrs.reverse().pop()
  327. return addrs
  328. });
  329. /**
  330. * Return subdomains as an array.
  331. *
  332. * Subdomains are the dot-separated parts of the host before the main domain of
  333. * the app. By default, the domain of the app is assumed to be the last two
  334. * parts of the host. This can be changed by setting "subdomain offset".
  335. *
  336. * For example, if the domain is "tobi.ferrets.example.com":
  337. * If "subdomain offset" is not set, req.subdomains is `["ferrets", "tobi"]`.
  338. * If "subdomain offset" is 3, req.subdomains is `["tobi"]`.
  339. *
  340. * @return {Array}
  341. * @public
  342. */
  343. defineGetter(req, 'subdomains', function subdomains() {
  344. var hostname = this.hostname;
  345. if (!hostname) return [];
  346. var offset = this.app.get('subdomain offset');
  347. var subdomains = !isIP(hostname)
  348. ? hostname.split('.').reverse()
  349. : [hostname];
  350. return subdomains.slice(offset);
  351. });
  352. /**
  353. * Short-hand for `url.parse(req.url).pathname`.
  354. *
  355. * @return {String}
  356. * @public
  357. */
  358. defineGetter(req, 'path', function path() {
  359. return parse(this).pathname;
  360. });
  361. /**
  362. * Parse the "Host" header field to a hostname.
  363. *
  364. * When the "trust proxy" setting trusts the socket
  365. * address, the "X-Forwarded-Host" header field will
  366. * be trusted.
  367. *
  368. * @return {String}
  369. * @public
  370. */
  371. defineGetter(req, 'hostname', function hostname(){
  372. var trust = this.app.get('trust proxy fn');
  373. var host = this.get('X-Forwarded-Host');
  374. if (!host || !trust(this.connection.remoteAddress, 0)) {
  375. host = this.get('Host');
  376. } else if (host.indexOf(',') !== -1) {
  377. // Note: X-Forwarded-Host is normally only ever a
  378. // single value, but this is to be safe.
  379. host = host.substring(0, host.indexOf(',')).trimRight()
  380. }
  381. if (!host) return;
  382. // IPv6 literal support
  383. var offset = host[0] === '['
  384. ? host.indexOf(']') + 1
  385. : 0;
  386. var index = host.indexOf(':', offset);
  387. return index !== -1
  388. ? host.substring(0, index)
  389. : host;
  390. });
  391. // TODO: change req.host to return host in next major
  392. defineGetter(req, 'host', deprecate.function(function host(){
  393. return this.hostname;
  394. }, 'req.host: Use req.hostname instead'));
  395. /**
  396. * Check if the request is fresh, aka
  397. * Last-Modified and/or the ETag
  398. * still match.
  399. *
  400. * @return {Boolean}
  401. * @public
  402. */
  403. defineGetter(req, 'fresh', function(){
  404. var method = this.method;
  405. var res = this.res
  406. var status = res.statusCode
  407. // GET or HEAD for weak freshness validation only
  408. if ('GET' !== method && 'HEAD' !== method) return false;
  409. // 2xx or 304 as per rfc2616 14.26
  410. if ((status >= 200 && status < 300) || 304 === status) {
  411. return fresh(this.headers, {
  412. 'etag': res.get('ETag'),
  413. 'last-modified': res.get('Last-Modified')
  414. })
  415. }
  416. return false;
  417. });
  418. /**
  419. * Check if the request is stale, aka
  420. * "Last-Modified" and / or the "ETag" for the
  421. * resource has changed.
  422. *
  423. * @return {Boolean}
  424. * @public
  425. */
  426. defineGetter(req, 'stale', function stale(){
  427. return !this.fresh;
  428. });
  429. /**
  430. * Check if the request was an _XMLHttpRequest_.
  431. *
  432. * @return {Boolean}
  433. * @public
  434. */
  435. defineGetter(req, 'xhr', function xhr(){
  436. var val = this.get('X-Requested-With') || '';
  437. return val.toLowerCase() === 'xmlhttprequest';
  438. });
  439. /**
  440. * Helper function for creating a getter on an object.
  441. *
  442. * @param {Object} obj
  443. * @param {String} name
  444. * @param {Function} getter
  445. * @private
  446. */
  447. function defineGetter(obj, name, getter) {
  448. Object.defineProperty(obj, name, {
  449. configurable: true,
  450. enumerable: true,
  451. get: getter
  452. });
  453. }