/*! * 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) { try { return normalizeType(value) } catch (err) { return null } }