You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
266 lines
5.4 KiB
266 lines
5.4 KiB
/*! |
|
* type-is |
|
* Copyright(c) 2014 Jonathan Ong |
|
* Copyright(c) 2014-2015 Douglas Christopher Wilson |
|
* MIT Licensed |
|
*/ |
|
|
|
'use strict' |
|
|
|
/** |
|
* Module dependencies. |
|
* @private |
|
*/ |
|
|
|
var typer = require('media-typer') |
|
var mime = require('mime-types') |
|
|
|
/** |
|
* Module exports. |
|
* @public |
|
*/ |
|
|
|
module.exports = typeofrequest |
|
module.exports.is = typeis |
|
module.exports.hasBody = hasbody |
|
module.exports.normalize = normalize |
|
module.exports.match = mimeMatch |
|
|
|
/** |
|
* Compare a `value` content-type with `types`. |
|
* Each `type` can be an extension like `html`, |
|
* a special shortcut like `multipart` or `urlencoded`, |
|
* or a mime type. |
|
* |
|
* If no types match, `false` is returned. |
|
* Otherwise, the first `type` that matches is returned. |
|
* |
|
* @param {String} value |
|
* @param {Array} types |
|
* @public |
|
*/ |
|
|
|
function typeis (value, types_) { |
|
var i |
|
var types = types_ |
|
|
|
// remove parameters and normalize |
|
var val = tryNormalizeType(value) |
|
|
|
// no type or invalid |
|
if (!val) { |
|
return false |
|
} |
|
|
|
// support flattened arguments |
|
if (types && !Array.isArray(types)) { |
|
types = new Array(arguments.length - 1) |
|
for (i = 0; i < types.length; i++) { |
|
types[i] = arguments[i + 1] |
|
} |
|
} |
|
|
|
// no types, return the content type |
|
if (!types || !types.length) { |
|
return val |
|
} |
|
|
|
var type |
|
for (i = 0; i < types.length; i++) { |
|
if (mimeMatch(normalize(type = types[i]), val)) { |
|
return type[0] === '+' || type.indexOf('*') !== -1 |
|
? val |
|
: type |
|
} |
|
} |
|
|
|
// no matches |
|
return false |
|
} |
|
|
|
/** |
|
* Check if a request has a request body. |
|
* A request with a body __must__ either have `transfer-encoding` |
|
* or `content-length` headers set. |
|
* http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.3 |
|
* |
|
* @param {Object} request |
|
* @return {Boolean} |
|
* @public |
|
*/ |
|
|
|
function hasbody (req) { |
|
return req.headers['transfer-encoding'] !== undefined || |
|
!isNaN(req.headers['content-length']) |
|
} |
|
|
|
/** |
|
* Check if the incoming request contains the "Content-Type" |
|
* header field, and it contains any of the give mime `type`s. |
|
* If there is no request body, `null` is returned. |
|
* If there is no content type, `false` is returned. |
|
* Otherwise, it returns the first `type` that matches. |
|
* |
|
* Examples: |
|
* |
|
* // With Content-Type: text/html; charset=utf-8 |
|
* this.is('html'); // => 'html' |
|
* this.is('text/html'); // => 'text/html' |
|
* this.is('text/*', 'application/json'); // => 'text/html' |
|
* |
|
* // When Content-Type is application/json |
|
* this.is('json', 'urlencoded'); // => 'json' |
|
* this.is('application/json'); // => 'application/json' |
|
* this.is('html', 'application/*'); // => 'application/json' |
|
* |
|
* this.is('html'); // => false |
|
* |
|
* @param {String|Array} types... |
|
* @return {String|false|null} |
|
* @public |
|
*/ |
|
|
|
function typeofrequest (req, types_) { |
|
var types = types_ |
|
|
|
// no body |
|
if (!hasbody(req)) { |
|
return null |
|
} |
|
|
|
// support flattened arguments |
|
if (arguments.length > 2) { |
|
types = new Array(arguments.length - 1) |
|
for (var i = 0; i < types.length; i++) { |
|
types[i] = arguments[i + 1] |
|
} |
|
} |
|
|
|
// request content type |
|
var value = req.headers['content-type'] |
|
|
|
return typeis(value, types) |
|
} |
|
|
|
/** |
|
* Normalize a mime type. |
|
* If it's a shorthand, expand it to a valid mime type. |
|
* |
|
* In general, you probably want: |
|
* |
|
* var type = is(req, ['urlencoded', 'json', 'multipart']); |
|
* |
|
* Then use the appropriate body parsers. |
|
* These three are the most common request body types |
|
* and are thus ensured to work. |
|
* |
|
* @param {String} type |
|
* @private |
|
*/ |
|
|
|
function normalize (type) { |
|
if (typeof type !== 'string') { |
|
// invalid type |
|
return false |
|
} |
|
|
|
switch (type) { |
|
case 'urlencoded': |
|
return 'application/x-www-form-urlencoded' |
|
case 'multipart': |
|
return 'multipart/*' |
|
} |
|
|
|
if (type[0] === '+') { |
|
// "+json" -> "*/*+json" expando |
|
return '*/*' + type |
|
} |
|
|
|
return type.indexOf('/') === -1 |
|
? mime.lookup(type) |
|
: type |
|
} |
|
|
|
/** |
|
* Check if `expected` mime type |
|
* matches `actual` mime type with |
|
* wildcard and +suffix support. |
|
* |
|
* @param {String} expected |
|
* @param {String} actual |
|
* @return {Boolean} |
|
* @private |
|
*/ |
|
|
|
function mimeMatch (expected, actual) { |
|
// invalid type |
|
if (expected === false) { |
|
return false |
|
} |
|
|
|
// split types |
|
var actualParts = actual.split('/') |
|
var expectedParts = expected.split('/') |
|
|
|
// invalid format |
|
if (actualParts.length !== 2 || expectedParts.length !== 2) { |
|
return false |
|
} |
|
|
|
// validate type |
|
if (expectedParts[0] !== '*' && expectedParts[0] !== actualParts[0]) { |
|
return false |
|
} |
|
|
|
// validate suffix wildcard |
|
if (expectedParts[1].substr(0, 2) === '*+') { |
|
return expectedParts[1].length <= actualParts[1].length + 1 && |
|
expectedParts[1].substr(1) === actualParts[1].substr(1 - expectedParts[1].length) |
|
} |
|
|
|
// validate subtype |
|
if (expectedParts[1] !== '*' && expectedParts[1] !== actualParts[1]) { |
|
return false |
|
} |
|
|
|
return true |
|
} |
|
|
|
/** |
|
* Normalize a type and remove parameters. |
|
* |
|
* @param {string} value |
|
* @return {string} |
|
* @private |
|
*/ |
|
|
|
function normalizeType (value) { |
|
// parse the type |
|
var type = typer.parse(value) |
|
|
|
// remove the parameters |
|
type.parameters = undefined |
|
|
|
// reformat it |
|
return typer.format(type) |
|
} |
|
|
|
/** |
|
* Try to normalize a type and remove parameters. |
|
* |
|
* @param {string} value |
|
* @return {string} |
|
* @private |
|
*/ |
|
|
|
function tryNormalizeType (value) { |
|
if (!value) { |
|
return null |
|
} |
|
|
|
try { |
|
return normalizeType(value) |
|
} catch (err) { |
|
return null |
|
} |
|
}
|
|
|