got
Version:
Human-friendly and powerful HTTP request library for Node.js
84 lines (83 loc) • 3.09 kB
JavaScript
const splitHeaderValue = (value, separator) => {
const values = [];
let current = '';
let inQuotes = false;
let inReference = false;
let isEscaped = false;
for (const character of value) {
if (inQuotes && isEscaped) {
current += character;
isEscaped = false;
continue;
}
if (inQuotes && character === '\\') {
current += character;
isEscaped = true;
continue;
}
if (character === '"') {
inQuotes = !inQuotes;
current += character;
continue;
}
if (!inQuotes && character === '<') {
inReference = true;
current += character;
continue;
}
if (!inQuotes && character === '>') {
inReference = false;
current += character;
continue;
}
// Link headers use both quoted strings and <URI-reference> values, so raw
// splitting on `,` / `;` would break valid values containing those characters.
if (!inQuotes && !inReference && character === separator) {
values.push(current);
current = '';
continue;
}
current += character;
}
if (inQuotes || isEscaped) {
throw new Error(`Failed to parse Link header: ${value}`);
}
values.push(current);
return values;
};
export default function parseLinkHeader(link) {
const parsed = [];
const items = splitHeaderValue(link, ',');
for (const item of items) {
// https://tools.ietf.org/html/rfc5988#section-5
const [rawUriReference, ...rawLinkParameters] = splitHeaderValue(item, ';');
const trimmedUriReference = rawUriReference.trim();
// eslint-disable-next-line @typescript-eslint/prefer-string-starts-ends-with
if (trimmedUriReference[0] !== '<' || trimmedUriReference.at(-1) !== '>') {
throw new Error(`Invalid format of the Link header reference: ${trimmedUriReference}`);
}
const reference = trimmedUriReference.slice(1, -1);
const parameters = {};
if (reference.includes('<') || reference.includes('>')) {
throw new Error(`Invalid format of the Link header reference: ${trimmedUriReference}`);
}
if (rawLinkParameters.length === 0) {
throw new Error(`Unexpected end of Link header parameters: ${rawLinkParameters.join(';')}`);
}
for (const rawParameter of rawLinkParameters) {
const trimmedRawParameter = rawParameter.trim();
const center = trimmedRawParameter.indexOf('=');
if (center === -1) {
throw new Error(`Failed to parse Link header: ${link}`);
}
const name = trimmedRawParameter.slice(0, center).trim();
const value = trimmedRawParameter.slice(center + 1).trim();
parameters[name] = value;
}
parsed.push({
reference,
parameters,
});
}
return parsed;
}