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