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