UNPKG

@remix-run/headers

Version:

A toolkit for working with HTTP headers in JavaScript

85 lines (84 loc) 3.04 kB
import {} from "./header-value.js"; import { parseHttpDate, removeMilliseconds } from "./utils.js"; import { quoteEtag } from "./utils.js"; /** * The value of an `If-Range` HTTP header. * * The `If-Range` header can contain either an entity tag (ETag) or an HTTP date. * * [MDN `If-Range` Reference](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-Range) * * [HTTP/1.1 Specification](https://datatracker.ietf.org/doc/html/rfc7233#section-3.2) */ export class IfRange { value = ''; /** * @param init A string or Date to initialize the header */ constructor(init) { if (init) { if (typeof init === 'string') { this.value = init; } else { this.value = init.toUTCString(); } } } /** * Checks if the `If-Range` condition is satisfied for the current resource state. * * This method always returns `true` if the `If-Range` header is not present, * meaning the range request should proceed unconditionally. * * The `If-Range` header can contain either: * - An HTTP date (RFC 7231 IMF-fixdate format) * - An entity tag (ETag) * * When comparing ETags, only strong entity tags are matched as per RFC 7233. * Weak entity tags (prefixed with `W/`) are never considered a match. * * @param resource The current resource state to compare against * @return `true` if the condition is satisfied, `false` otherwise * * @example * ```ts * let ifRange = new IfRange('Wed, 21 Oct 2015 07:28:00 GMT') * ifRange.matches({ lastModified: 1445412480000 }) // true if dates match * ifRange.matches({ lastModified: new Date('2015-10-21T07:28:00Z') }) // true * * let ifRange2 = new IfRange('"abc123"') * ifRange2.matches({ etag: '"abc123"' }) // true * ifRange2.matches({ etag: 'W/"abc123"' }) // false (weak ETag) * ``` */ matches(resource) { if (!this.value) { return true; } // Try parsing as HTTP date first let dateTimestamp = parseHttpDate(this.value); if (dateTimestamp !== null && resource.lastModified != null) { return removeMilliseconds(dateTimestamp) === removeMilliseconds(resource.lastModified); } // Otherwise treat as ETag if (resource.etag != null) { let normalizedTag = quoteEtag(this.value); let normalizedResourceTag = quoteEtag(resource.etag); // Weak tags never match in If-Range (strong comparison only, per RFC 7233) if (normalizedTag.startsWith('W/') || normalizedResourceTag.startsWith('W/')) { return false; } return normalizedTag === normalizedResourceTag; } return false; } /** * Returns the string representation of the header value. * * @return The header value as a string */ toString() { return this.value; } }