[Pkg-javascript-commits] [node-media-typer] 01/02: Imported Upstream version 0.2.0
Andrew Kelley
andrewrk-guest at moszumanska.debian.org
Sat Jun 28 16:15:49 UTC 2014
This is an automated email from the git hooks/post-receive script.
andrewrk-guest pushed a commit to branch master
in repository node-media-typer.
commit caee432085cf1f7e905dfd3910507063ed4a5358
Author: Andrew Kelley <superjoe30 at gmail.com>
Date: Sat Jun 28 16:13:33 2014 +0000
Imported Upstream version 0.2.0
---
.gitignore | 3 +
.npmignore | 3 +
.travis.yml | 11 +++
HISTORY.md | 16 ++++
README.md | 88 ++++++++++++++++++++
index.js | 261 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
package.json | 21 +++++
test/test.js | 213 ++++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 616 insertions(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..3cd27af
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+coverage/
+node_modules/
+npm-debug.log
diff --git a/.npmignore b/.npmignore
new file mode 100644
index 0000000..cd39b77
--- /dev/null
+++ b/.npmignore
@@ -0,0 +1,3 @@
+coverage/
+test/
+.travis.yml
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..1ff243c
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,11 @@
+language: node_js
+node_js:
+ - "0.8"
+ - "0.10"
+ - "0.11"
+matrix:
+ allow_failures:
+ - node_js: "0.11"
+ fast_finish: true
+script: "npm run-script test-travis"
+after_script: "npm install coveralls at 2.10.0 && cat ./coverage/lcov.info | coveralls"
diff --git a/HISTORY.md b/HISTORY.md
new file mode 100644
index 0000000..215cc22
--- /dev/null
+++ b/HISTORY.md
@@ -0,0 +1,16 @@
+0.2.0 / 2014-06-18
+==================
+
+ * Add `typer.format()` to format media types
+
+0.1.0 / 2014-06-17
+==================
+
+ * Accept `req` as argument to `parse`
+ * Accept `res` as argument to `parse`
+ * Parse media type with extra LWS between type and first parameter
+
+0.0.0 / 2014-06-13
+==================
+
+ * Initial implementation
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..338b7ce
--- /dev/null
+++ b/README.md
@@ -0,0 +1,88 @@
+# media-typer
+
+[![NPM version](https://badge.fury.io/js/media-typer.svg)](https://badge.fury.io/js/media-typer)
+[![Build Status](https://travis-ci.org/expressjs/media-typer.svg?branch=master)](https://travis-ci.org/expressjs/media-typer)
+[![Coverage Status](https://img.shields.io/coveralls/expressjs/media-typer.svg?branch=master)](https://coveralls.io/r/expressjs/media-typer)
+
+Simple RFC 6838 media type parser
+
+## Installation
+
+```sh
+$ npm install media-typer
+```
+
+## API
+
+```js
+var typer = require('media-typer')
+```
+
+### typer.parse(string)
+
+```js
+var obj = typer.parse('image/svg+xml; charset=utf-8')
+```
+
+Parse a media type string. This will return an object with the following
+properties (examples are shown for the string `'image/svg+xml; charset=utf-8'`):
+
+ - `type`: The type of the media type (always lower case). Example: `'image'`
+
+ - `subtype`: The subtype of the media type (always lower case). Example: `'svg'`
+
+ - `suffix`: The suffix of the media type (always lower case). Example: `'xml'`
+
+ - `parameters`: An object of the parameters in the media type (name of parameter always lower case). Example: `{charset: 'utf-8'}`
+
+### typer.parse(req)
+
+```js
+var obj = typer.parse(req)
+```
+
+Parse the `content-type` header from the given `req`. Short-cut for
+`typer.parse(req.headers['content-type'])`.
+
+### typer.parse(res)
+
+```js
+var obj = typer.parse(req)
+```
+
+Parse the `content-type` header set on the given `res`. Short-cut for
+`typer.parse(res.getHeader('content-type'))`.
+
+### typer.format(obj)
+
+```js
+var obj = typer.format({type: 'image', subtype: 'svg', suffix: 'xml'})
+```
+
+Format an object into a media type string. This will return a string of the
+mime type for the given object. For the properties of the object, see the
+documentation for `typer.parse(string)`.
+
+## License
+
+The MIT License (MIT)
+
+Copyright (c) 2014 Douglas Christopher Wilson
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/index.js b/index.js
new file mode 100644
index 0000000..cbf2e17
--- /dev/null
+++ b/index.js
@@ -0,0 +1,261 @@
+/*!
+ * media-typer
+ * Copyright(c) 2014 Douglas Christopher Wilson
+ * MIT Licensed
+ */
+
+/**
+ * RegExp to match *( ";" parameter ) in RFC 2616 sec 3.7
+ *
+ * parameter = token "=" ( token | quoted-string )
+ * token = 1*<any CHAR except CTLs or separators>
+ * separators = "(" | ")" | "<" | ">" | "@"
+ * | "," | ";" | ":" | "\" | <">
+ * | "/" | "[" | "]" | "?" | "="
+ * | "{" | "}" | SP | HT
+ * quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
+ * qdtext = <any TEXT except <">>
+ * quoted-pair = "\" CHAR
+ * CHAR = <any US-ASCII character (octets 0 - 127)>
+ * TEXT = <any OCTET except CTLs, but including LWS>
+ * LWS = [CRLF] 1*( SP | HT )
+ * CRLF = CR LF
+ * CR = <US-ASCII CR, carriage return (13)>
+ * LF = <US-ASCII LF, linefeed (10)>
+ * SP = <US-ASCII SP, space (32)>
+ * SHT = <US-ASCII HT, horizontal-tab (9)>
+ * CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
+ * OCTET = <any 8-bit sequence of data>
+ */
+var paramRegExp = /; *([!#$%&'\*\+\-\.0-9A-Z\^_`a-z\|~]+) *= *("(?:[ !\u0023-\u005b\u005d-\u007e\u0080-\u00ff]|\\[\u0020-\u007e])*"|[!#$%&'\*\+\-\.0-9A-Z\^_`a-z\|~]+) */g;
+var textRegExp = /^[\u0020-\u007e\u0080-\u00ff]+$/
+var tokenRegExp = /^[!#$%&'\*\+\-\.0-9A-Z\^_`a-z\|~]+$/
+
+/**
+ * RegExp to match quoted-pair in RFC 2616
+ *
+ * quoted-pair = "\" CHAR
+ * CHAR = <any US-ASCII character (octets 0 - 127)>
+ */
+var qescRegExp = /\\([\u0000-\u007f])/g;
+
+/**
+ * RegExp to match chars that must be quoted-pair in RFC 2616
+ */
+var quoteRegExp = /([\\"])/g;
+
+/**
+ * RegExp to match type in RFC 6838
+ *
+ * type-name = restricted-name
+ * subtype-name = restricted-name
+ * restricted-name = restricted-name-first *126restricted-name-chars
+ * restricted-name-first = ALPHA / DIGIT
+ * restricted-name-chars = ALPHA / DIGIT / "!" / "#" /
+ * "$" / "&" / "-" / "^" / "_"
+ * restricted-name-chars =/ "." ; Characters before first dot always
+ * ; specify a facet name
+ * restricted-name-chars =/ "+" ; Characters after last plus always
+ * ; specify a structured syntax suffix
+ * ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
+ * DIGIT = %x30-39 ; 0-9
+ */
+var subtypeNameRegExp = /^[A-Za-z0-9][A-Za-z0-9!#$&^_.-]{0,126}$/
+var typeNameRegExp = /^[A-Za-z0-9][A-Za-z0-9!#$&^_-]{0,126}$/
+var typeRegExp = /^ *([A-Za-z0-9][A-Za-z0-9!#$&^_-]{0,126})\/([A-Za-z0-9][A-Za-z0-9!#$&^_.+-]{0,126}) *$/;
+
+/**
+ * Module exports.
+ */
+
+exports.format = format
+exports.parse = parse
+
+/**
+ * Format object to media type.
+ *
+ * @param {object} obj
+ * @return {string}
+ * @api public
+ */
+
+function format(obj) {
+ if (!obj || typeof obj !== 'object') {
+ throw new TypeError('argument obj is required')
+ }
+
+ var parameters = obj.parameters
+ var subtype = obj.subtype
+ var suffix = obj.suffix
+ var type = obj.type
+
+ if (!type || !typeNameRegExp.test(type)) {
+ throw new TypeError('invalid type')
+ }
+
+ if (!subtype || !subtypeNameRegExp.test(subtype)) {
+ throw new TypeError('invalid subtype')
+ }
+
+ // format as type/subtype
+ var string = type + '/' + subtype
+
+ // append +suffix
+ if (suffix) {
+ if (!typeNameRegExp.test(suffix)) {
+ throw new TypeError('invalid suffix')
+ }
+
+ string += '+' + suffix
+ }
+
+ // append parameters
+ if (parameters && typeof parameters === 'object') {
+ var param
+ var params = Object.keys(parameters).sort()
+
+ for (var i = 0; i < params.length; i++) {
+ param = params[i]
+
+ if (!tokenRegExp.test(param)) {
+ throw new TypeError('invalid parameter name')
+ }
+
+ string += '; ' + param + '=' + qstring(parameters[param])
+ }
+ }
+
+ return string
+}
+
+/**
+ * Parse media type to object.
+ *
+ * @param {string|object} string
+ * @return {Object}
+ * @api public
+ */
+
+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)
+ }
+
+ if (typeof string !== 'string') {
+ throw new TypeError('argument string is required to be a string')
+ }
+
+ var index = string.indexOf(';')
+ var type = index !== -1
+ ? string.substr(0, index)
+ : string
+
+ var key
+ var match
+ var obj = splitType(type)
+ var params = {}
+ var value
+
+ paramRegExp.lastIndex = index
+
+ while (match = paramRegExp.exec(string)) {
+ key = match[1].toLowerCase()
+ value = match[2]
+
+ if (value[0] === '"') {
+ // remove quotes and escapes
+ value = value
+ .substr(1, value.length - 2)
+ .replace(qescRegExp, '$1')
+ }
+
+ params[key] = value
+ }
+
+ obj.parameters = params
+
+ return obj
+}
+
+/**
+ * Get content-type from req/res objects.
+ *
+ * @param {object}
+ * @return {Object}
+ * @api private
+ */
+
+function getcontenttype(obj) {
+ if (typeof obj.getHeader === 'function') {
+ // res-like
+ return obj.getHeader('content-type')
+ }
+
+ if (typeof obj.headers === 'object') {
+ // req-like
+ return obj.headers && obj.headers['content-type']
+ }
+}
+
+/**
+ * Quote a string if necessary.
+ *
+ * @param {string} val
+ * @return {string}
+ * @api private
+ */
+
+function qstring(val) {
+ var str = String(val)
+
+ // no need to quote tokens
+ if (tokenRegExp.test(str)) {
+ return str
+ }
+
+ if (str.length > 0 && !textRegExp.test(str)) {
+ throw new TypeError('invalid parameter value')
+ }
+
+ return '"' + str.replace(quoteRegExp, '\\$1') + '"'
+}
+
+/**
+ * Simply "type/subtype+siffx" into parts.
+ *
+ * @param {string} string
+ * @return {Object}
+ * @api private
+ */
+
+function splitType(string) {
+ var match = typeRegExp.exec(string.toLowerCase())
+
+ if (!match) {
+ throw new TypeError('invalid media type')
+ }
+
+ var type = match[1]
+ var subtype = match[2]
+ var suffix
+
+ // suffix after last +
+ var index = subtype.lastIndexOf('+')
+ if (index !== -1) {
+ suffix = subtype.substr(index + 1)
+ subtype = subtype.substr(0, index)
+ }
+
+ var obj = {
+ type: type,
+ subtype: subtype,
+ suffix: suffix
+ }
+
+ return obj
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..be2b2f0
--- /dev/null
+++ b/package.json
@@ -0,0 +1,21 @@
+{
+ "name": "media-typer",
+ "description": "Simple RFC 6838 media type parser and formatter",
+ "version": "0.2.0",
+ "author": "Douglas Christopher Wilson <doug at somethingdoug.com>",
+ "license": "MIT",
+ "repository": "expressjs/media-typer",
+ "devDependencies": {
+ "istanbul": "0.2.10",
+ "mocha": "~1.20.1",
+ "should": "~4.0.4"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ },
+ "scripts": {
+ "test": "mocha --reporter dot test/",
+ "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot test/",
+ "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec test/"
+ }
+}
diff --git a/test/test.js b/test/test.js
new file mode 100644
index 0000000..7126eb0
--- /dev/null
+++ b/test/test.js
@@ -0,0 +1,213 @@
+
+var typer = require('..')
+var should = require('should')
+
+var invalidTypes = [
+ ' ',
+ 'null',
+ 'undefined',
+ '/',
+ 'text/;plain',
+ 'text/"plain"',
+ 'text/p£ain',
+ 'text/(plain)',
+ 'text/@plain',
+ 'text/plain,wrong',
+]
+
+describe('typer.format(obj)', function () {
+ it('should format basic type', function () {
+ var str = typer.format({type: 'text', subtype: 'html'})
+ str.should.equal('text/html')
+ })
+
+ it('should format type with suffix', function () {
+ var str = typer.format({type: 'image', subtype: 'svg', suffix: 'xml'})
+ str.should.equal('image/svg+xml')
+ })
+
+ it('should format type with parameter', function () {
+ var str = typer.format({
+ type: 'text',
+ subtype: 'html',
+ parameters: {charset: 'utf-8'}
+ })
+ str.should.equal('text/html; charset=utf-8')
+ })
+
+ it('should format type with parameter that needs quotes', function () {
+ var str = typer.format({
+ type: 'text',
+ subtype: 'html',
+ parameters: {foo: 'bar or "baz"'}
+ })
+ str.should.equal('text/html; foo="bar or \\"baz\\""')
+ })
+
+ it('should format type with parameter with empty value', function () {
+ var str = typer.format({
+ type: 'text',
+ subtype: 'html',
+ parameters: {foo: ''}
+ })
+ str.should.equal('text/html; foo=""')
+ })
+
+ it('should format type with multiple parameters', function () {
+ var str = typer.format({
+ type: 'text',
+ subtype: 'html',
+ parameters: {charset: 'utf-8', foo: 'bar', bar: 'baz'}
+ })
+ str.should.equal('text/html; bar=baz; charset=utf-8; foo=bar')
+ })
+
+ it('should require argument', function () {
+ typer.format.should.throw(/obj.*required/)
+ })
+
+ it('should reject non-objects', function () {
+ typer.format.bind(null, 7).should.throw(/obj.*required/)
+ })
+
+ it('should require type', function () {
+ typer.format.bind(null, {}).should.throw(/invalid type/)
+ })
+
+ it('should reject invalid type', function () {
+ typer.format.bind(null, {type: 'text/'}).should.throw(/invalid type/)
+ })
+
+ it('should require subtype', function () {
+ typer.format.bind(null, {type: 'text'}).should.throw(/invalid subtype/)
+ })
+
+ it('should reject invalid subtype', function () {
+ typer.format.bind(null, {type: 'text', subtype: 'html/'}).should.throw(/invalid subtype/)
+ })
+
+ it('should reject invalid suffix', function () {
+ var obj = {type: 'image', subtype: 'svg', suffix: 'xml\\'}
+ typer.format.bind(null, obj).should.throw(/invalid suffix/)
+ })
+
+ it('should reject invalid parameter name', function () {
+ var obj = {type: 'image', subtype: 'svg', parameters: {'foo/': 'bar'}}
+ typer.format.bind(null, obj).should.throw(/invalid parameter name/)
+ })
+
+ it('should reject invalid parameter value', function () {
+ var obj = {type: 'image', subtype: 'svg', parameters: {'foo': 'bar\u0000'}}
+ typer.format.bind(null, obj).should.throw(/invalid parameter value/)
+ })
+})
+
+describe('typer.parse(string)', function () {
+ it('should parse basic type', function () {
+ var type = typer.parse('text/html')
+ type.type.should.equal('text')
+ type.subtype.should.equal('html')
+ })
+
+ it('should parse with suffix', function () {
+ var type = typer.parse('image/svg+xml')
+ type.type.should.equal('image')
+ type.subtype.should.equal('svg')
+ type.suffix.should.equal('xml')
+ })
+
+ it('should parse parameters', function () {
+ var type = typer.parse('text/html; charset=utf-8; foo=bar')
+ type.type.should.equal('text')
+ type.subtype.should.equal('html')
+ type.parameters.should.have.property('charset', 'utf-8')
+ type.parameters.should.have.property('foo', 'bar')
+ })
+
+ it('should parse parameters with extra LWS', function () {
+ var type = typer.parse('text/html ; charset=utf-8 ; foo=bar')
+ type.type.should.equal('text')
+ type.subtype.should.equal('html')
+ type.parameters.should.have.property('charset', 'utf-8')
+ type.parameters.should.have.property('foo', 'bar')
+ })
+
+ it('should lower-case type', function () {
+ var type = typer.parse('IMAGE/SVG+XML')
+ type.type.should.equal('image')
+ type.subtype.should.equal('svg')
+ type.suffix.should.equal('xml')
+ })
+
+ it('should lower-case parameter names', function () {
+ var type = typer.parse('text/html; Charset=UTF-8')
+ type.parameters.should.have.property('charset', 'UTF-8')
+ })
+
+ it('should unquote parameter values', function () {
+ var type = typer.parse('text/html; charset="UTF-8"')
+ type.parameters.should.have.property('charset', 'UTF-8')
+ })
+
+ it('should unquote parameter values with escapes', function () {
+ var type = typer.parse('text/html; charset = "UT\\F-\\\\\\"8\\""')
+ type.parameters.should.have.property('charset', 'UTF-\\"8"')
+ })
+
+ it('should handle balanced quotes', function () {
+ var type = typer.parse('text/html; param="charset=\\"utf-8\\"; foo=bar"; bar=foo')
+ Object.keys(type.parameters).length.should.equal(2)
+ type.parameters.should.have.property('param', 'charset="utf-8"; foo=bar')
+ type.parameters.should.have.property('bar', 'foo')
+ })
+
+ invalidTypes.forEach(function (type) {
+ it('should throw on invalid media type ' + type, function () {
+ typer.parse.bind(null, type).should.throw(/invalid media type/)
+ })
+ })
+
+ it('should require argument', function () {
+ typer.parse.should.throw(/string.*required/)
+ })
+
+ it('should reject non-strings', function () {
+ typer.parse.bind(null, 7).should.throw(/string.*required/)
+ })
+})
+
+describe('typer.parse(req)', function () {
+ it('should parse content-type header', function () {
+ var req = {headers: {'content-type': 'text/html'}}
+ var type = typer.parse(req)
+ type.type.should.equal('text')
+ type.subtype.should.equal('html')
+ })
+
+ it('should reject objects without headers property', function () {
+ typer.parse.bind(null, {}).should.throw(/string.*required/)
+ })
+
+ it('should reject missing content-type', function () {
+ var req = {headers: {}}
+ typer.parse.bind(null, req).should.throw(/string.*required/)
+ })
+})
+
+describe('typer.parse(res)', function () {
+ it('should parse content-type header', function () {
+ var res = {getHeader: function(){ return 'text/html'}}
+ var type = typer.parse(res)
+ type.type.should.equal('text')
+ type.subtype.should.equal('html')
+ })
+
+ it('should reject objects without getHeader method', function () {
+ typer.parse.bind(null, {}).should.throw(/string.*required/)
+ })
+
+ it('should reject missing content-type', function () {
+ var res = {getHeader: function(){}}
+ typer.parse.bind(null, res).should.throw(/string.*required/)
+ })
+})
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-media-typer.git
More information about the Pkg-javascript-commits
mailing list