UNPKG

got

Version:

Human-friendly and powerful HTTP request library for Node.js

82 lines (81 loc) • 3.34 kB
const splitHeaderValue = (value, separator) => { const values = []; let current = ''; let inQuotes = false; let inReference = false; let isEscaped = false; for (const character of value) { if (inQuotes) { if (isEscaped) { isEscaped = false; } else if (character === '\\') { isEscaped = true; } else if (character === '"') { inQuotes = false; } } else if (character === '"') { inQuotes = true; } else if (character === '<' || character === '>') { inReference = character === '<'; } else if (!inReference && character === separator) { // Link headers use both quoted strings and <URI-reference> values, so raw // splitting on `,` / `;` would break valid values containing those characters. values.push(current); current = ''; continue; } current += character; } if (inQuotes) { throw new Error(`Failed to parse Link header: ${value}`); } values.push(current); return values; }; export default function parseLinkHeader(link) { const parsed = []; for (const item of splitHeaderValue(link, ',')) { // HTTP list recipients ignore empty members (RFC 9110, section 5.6.1.2). if (item.trim() === '') { continue; } // 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}`); } // Optional whitespace is allowed inside the angle brackets (RFC 8288, section 3). const reference = trimmedUriReference.slice(1, -1).trim(); if (reference.includes('<') || reference.includes('>')) { throw new Error(`Invalid format of the Link header reference: ${trimmedUriReference}`); } // Parameter names can match `Object.prototype` properties, so use a null-prototype object. const parameters = Object.create(null); for (const rawParameter of rawLinkParameters) { const trimmedRawParameter = rawParameter.trim(); const center = trimmedRawParameter.indexOf('='); // The parameter value is optional. See https://www.rfc-editor.org/rfc/rfc8288#section-3 const name = center === -1 ? trimmedRawParameter : trimmedRawParameter.slice(0, center).trim(); if (name === '') { throw new Error(`Failed to parse Link header: ${link}`); } const value = center === -1 ? '' : trimmedRawParameter.slice(center + 1).trim(); const normalizedName = name.toLowerCase(); if (!Object.hasOwn(parameters, normalizedName)) { parameters[normalizedName] = value; } } parsed.push({ reference, parameters, }); } return parsed; }