Weak And Strong ETag Comparison

Parse entity-tag lists and evaluate If-Match and If-None-Match with the strong or weak comparison rules required by HTTP caching.

JavascriptData Fetching

Code

snippet
export function parseETagList(header) {
  if (typeof header !== "string") throw new TypeError("header must be a string");
  const tags = [];
  let index = 0;
  while (index < header.length) {
    while (index < header.length && (header[index] === "," || /\s/.test(header[index]))) {
      index += 1;
    }
    if (index >= header.length) break;
    let weak = false;
    if (header.startsWith("W/", index)) {
      weak = true;
      index += 2;
    }
    if (header[index] !== '"') throw new SyntaxError("invalid ETag at index " + index);
    index += 1;
    const start = index;
    while (index < header.length && header[index] !== '"') index += 1;
    if (index >= header.length) throw new SyntaxError("unterminated ETag");
    const opaque = header.slice(start, index);
    index += 1;
    tags.push({ weak, opaque });
  }
  return tags;
}

export function strongMatch(left, right) {
  return Boolean(left && right && !left.weak && !right.weak && left.opaque === right.opaque);
}

export function weakMatch(left, right) {
  return Boolean(left && right && left.opaque === right.opaque);
}

export function matchesIfNoneMatch(header, current) {
  if (typeof header !== "string") throw new TypeError("header must be a string");
  if (header.trim() === "*") return current != null;
  return parseETagList(header).some((tag) => weakMatch(tag, current));
}

export function matchesIfMatch(header, current) {
  if (typeof header !== "string") throw new TypeError("header must be a string");
  if (header.trim() === "*") return current != null;
  return parseETagList(header).some((tag) => strongMatch(tag, current));
}