Git Product home page Git Product logo

type-is's Introduction

type-is

NPM Version NPM Downloads Node.js Version Build Status Test Coverage

Infer the content-type of a request.

Install

This is a Node.js module available through the npm registry. Installation is done using the npm install command:

$ npm install type-is

API

var http = require('http')
var typeis = require('type-is')

http.createServer(function (req, res) {
  var istext = typeis(req, ['text/*'])
  res.end('you ' + (istext ? 'sent' : 'did not send') + ' me text')
})

typeis(request, types)

Checks if the request is one of the types. If the request has no body, even if there is a Content-Type header, then null is returned. If the Content-Type header is invalid or does not matches any of the types, then false is returned. Otherwise, a string of the type that matched is returned.

The request argument is expected to be a Node.js HTTP request. The types argument is an array of type strings.

Each type in the types array can be one of the following:

  • A file extension name such as json. This name will be returned if matched.
  • A mime type such as application/json.
  • A mime type with a wildcard such as */* or */json or application/*. The full mime type will be returned if matched.
  • A suffix such as +json. This can be combined with a wildcard such as */vnd+json or application/*+json. The full mime type will be returned if matched.

Some examples to illustrate the inputs and returned value:

// req.headers.content-type = 'application/json'

typeis(req, ['json']) // => 'json'
typeis(req, ['html', 'json']) // => 'json'
typeis(req, ['application/*']) // => 'application/json'
typeis(req, ['application/json']) // => 'application/json'

typeis(req, ['html']) // => false

typeis.hasBody(request)

Returns a Boolean if the given request has a body, regardless of the Content-Type header.

Having a body has no relation to how large the body is (it may be 0 bytes). This is similar to how file existence works. If a body does exist, then this indicates that there is data to read from the Node.js request stream.

if (typeis.hasBody(req)) {
  // read the body, since there is one

  req.on('data', function (chunk) {
    // ...
  })
}

typeis.is(mediaType, types)

Checks if the mediaType is one of the types. If the mediaType is invalid or does not matches any of the types, then false is returned. Otherwise, a string of the type that matched is returned.

The mediaType argument is expected to be a media type string. The types argument is an array of type strings.

Each type in the types array can be one of the following:

  • A file extension name such as json. This name will be returned if matched.
  • A mime type such as application/json.
  • A mime type with a wildcard such as */* or */json or application/*. The full mime type will be returned if matched.
  • A suffix such as +json. This can be combined with a wildcard such as */vnd+json or application/*+json. The full mime type will be returned if matched.

Some examples to illustrate the inputs and returned value:

var mediaType = 'application/json'

typeis.is(mediaType, ['json']) // => 'json'
typeis.is(mediaType, ['html', 'json']) // => 'json'
typeis.is(mediaType, ['application/*']) // => 'application/json'
typeis.is(mediaType, ['application/json']) // => 'application/json'

typeis.is(mediaType, ['html']) // => false

typeis.match(expected, actual)

Match the type string expected with actual, taking in to account wildcards. A wildcard can only be in the type of the subtype part of a media type and only in the expected value (as actual should be the real media type to match). A suffix can still be included even with a wildcard subtype. If an input is malformed, false will be returned.

typeis.match('text/html', 'text/html') // => true
typeis.match('*/html', 'text/html') // => true
typeis.match('text/*', 'text/html') // => true
typeis.match('*/*', 'text/html') // => true
typeis.match('*/*+json', 'application/x-custom+json') // => true

typeis.normalize(type)

Normalize a type string. This works by performing the following:

  • If the type is not a string, false is returned.
  • If the string starts with + (so it is a +suffix shorthand like +json), then it is expanded to contain the complete wildcard notation of */*+suffix.
  • If the string contains a /, then it is returned as the type.
  • Else the string is assumed to be a file extension and the mapped media type is returned, or false is there is no mapping.

This includes two special mappings:

  • 'multipart' -> 'multipart/*'
  • 'urlencoded' -> 'application/x-www-form-urlencoded'

Examples

Example body parser

var express = require('express')
var typeis = require('type-is')

var app = express()

app.use(function bodyParser (req, res, next) {
  if (!typeis.hasBody(req)) {
    return next()
  }

  switch (typeis(req, ['urlencoded', 'json', 'multipart'])) {
    case 'urlencoded':
      // parse urlencoded body
      throw new Error('implement urlencoded body parsing')
    case 'json':
      // parse json body
      throw new Error('implement json body parsing')
    case 'multipart':
      // parse multipart body
      throw new Error('implement multipart body parsing')
    default:
      // 415 error code
      res.statusCode = 415
      res.end()
      break
  }
})

License

MIT

type-is's People

Contributors

a-r-d avatar afeld avatar billymoon avatar carpasse avatar dcousens avatar dougwilson avatar fishrock123 avatar henrygau avatar identifier avatar jonathanong avatar nook-scheel avatar vendethiel avatar yash-singh1 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

type-is's Issues

Invalid for 'application/vnd.api+json

I am using this configuration in express

app.use(bodyParser.json({ type: 'application/vnd.api+json' }))

and I am sending this header Content-Type: application/vnd.api+json

I am getting invalid json

It is not working at all

I have no idea why it is not working , i am using vanilla http server and superagent to make the request. Here's how it looks

    var server = http.createServer(function(req, res) {
      var type = is(req,["json"]);
      res.writeHead(200, {
        "Content-type": "text/plain"
      });
      res.end(type);
    });

    supertest(server)
      .get("/")
      .set("content-type", "application/json")
      .end(function(err, res) {
        if (err) done(err);
        expect(res.text).toBe(true);
        done();
      });

When i log is(req,["json"]) it returns null

Support for application/vnd.api+json

This is the media type JSON API uses and should be treated as JSON. Am I correct in understanding that currently this media type is not directly supported?

hasbody is incorrectly false when Content-Length: 0

According to the HTTP spec, a body is signaled simply by the inclusion of the content-length or transfer-encoding headers. This module is checking if content-length is > 0 against the wishes. It also means the check returns inconsistently between a zero-length non-chunked request and a zero-length chunk request.

Should support both SPACE and HTAB as whitespace in Content-Type

Currently, type-is fails to match the media-type of a request when the content-type header includes tabs. Minimal reproduction:

var http = require('http');
var typeis = require('type-is');
var server = http.createServer(function (req, res) {
	res.write("Content-type: " + JSON.stringify(req.headers["content-type"]) + "\n");
	res.write("typeis(req, ['json']) = " + JSON.stringify(typeis(req, ['json'])) + "\n");
	res.end();
}).listen(3000);

The script above works when the content type includes parameters, and spaces:

$ curl -X POST -H 'Content-Type: application/json; charset=utf-8' http://localhost:3000 -d '{}'
Content-type: "application/json; charset=utf-8"
typeis(req, ['json']) = "json"

But fails when it includes tabs:

$ export TAB=$'\t'
$ curl -X POST -H "Content-Type: application/json;${TAB}charset=utf-8" http://localhost:3000 -d '{}'
Content-type: "application/json;\tcharset=utf-8"
typeis(req, ['json']) = false

My understanding of RFC 9110 is that horizontal tabs should be considered valid whitespace in a content-type header, so type-is shoudn't fail in this case. According to https://www.rfc-editor.org/rfc/rfc9110#field.content-type:

The type/subtype MAY be followed by semicolon-delimited parameters (Section 5.6.6) in the form of name/value pairs.

And section 5.6.6 defines the semicolon-delimited parameters as:

  parameters      = *( OWS ";" OWS [ parameter ] )
  parameter       = parameter-name "=" parameter-value
  parameter-name  = token
  parameter-value = ( token / quoted-string )

Where OWS is defined in section 5.6.3 as:

  OWS            = *( SP / HTAB )
                 ; optional whitespace

Support for media type version request

We use vendor specific media types for all our resources and use versions to differentiate between breaking changes to that resource.

For example:

application/vnd.linn.space-configuration+json; version=1

is our current media type for application/vnd.linn.space-configuration+json and type-is correctly returns the type when +json is used. However, the versioning is lost and we have no way to differentiate between a version 2 of this resource when it becomes available.

Should support upper-case headers

According to RFC 9110, I believe headers are meant to be case-insensitive.

Field names are case-insensitive ...

Running the test suite in this project with a header of Content-Type yields errors. Would be nice to case-insensitively process those headers.

I understand most clients will lower-case headers on the way out, but we have been using this in machine-to-machine APIs that don't always get lower-cased on the way out.

Support charset checking as well?

I would like to check for the charset of a content-type, so I'm not sure if something like that would belong in this library. I feel like it should, since it just operates on content-types. Example:

var typeis = require('typeis')

typeis.charset(req, 'utf-8')
// text/html => undefined
// text/html; charset=utf-8 => utf-8
// text/html; charset=utf-16 => false

+ suffix and wildcard

Hi, I'm trying to use the *+json suffix but I can't make it work.

var typeis = require('type-is')

var mediaType = 'application/json'
typeis.is(mediaType, ['application/*'])       // 'application/json'
typeis.is(mediaType, ['application/json'])    // 'application/json'
typeis.is(mediaType, ['application/*+json'])  // false

In the main documentation is written

A suffix such as +json. This can be combined with a wildcard such as /vnd+json or application/+json. The full mime type will be returned if matched.

How the *+ suffix works? I've tryied to look at the code but I found something strange (line 215):

  // 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)
  }

Here:
expectedParts[1] = "*+json"
actualParts[1] = "json"

Now, expectedParts[1] starts with "*+" but then the first part of the return expression is false:
expectedParts[1].length is > than actualParts[1].length+1 ... should't be +2 instead (* and +)?

But even if I use +2 the second part seems wrong as well...
expectedParts[1].substr(2) not expectedParts[1].substr(1)...

What am I missing?

Issue handling multiple types in one header

stated at helapkg/hela#22

Problem is that some API service - Alipay, sending request content-type header like that

application/x-www-form-urlencoded; text/html; charset=utf-8

I dont know why they send it like that, but there's no logic for me. We've tried in different ways, but didn't work.

is(req, ['x-www-form-urlencoded', 'html'])
is(req, ['application/x-www-form-urlencoded', 'text/html'])

It's not koa-better-body implementation issue, cuz it support extendTypes option that pass things to koa this.is/this.request.is

failing to match application/json with application/json :(

Had this really strange error with my requests failing, managed to trace it down to this library:

var typeis = require('type-is')

typeis.is('application/json', ['json']) //returns: 'json'
typeis.is('application/json', ['application/json']) //returns 'application/json'
typeis.is('application/json; charset=UTF-8', ['application/json']) //returns 'application/json'
typeis.is('application/json; charset=UTF-8 application/json', ['application/json']) //returns false ?!?!?!

Apparently Chrome is sending Content-type application/json; charset=UTF-8 application/json where as Firefox sends application/json; charset=UTF-8.

In other words, all my Chrome requests are failing :(

type.indexOf is not a function

seeing this error:

EXPRESS ERROR:  TypeError: type.indexOf is not a function 
    at normalize (/app/node_modules/express/node_modules/type-is/index.js:174:15) 
    at typeis (/app/node_modules/express/node_modules/type-is/index.js:70:19) 
    at typeofrequest (/app/node_modules/express/node_modules/type-is/index.js:142:10) 
    at IncomingMessage.is (/app/node_modules/express/lib/request.js:270:10) 
    at exports.upload (/app/app/controllers/api/videos.js:54:14) 
    at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5) 
    at next (/app/node_modules/express/lib/router/route.js:131:13) 
    at exports.user.exists (/app/app/middleware/authorization.js:30:3) 
    at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5) 
    at next (/app/node_modules/express/lib/router/route.js:131:13) 
    at Route.dispatch (/app/node_modules/express/lib/router/route.js:112:3) 
    at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5) 
    at /app/node_modules/express/lib/router/index.js:277:22 
    at Function.process_params (/app/node_modules/express/lib/router/index.js:330:12) 
    at next (/app/node_modules/express/lib/router/index.js:271:10) 
    at /app/node_modules/express/lib/application.js:232:9 
    at /app/node_modules/express/lib/router/index.js:615:15 
    at next (/app/node_modules/express/lib/router/index.js:256:14) 
    at compression (/app/node_modules/compression/index.js:205:5) 
    at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5) 
    at trim_prefix (/app/node_modules/express/lib/router/index.js:312:13) 
    at /app/node_modules/express/lib/router/index.js:280:7 
    at Function.process_params (/app/node_modules/express/lib/router/index.js:330:12) 
    at next (/app/node_modules/express/lib/router/index.js:271:10) 
    at expressInit (/app/node_modules/express/lib/middleware/init.js:33:5) 
    at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5) 
    at trim_prefix (/app/node_modules/express/lib/router/index.js:312:13) 
    at /app/node_modules/express/lib/router/index.js:280:7 
    at Function.process_params (/app/node_modules/express/lib/router/index.js:330:12) 
    at next (/app/node_modules/express/lib/router/index.js:271:10) 
    at query (/app/node_modules/express/lib/middleware/query.js:49:5) 
    at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5) 
    at trim_prefix (/app/node_modules/express/lib/router/index.js:312:13) 
    at /app/node_modules/express/lib/router/index.js:280:7 

going through the source code but can't find where this would error. any ideas?

Not correct verifying application/json

Consider the next example:

var e= require("type-is")
e.is({headers:{"content-type": "application/json,application/json"}}, "application/json")
// Show false

Is showing false, and must be application/json

Nodejs: v6.50
Windows 10 x64, Ubuntu 16.04 x64

TypeError: argument string is required - error when content type is missing

This library is used by body parser in Express. If there is no "content-type" header, the library throws error at this line:

function parse(string) {
  if (!string) {
    throw new TypeError('argument string is required')
  }

  // support req/res-like objects as argument
  if (typeof string === 'object') {
    string = getcontenttype(string)
  }

This happens because you don't check if the header existed in the first place:

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
// BAD BAD BAD!!!
  var value = req.headers['content-type']

  return typeis(value, types)
}

In the above code you send undefined to the typeis function.

Full stack trace:

 	parse [index.js] Line 141	JavaScript
 	normalizeType [index.js] Line 239	JavaScript
 	tryNormalizeType [index.js] Line 258	JavaScript
 	typeis [index.js] Line 48	JavaScript
>	typeofrequest [index.js] Line 142	JavaScript

Cannot deal with the content-type with semicolon

Some websites may return the headers with content-type 'text/html;', I think it should be treated as legal html as well.

code:

let typeis = require('type-is').is;

let val = 'text/html;';
let types = ['html', 'xhtml', 'text/xml', 'application/xml', '+xml'];

console.log(typeis(val, types)); // false

typeis and html

Hello,

I encountered the following issue with your great library and I'm wondering if this can be considered as a potential enhancement of your library

Consider the following link:
http://www.npr.org/2017/06/19/532916572/maudie-paints-intimate-portrait-of-canadian-painter-maud-lewis
And all the potential links from this website

when you do
typeis(contentTypeHeader, ['html' ,'xhtml'])
it will return false.

Why?
The header returned is the following
Content-Type:text/html;;charset=UTF-8

So its looks normal header no?
As you can see there is an extra ; in the headers.
So who is wrong ? can we consider that the header is definitely malformed (it's still a big website) or that the library is not handling that case properly?

Thanks in advance for your feedback and have a great day,

Ansekme

Support RFC 6839 for matching

It would be nice if this module supported matching according to RFC 6839 (i.e. +json should match application/vnd+json).

Obscure condition checks

There is a bad bug, all my json requests do return false on req.is('json') within the expressjs v4 code :(

I debugged and think these lines do not make any sense at all:

  if (!(parseInt(headers['content-length'], 10)
    || 'transfer-encoding' in headers)) return;

In my requests I have no content-length yet and there is no transfer-encoding.

This here is my header:

{ host: 'localhost:8080',
  connection: 'keep-alive',
  'cache-control': 'no-cache',
  pragma: 'no-cache',
  'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.116 Safari/537.36',
  'content-type': 'application/json',
  accept: '*/*',
  dnt: '1',
  referer: 'https://localhost:8080/',
  'accept-encoding': 'gzip,deflate,sdch',
  'accept-language': 'en-GB,en-US;q=0.8,en;q=0.6',
  cookie: 'session=81e32809-7efe-447d-bbda-624acbbc2543; session.sig=d2gT3Yssb_ZLqYixqojzEGs53j8; _ga=GA1.1.1416672249.1394592952; language=en; express:sess=eyJwYXNzcG9ydCI6e319; express:sess.sig=bhxojHt1UQWbtKNR_ztU_-X2LLg' }

It must work with these!

bug with type-is

Hi,

The background: Alipay system is sending a POST request with header of:
content-type: application/x-www-form-urlencoded; text/html; charset=utf-8

after using bodyparser with type-is internally, there is a big issue:
current behavior

var is   = require('type-is')
var mediaType = 'application/x-www-form-urlencoded; text/html; charset=utf-8'
is.is(mediaType, ['*/*']) // returning false and rejecting my content/body

expected behavior

is.is(mediaType, ['*/*']) // should return application/x-www-form-urlencoded ??

@didayche

node example.js fails on Error

Error function fail in node

C:\Temp\generate-schema\node_modules\type-of-is>node --version
v6.9.4

C:\Temp\generate-schema\node_modules\type-of-is>node example.js
[Function: String]
[Function: Number]
[Function: Object]
[Function: Array]
null
undefined
[Function: Boolean]
[Function: Function]
[Function: RegExp]
[Function: Date]
{ [Function: Error]
captureStackTrace: [Function: captureStackTrace],
stackTraceLimit: 10 }

String
Number
Object
Array
null
undefined
Boolean
Function
RegExp
Date
Error
true
false
true
true
false
false
true
true
true
false
true
false
[Function: Person]
true
false
true
true
[Function: Object]
[Function: Number]
[Function: Number]
[Function: Number]
[Function: Object]
[Function: Object]
T
true
false

Has body check returns `true` with `Content-Length: 0`

Also with negative numbers, but 0 is the primary place I want to catch. There's no real difference between a missing content-length and content-length: 0, so I expect those would be treated the same. If this is a legitimate issue, I can open a PR. I noticed, however, that there's a test actually for the 0 handling - why?

References:

Content type with profile not matching as expected

Version 1.6.18

typeIs.is('application/ld+json; profile="https://www.w3.org/ns/activitystreams"', ['application/ld+json; profile="https://www.w3.org/ns/activitystreams"'])

Evaluates to false. It seems to use different normalization methods for value and type

Invalid result for some multipart/formdata requests

some browsers use multipart/form-data boundaries encapsulated by ===. According to spec, this is valid. however type-is fails here:

const {is} = require('type-is');

console.log(is({headers:{'content-type':'multipart/form-data; boundary====1626687228335==='}},['multipart']));
console.log(is({headers:{'content-type':'multipart/form-data; boundary=--------------------------575525763331173399582839'}},['multipart']));

expected result would be:

multipart
multipart

actual result is:

false
multipart

AdonisJS broke for the normalizeType change

Hi, adonisjs is broken because of a change in this library.

The commit change is the next one:
3c1d9b7#diff-168726dbe96b3ce427e7fedce31bb0bc

You have forced the type of the argument to be "string" but node-req, the library that is using adonisjs calls "is" with the request (object) and not with the content-type (string):

I don't know why they calls the function with the request and node-req github page dissapeared to report them

https://github.com/poppinss/node-req/issues

The method tryNormalizeType works fine with "request objects", but forcing the argument to be a string breaks node-req and adonisjs, it's possible to remove the type force until I report the issue to AdonisJS?

Thanks!

Error thrown by media-typer when Content-Type header not set

I ran into an issue when using express 4.16.2 and body-parser.urlencoded to handle a POST request with an optional body.

When the body was empty, the request had Content-Length: 0 but no Content-Type header. That caused an error "TypeError: argument string is required" which was ultimately thrown by

https://github.com/jshttp/media-typer/blob/d49d41ffd0bb5a0655fa44a59df2ec0bfc835b16/index.js#L141

but I think the root cause is

type-is/index.js

Lines 139 to 142 in 9e88be8

// request content type
var value = req.headers['content-type']
return typeis(value, types)

(or any of the methods typeis() calls) because none of them check if the Content-Type header is set.

Now, the fix was easy (just set Content-Type even for an empty body) and it might well be that the specs require that for all POST requests anyway.

So the only reason I'm opening this issue is that I wonder if it's really the intended behaviour, as the comment for typeofrequest() states

* If there is no content type, `false` is returned.

which obviously isn't true, as media-typer.parse() throws before that can happen...

ESM/browser support

I'd love to use this package in the browser, but currently it uses Node-style CommonJS. Would it be possible to support ESM through package.json fields?

remove content-length check

Firstly, thanks for all the great libraries on jshttp!

The readme states

Infer the content-type of a request.

However, because typeofrequest() is always checking content-length, the application of this package is limited. This makes sense given the primary user is body-parser, however it would be great for the package to be more generic.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.