Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Avatar for KuCalc : devops.
Download
50640 views
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
*/
23
var paramRegExp = /; *([!#$%&'\*\+\-\.\^_`\|~0-9A-Za-z]+) *= *("(?:[\u000b\u0020\u0021\u0023-\u005b\u005d-\u007e\u0080-\u00ff]|\\[\u000b\u0020-\u00ff])*"|[!#$%&'\*\+\-\.\^_`\|~0-9A-Za-z]+) */g
24
var textRegExp = /^[\u000b\u0020-\u007e\u0080-\u00ff]+$/
25
var tokenRegExp = /^[!#$%&'\*\+\-\.\^_`\|~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
*/
33
var qescRegExp = /\\([\u000b\u0020-\u00ff])/g
34
35
/**
36
* RegExp to match chars that must be quoted-pair in RFC 7230 sec 3.2.6
37
*/
38
var quoteRegExp = /([\\"])/g
39
40
/**
41
* RegExp to match type in RFC 6838
42
*
43
* media-type = type "/" subtype
44
* type = token
45
* subtype = token
46
*/
47
var typeRegExp = /^[!#$%&'\*\+\-\.\^_`\|~0-9A-Za-z]+\/[!#$%&'\*\+\-\.\^_`\|~0-9A-Za-z]+$/
48
49
/**
50
* Module exports.
51
* @public
52
*/
53
54
exports.format = format
55
exports.parse = parse
56
57
/**
58
* Format object to media type.
59
*
60
* @param {object} obj
61
* @return {string}
62
* @public
63
*/
64
65
function 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 || !typeRegExp.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 (!tokenRegExp.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
106
function parse(string) {
107
if (!string) {
108
throw new TypeError('argument string is required')
109
}
110
111
if (typeof string === 'object') {
112
// support req/res-like objects as argument
113
string = getcontenttype(string)
114
115
if (typeof string !== 'string') {
116
throw new TypeError('content-type header is missing from object');
117
}
118
}
119
120
if (typeof string !== 'string') {
121
throw new TypeError('argument string is required to be a string')
122
}
123
124
var index = string.indexOf(';')
125
var type = index !== -1
126
? string.substr(0, index).trim()
127
: string.trim()
128
129
if (!typeRegExp.test(type)) {
130
throw new TypeError('invalid media type')
131
}
132
133
var key
134
var match
135
var obj = new ContentType(type.toLowerCase())
136
var value
137
138
paramRegExp.lastIndex = index
139
140
while (match = paramRegExp.exec(string)) {
141
if (match.index !== index) {
142
throw new TypeError('invalid parameter format')
143
}
144
145
index += match[0].length
146
key = match[1].toLowerCase()
147
value = match[2]
148
149
if (value[0] === '"') {
150
// remove quotes and escapes
151
value = value
152
.substr(1, value.length - 2)
153
.replace(qescRegExp, '$1')
154
}
155
156
obj.parameters[key] = value
157
}
158
159
if (index !== -1 && index !== string.length) {
160
throw new TypeError('invalid parameter format')
161
}
162
163
return obj
164
}
165
166
/**
167
* Get content-type from req/res objects.
168
*
169
* @param {object}
170
* @return {Object}
171
* @private
172
*/
173
174
function getcontenttype(obj) {
175
if (typeof obj.getHeader === 'function') {
176
// res-like
177
return obj.getHeader('content-type')
178
}
179
180
if (typeof obj.headers === 'object') {
181
// req-like
182
return obj.headers && obj.headers['content-type']
183
}
184
}
185
186
/**
187
* Quote a string if necessary.
188
*
189
* @param {string} val
190
* @return {string}
191
* @private
192
*/
193
194
function qstring(val) {
195
var str = String(val)
196
197
// no need to quote tokens
198
if (tokenRegExp.test(str)) {
199
return str
200
}
201
202
if (str.length > 0 && !textRegExp.test(str)) {
203
throw new TypeError('invalid parameter value')
204
}
205
206
return '"' + str.replace(quoteRegExp, '\\$1') + '"'
207
}
208
209
/**
210
* Class to represent a content type.
211
* @private
212
*/
213
function ContentType(type) {
214
this.parameters = Object.create(null)
215
this.type = type
216
}
217
218