UNPKG

content-type

Version:

Create and parse HTTP Content-Type header

170 lines 5.35 kB
"use strict"; /*! * content-type * Copyright(c) 2015 Douglas Christopher Wilson * MIT Licensed */ Object.defineProperty(exports, "__esModule", { value: true }); exports.format = format; exports.parse = parse; const TEXT_REGEXP = /^[\u0009\u0020-\u007e\u0080-\u00ff]*$/; const TOKEN_REGEXP = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/; /** * RegExp to match chars that must be quoted-pair in RFC 9110 sec 5.6.4 */ const QUOTE_REGEXP = /[\\"]/g; /** * RegExp to match type in RFC 9110 sec 8.3.1 * * media-type = type "/" subtype * type = token * subtype = token */ const TYPE_REGEXP = /^[!#$%&'*+.^_`|~0-9A-Za-z-]+\/[!#$%&'*+.^_`|~0-9A-Za-z-]+$/; /** * Null object perf optimization. Faster than `Object.create(null)` and `{ __proto__: null }`. */ const NullObject = /* @__PURE__ */ (() => { const C = function () { }; C.prototype = Object.create(null); return C; })(); /** * Format an object into a `Content-Type` header. */ function format(obj) { const { type, parameters } = obj; if (!type || !TYPE_REGEXP.test(type)) { throw new TypeError(`Invalid type: ${type}`); } let result = type; if (parameters) { for (const param of Object.keys(parameters)) { if (!TOKEN_REGEXP.test(param)) { throw new TypeError(`Invalid parameter name: ${param}`); } result += `; ${param}=${qstring(parameters[param])}`; } } return result; } /** * Parse a `Content-Type` header. */ function parse(header, options) { const len = header.length; let index = skipOWS(header, 0, len); const valueStart = index; index = skipValue(header, index, len); const valueEnd = trailingOWS(header, valueStart, index); const type = header.slice(valueStart, valueEnd).toLowerCase(); const parameters = options?.parameters === false ? new NullObject() : parseParameters(header, index, len); return { type, parameters }; } const SP = 32; // " " const HTAB = 9; // "\t" const SEMI = 59; // ";" const EQ = 61; // "=" const DQUOTE = 34; // '"' const BSLASH = 92; // "\\" /** * Parses the parameters of a `Content-Type` header starting at the given index. */ function parseParameters(header, index, len) { const parameters = new NullObject(); parameter: while (index < len) { index = skipOWS(header, index + 1 /* Skip over ; */, len); const keyStart = index; while (index < len) { const code = header.charCodeAt(index); if (code === SEMI) continue parameter; if (code === EQ) { const keyEnd = trailingOWS(header, keyStart, index); const key = header.slice(keyStart, keyEnd).toLowerCase(); index = skipOWS(header, index + 1, len); if (index < len && header.charCodeAt(index) === DQUOTE) { index++; let value = ""; while (index < len) { const code = header.charCodeAt(index++); if (code === DQUOTE) { index = skipValue(header, index, len); if (parameters[key] === undefined) parameters[key] = value; break; } if (code === BSLASH && index < len) { value += header[index++]; continue; } value += String.fromCharCode(code); } continue parameter; } const valueStart = index; index = skipValue(header, index, len); if (parameters[key] === undefined) { const valueEnd = trailingOWS(header, valueStart, index); parameters[key] = header.slice(valueStart, valueEnd); } continue parameter; } index++; } } return parameters; } /** * Skip over characters until a semicolon. */ function skipValue(str, index, len) { while (index < len) { const char = str.charCodeAt(index); if (char === SEMI) break; index++; } return index; } /** * Skip optional whitespace (OWS) in an HTTP header value. * * OWS is defined in RFC 9110 sec 5.6.3 as SP (" ") or HTAB ("\t"). */ function skipOWS(header, index, len) { while (index < len) { const char = header.charCodeAt(index); if (char !== SP && char !== HTAB) break; index++; } return index; } /** * Trim optional whitespace (OWS) from the end of a substring. * * OWS is defined in RFC 9110 sec 5.6.3 as SP (" ") or HTAB ("\t"). */ function trailingOWS(header, start, end) { while (end > start) { const char = header.charCodeAt(end - 1); if (char !== SP && char !== HTAB) break; end--; } return end; } /** * Serialize a parameter value. */ function qstring(str) { if (TOKEN_REGEXP.test(str)) return str; if (TEXT_REGEXP.test(str)) return `"${str.replace(QUOTE_REGEXP, "\\$&")}"`; throw new TypeError(`Invalid parameter value: ${str}`); } //# sourceMappingURL=index.js.map