/*!
|
* 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
|
}
|
}
|