Content-Type Header Parser
Parse media types and parameters from Content-Type headers, handling quoted strings, charset and duplicate names.
Code
export interface ContentType {
type: string;
subtype: string;
essence: string;
parameters: Record<string, string>;
}
function splitParameters(header: string): string[] {
const parts: string[] = [];
let current = "";
let quoted = false;
for (let index = 0; index < header.length; index += 1) {
const character = header[index]!;
if (character === "\\" && quoted && index + 1 < header.length) {
current += character + header[index + 1];
index += 1;
continue;
}
if (character === '"') quoted = !quoted;
if (character === ";" && !quoted) {
parts.push(current);
current = "";
continue;
}
current += character;
}
parts.push(current);
return parts;
}
export function parseContentType(header: string): ContentType {
if (typeof header !== "string" || header.trim() === "") {
throw new TypeError("header must be a non-empty string");
}
const [essencePart, ...parameterParts] = splitParameters(header);
const [type, subtype] = (essencePart ?? "").split("/");
const token = /^[a-z0-9!#$&^_.+*-]+$/i;
if (!type || !subtype || !token.test(type) || !token.test(subtype)) {
throw new SyntaxError("invalid media type: " + essencePart);
}
const parameters: Record<string, string> = {};
for (const part of parameterParts) {
const separator = part.indexOf("=");
if (separator <= 0) continue;
const name = part.slice(0, separator).trim().toLowerCase();
let value = part.slice(separator + 1).trim();
if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) {
value = value.slice(1, -1).replace(/\\(.)/g, "$1");
}
if (!(name in parameters)) parameters[name] = value;
}
return {
type: type.toLowerCase(),
subtype: subtype.toLowerCase(),
essence: type.toLowerCase() + "/" + subtype.toLowerCase(),
parameters,
};
}Report a problem with this exampleOpens a pre-filled issue on GitHub.