second
[josuexyz/.git] / node_modules / type-is / index.js
1 /*!
2  * type-is
3  * Copyright(c) 2014 Jonathan Ong
4  * Copyright(c) 2014-2015 Douglas Christopher Wilson
5  * MIT Licensed
6  */
7
8 'use strict'
9
10 /**
11  * Module dependencies.
12  * @private
13  */
14
15 var typer = require('media-typer')
16 var mime = require('mime-types')
17
18 /**
19  * Module exports.
20  * @public
21  */
22
23 module.exports = typeofrequest
24 module.exports.is = typeis
25 module.exports.hasBody = hasbody
26 module.exports.normalize = normalize
27 module.exports.match = mimeMatch
28
29 /**
30  * Compare a `value` content-type with `types`.
31  * Each `type` can be an extension like `html`,
32  * a special shortcut like `multipart` or `urlencoded`,
33  * or a mime type.
34  *
35  * If no types match, `false` is returned.
36  * Otherwise, the first `type` that matches is returned.
37  *
38  * @param {String} value
39  * @param {Array} types
40  * @public
41  */
42
43 function typeis (value, types_) {
44   var i
45   var types = types_
46
47   // remove parameters and normalize
48   var val = tryNormalizeType(value)
49
50   // no type or invalid
51   if (!val) {
52     return false
53   }
54
55   // support flattened arguments
56   if (types && !Array.isArray(types)) {
57     types = new Array(arguments.length - 1)
58     for (i = 0; i < types.length; i++) {
59       types[i] = arguments[i + 1]
60     }
61   }
62
63   // no types, return the content type
64   if (!types || !types.length) {
65     return val
66   }
67
68   var type
69   for (i = 0; i < types.length; i++) {
70     if (mimeMatch(normalize(type = types[i]), val)) {
71       return type[0] === '+' || type.indexOf('*') !== -1
72         ? val
73         : type
74     }
75   }
76
77   // no matches
78   return false
79 }
80
81 /**
82  * Check if a request has a request body.
83  * A request with a body __must__ either have `transfer-encoding`
84  * or `content-length` headers set.
85  * http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.3
86  *
87  * @param {Object} request
88  * @return {Boolean}
89  * @public
90  */
91
92 function hasbody (req) {
93   return req.headers['transfer-encoding'] !== undefined ||
94     !isNaN(req.headers['content-length'])
95 }
96
97 /**
98  * Check if the incoming request contains the "Content-Type"
99  * header field, and it contains any of the give mime `type`s.
100  * If there is no request body, `null` is returned.
101  * If there is no content type, `false` is returned.
102  * Otherwise, it returns the first `type` that matches.
103  *
104  * Examples:
105  *
106  *     // With Content-Type: text/html; charset=utf-8
107  *     this.is('html'); // => 'html'
108  *     this.is('text/html'); // => 'text/html'
109  *     this.is('text/*', 'application/json'); // => 'text/html'
110  *
111  *     // When Content-Type is application/json
112  *     this.is('json', 'urlencoded'); // => 'json'
113  *     this.is('application/json'); // => 'application/json'
114  *     this.is('html', 'application/*'); // => 'application/json'
115  *
116  *     this.is('html'); // => false
117  *
118  * @param {String|Array} types...
119  * @return {String|false|null}
120  * @public
121  */
122
123 function typeofrequest (req, types_) {
124   var types = types_
125
126   // no body
127   if (!hasbody(req)) {
128     return null
129   }
130
131   // support flattened arguments
132   if (arguments.length > 2) {
133     types = new Array(arguments.length - 1)
134     for (var i = 0; i < types.length; i++) {
135       types[i] = arguments[i + 1]
136     }
137   }
138
139   // request content type
140   var value = req.headers['content-type']
141
142   return typeis(value, types)
143 }
144
145 /**
146  * Normalize a mime type.
147  * If it's a shorthand, expand it to a valid mime type.
148  *
149  * In general, you probably want:
150  *
151  *   var type = is(req, ['urlencoded', 'json', 'multipart']);
152  *
153  * Then use the appropriate body parsers.
154  * These three are the most common request body types
155  * and are thus ensured to work.
156  *
157  * @param {String} type
158  * @private
159  */
160
161 function normalize (type) {
162   if (typeof type !== 'string') {
163     // invalid type
164     return false
165   }
166
167   switch (type) {
168     case 'urlencoded':
169       return 'application/x-www-form-urlencoded'
170     case 'multipart':
171       return 'multipart/*'
172   }
173
174   if (type[0] === '+') {
175     // "+json" -> "*/*+json" expando
176     return '*/*' + type
177   }
178
179   return type.indexOf('/') === -1
180     ? mime.lookup(type)
181     : type
182 }
183
184 /**
185  * Check if `expected` mime type
186  * matches `actual` mime type with
187  * wildcard and +suffix support.
188  *
189  * @param {String} expected
190  * @param {String} actual
191  * @return {Boolean}
192  * @private
193  */
194
195 function mimeMatch (expected, actual) {
196   // invalid type
197   if (expected === false) {
198     return false
199   }
200
201   // split types
202   var actualParts = actual.split('/')
203   var expectedParts = expected.split('/')
204
205   // invalid format
206   if (actualParts.length !== 2 || expectedParts.length !== 2) {
207     return false
208   }
209
210   // validate type
211   if (expectedParts[0] !== '*' && expectedParts[0] !== actualParts[0]) {
212     return false
213   }
214
215   // validate suffix wildcard
216   if (expectedParts[1].substr(0, 2) === '*+') {
217     return expectedParts[1].length <= actualParts[1].length + 1 &&
218       expectedParts[1].substr(1) === actualParts[1].substr(1 - expectedParts[1].length)
219   }
220
221   // validate subtype
222   if (expectedParts[1] !== '*' && expectedParts[1] !== actualParts[1]) {
223     return false
224   }
225
226   return true
227 }
228
229 /**
230  * Normalize a type and remove parameters.
231  *
232  * @param {string} value
233  * @return {string}
234  * @private
235  */
236
237 function normalizeType (value) {
238   // parse the type
239   var type = typer.parse(value)
240
241   // remove the parameters
242   type.parameters = undefined
243
244   // reformat it
245   return typer.format(type)
246 }
247
248 /**
249  * Try to normalize a type and remove parameters.
250  *
251  * @param {string} value
252  * @return {string}
253  * @private
254  */
255
256 function tryNormalizeType (value) {
257   if (!value) {
258     return null
259   }
260
261   try {
262     return normalizeType(value)
263   } catch (err) {
264     return null
265   }
266 }