source: node_modules/content-type/index.js

main
Last change on this file was d24f17c, checked in by Aleksandar Panovski <apano77@…>, 15 months ago

Initial commit

  • Property mode set to 100644
File size: 4.9 KB
Line 
1/*!
2 * content-type
3 * Copyright(c) 2015 Douglas Christopher Wilson
4 * MIT Licensed
5 */
6
7'use strict'
8
9/**
10 * RegExp to match *( ";" parameter ) in RFC 7231 sec 3.1.1.1
11 *
12 * parameter = token "=" ( token / quoted-string )
13 * token = 1*tchar
14 * tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*"
15 * / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
16 * / DIGIT / ALPHA
17 * ; any VCHAR, except delimiters
18 * quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
19 * qdtext = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text
20 * obs-text = %x80-FF
21 * quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
22 */
23var PARAM_REGEXP = /; *([!#$%&'*+.^_`|~0-9A-Za-z-]+) *= *("(?:[\u000b\u0020\u0021\u0023-\u005b\u005d-\u007e\u0080-\u00ff]|\\[\u000b\u0020-\u00ff])*"|[!#$%&'*+.^_`|~0-9A-Za-z-]+) */g // eslint-disable-line no-control-regex
24var TEXT_REGEXP = /^[\u000b\u0020-\u007e\u0080-\u00ff]+$/ // eslint-disable-line no-control-regex
25var TOKEN_REGEXP = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/
26
27/**
28 * RegExp to match quoted-pair in RFC 7230 sec 3.2.6
29 *
30 * quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
31 * obs-text = %x80-FF
32 */
33var QESC_REGEXP = /\\([\u000b\u0020-\u00ff])/g // eslint-disable-line no-control-regex
34
35/**
36 * RegExp to match chars that must be quoted-pair in RFC 7230 sec 3.2.6
37 */
38var QUOTE_REGEXP = /([\\"])/g
39
40/**
41 * RegExp to match type in RFC 7231 sec 3.1.1.1
42 *
43 * media-type = type "/" subtype
44 * type = token
45 * subtype = token
46 */
47var TYPE_REGEXP = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+\/[!#$%&'*+.^_`|~0-9A-Za-z-]+$/
48
49/**
50 * Module exports.
51 * @public
52 */
53
54exports.format = format
55exports.parse = parse
56
57/**
58 * Format object to media type.
59 *
60 * @param {object} obj
61 * @return {string}
62 * @public
63 */
64
65function format (obj) {
66 if (!obj || typeof obj !== 'object') {
67 throw new TypeError('argument obj is required')
68 }
69
70 var parameters = obj.parameters
71 var type = obj.type
72
73 if (!type || !TYPE_REGEXP.test(type)) {
74 throw new TypeError('invalid type')
75 }
76
77 var string = type
78
79 // append parameters
80 if (parameters && typeof parameters === 'object') {
81 var param
82 var params = Object.keys(parameters).sort()
83
84 for (var i = 0; i < params.length; i++) {
85 param = params[i]
86
87 if (!TOKEN_REGEXP.test(param)) {
88 throw new TypeError('invalid parameter name')
89 }
90
91 string += '; ' + param + '=' + qstring(parameters[param])
92 }
93 }
94
95 return string
96}
97
98/**
99 * Parse media type to object.
100 *
101 * @param {string|object} string
102 * @return {Object}
103 * @public
104 */
105
106function parse (string) {
107 if (!string) {
108 throw new TypeError('argument string is required')
109 }
110
111 // support req/res-like objects as argument
112 var header = typeof string === 'object'
113 ? getcontenttype(string)
114 : string
115
116 if (typeof header !== 'string') {
117 throw new TypeError('argument string is required to be a string')
118 }
119
120 var index = header.indexOf(';')
121 var type = index !== -1
122 ? header.slice(0, index).trim()
123 : header.trim()
124
125 if (!TYPE_REGEXP.test(type)) {
126 throw new TypeError('invalid media type')
127 }
128
129 var obj = new ContentType(type.toLowerCase())
130
131 // parse parameters
132 if (index !== -1) {
133 var key
134 var match
135 var value
136
137 PARAM_REGEXP.lastIndex = index
138
139 while ((match = PARAM_REGEXP.exec(header))) {
140 if (match.index !== index) {
141 throw new TypeError('invalid parameter format')
142 }
143
144 index += match[0].length
145 key = match[1].toLowerCase()
146 value = match[2]
147
148 if (value.charCodeAt(0) === 0x22 /* " */) {
149 // remove quotes
150 value = value.slice(1, -1)
151
152 // remove escapes
153 if (value.indexOf('\\') !== -1) {
154 value = value.replace(QESC_REGEXP, '$1')
155 }
156 }
157
158 obj.parameters[key] = value
159 }
160
161 if (index !== header.length) {
162 throw new TypeError('invalid parameter format')
163 }
164 }
165
166 return obj
167}
168
169/**
170 * Get content-type from req/res objects.
171 *
172 * @param {object}
173 * @return {Object}
174 * @private
175 */
176
177function getcontenttype (obj) {
178 var header
179
180 if (typeof obj.getHeader === 'function') {
181 // res-like
182 header = obj.getHeader('content-type')
183 } else if (typeof obj.headers === 'object') {
184 // req-like
185 header = obj.headers && obj.headers['content-type']
186 }
187
188 if (typeof header !== 'string') {
189 throw new TypeError('content-type header is missing from object')
190 }
191
192 return header
193}
194
195/**
196 * Quote a string if necessary.
197 *
198 * @param {string} val
199 * @return {string}
200 * @private
201 */
202
203function qstring (val) {
204 var str = String(val)
205
206 // no need to quote tokens
207 if (TOKEN_REGEXP.test(str)) {
208 return str
209 }
210
211 if (str.length > 0 && !TEXT_REGEXP.test(str)) {
212 throw new TypeError('invalid parameter value')
213 }
214
215 return '"' + str.replace(QUOTE_REGEXP, '\\$1') + '"'
216}
217
218/**
219 * Class to represent a content type.
220 * @private
221 */
222function ContentType (type) {
223 this.parameters = Object.create(null)
224 this.type = type
225}
Note: See TracBrowser for help on using the repository browser.