11cb0ef41Sopenharmony_ci'use strict';
21cb0ef41Sopenharmony_ci// rfc7231 6.1
31cb0ef41Sopenharmony_ciconst statusCodeCacheableByDefault = new Set([
41cb0ef41Sopenharmony_ci    200,
51cb0ef41Sopenharmony_ci    203,
61cb0ef41Sopenharmony_ci    204,
71cb0ef41Sopenharmony_ci    206,
81cb0ef41Sopenharmony_ci    300,
91cb0ef41Sopenharmony_ci    301,
101cb0ef41Sopenharmony_ci    308,
111cb0ef41Sopenharmony_ci    404,
121cb0ef41Sopenharmony_ci    405,
131cb0ef41Sopenharmony_ci    410,
141cb0ef41Sopenharmony_ci    414,
151cb0ef41Sopenharmony_ci    501,
161cb0ef41Sopenharmony_ci]);
171cb0ef41Sopenharmony_ci
181cb0ef41Sopenharmony_ci// This implementation does not understand partial responses (206)
191cb0ef41Sopenharmony_ciconst understoodStatuses = new Set([
201cb0ef41Sopenharmony_ci    200,
211cb0ef41Sopenharmony_ci    203,
221cb0ef41Sopenharmony_ci    204,
231cb0ef41Sopenharmony_ci    300,
241cb0ef41Sopenharmony_ci    301,
251cb0ef41Sopenharmony_ci    302,
261cb0ef41Sopenharmony_ci    303,
271cb0ef41Sopenharmony_ci    307,
281cb0ef41Sopenharmony_ci    308,
291cb0ef41Sopenharmony_ci    404,
301cb0ef41Sopenharmony_ci    405,
311cb0ef41Sopenharmony_ci    410,
321cb0ef41Sopenharmony_ci    414,
331cb0ef41Sopenharmony_ci    501,
341cb0ef41Sopenharmony_ci]);
351cb0ef41Sopenharmony_ci
361cb0ef41Sopenharmony_ciconst errorStatusCodes = new Set([
371cb0ef41Sopenharmony_ci    500,
381cb0ef41Sopenharmony_ci    502,
391cb0ef41Sopenharmony_ci    503,
401cb0ef41Sopenharmony_ci    504,
411cb0ef41Sopenharmony_ci]);
421cb0ef41Sopenharmony_ci
431cb0ef41Sopenharmony_ciconst hopByHopHeaders = {
441cb0ef41Sopenharmony_ci    date: true, // included, because we add Age update Date
451cb0ef41Sopenharmony_ci    connection: true,
461cb0ef41Sopenharmony_ci    'keep-alive': true,
471cb0ef41Sopenharmony_ci    'proxy-authenticate': true,
481cb0ef41Sopenharmony_ci    'proxy-authorization': true,
491cb0ef41Sopenharmony_ci    te: true,
501cb0ef41Sopenharmony_ci    trailer: true,
511cb0ef41Sopenharmony_ci    'transfer-encoding': true,
521cb0ef41Sopenharmony_ci    upgrade: true,
531cb0ef41Sopenharmony_ci};
541cb0ef41Sopenharmony_ci
551cb0ef41Sopenharmony_ciconst excludedFromRevalidationUpdate = {
561cb0ef41Sopenharmony_ci    // Since the old body is reused, it doesn't make sense to change properties of the body
571cb0ef41Sopenharmony_ci    'content-length': true,
581cb0ef41Sopenharmony_ci    'content-encoding': true,
591cb0ef41Sopenharmony_ci    'transfer-encoding': true,
601cb0ef41Sopenharmony_ci    'content-range': true,
611cb0ef41Sopenharmony_ci};
621cb0ef41Sopenharmony_ci
631cb0ef41Sopenharmony_cifunction toNumberOrZero(s) {
641cb0ef41Sopenharmony_ci    const n = parseInt(s, 10);
651cb0ef41Sopenharmony_ci    return isFinite(n) ? n : 0;
661cb0ef41Sopenharmony_ci}
671cb0ef41Sopenharmony_ci
681cb0ef41Sopenharmony_ci// RFC 5861
691cb0ef41Sopenharmony_cifunction isErrorResponse(response) {
701cb0ef41Sopenharmony_ci    // consider undefined response as faulty
711cb0ef41Sopenharmony_ci    if(!response) {
721cb0ef41Sopenharmony_ci        return true
731cb0ef41Sopenharmony_ci    }
741cb0ef41Sopenharmony_ci    return errorStatusCodes.has(response.status);
751cb0ef41Sopenharmony_ci}
761cb0ef41Sopenharmony_ci
771cb0ef41Sopenharmony_cifunction parseCacheControl(header) {
781cb0ef41Sopenharmony_ci    const cc = {};
791cb0ef41Sopenharmony_ci    if (!header) return cc;
801cb0ef41Sopenharmony_ci
811cb0ef41Sopenharmony_ci    // TODO: When there is more than one value present for a given directive (e.g., two Expires header fields, multiple Cache-Control: max-age directives),
821cb0ef41Sopenharmony_ci    // the directive's value is considered invalid. Caches are encouraged to consider responses that have invalid freshness information to be stale
831cb0ef41Sopenharmony_ci    const parts = header.trim().split(/,/);
841cb0ef41Sopenharmony_ci    for (const part of parts) {
851cb0ef41Sopenharmony_ci        const [k, v] = part.split(/=/, 2);
861cb0ef41Sopenharmony_ci        cc[k.trim()] = v === undefined ? true : v.trim().replace(/^"|"$/g, '');
871cb0ef41Sopenharmony_ci    }
881cb0ef41Sopenharmony_ci
891cb0ef41Sopenharmony_ci    return cc;
901cb0ef41Sopenharmony_ci}
911cb0ef41Sopenharmony_ci
921cb0ef41Sopenharmony_cifunction formatCacheControl(cc) {
931cb0ef41Sopenharmony_ci    let parts = [];
941cb0ef41Sopenharmony_ci    for (const k in cc) {
951cb0ef41Sopenharmony_ci        const v = cc[k];
961cb0ef41Sopenharmony_ci        parts.push(v === true ? k : k + '=' + v);
971cb0ef41Sopenharmony_ci    }
981cb0ef41Sopenharmony_ci    if (!parts.length) {
991cb0ef41Sopenharmony_ci        return undefined;
1001cb0ef41Sopenharmony_ci    }
1011cb0ef41Sopenharmony_ci    return parts.join(', ');
1021cb0ef41Sopenharmony_ci}
1031cb0ef41Sopenharmony_ci
1041cb0ef41Sopenharmony_cimodule.exports = class CachePolicy {
1051cb0ef41Sopenharmony_ci    constructor(
1061cb0ef41Sopenharmony_ci        req,
1071cb0ef41Sopenharmony_ci        res,
1081cb0ef41Sopenharmony_ci        {
1091cb0ef41Sopenharmony_ci            shared,
1101cb0ef41Sopenharmony_ci            cacheHeuristic,
1111cb0ef41Sopenharmony_ci            immutableMinTimeToLive,
1121cb0ef41Sopenharmony_ci            ignoreCargoCult,
1131cb0ef41Sopenharmony_ci            _fromObject,
1141cb0ef41Sopenharmony_ci        } = {}
1151cb0ef41Sopenharmony_ci    ) {
1161cb0ef41Sopenharmony_ci        if (_fromObject) {
1171cb0ef41Sopenharmony_ci            this._fromObject(_fromObject);
1181cb0ef41Sopenharmony_ci            return;
1191cb0ef41Sopenharmony_ci        }
1201cb0ef41Sopenharmony_ci
1211cb0ef41Sopenharmony_ci        if (!res || !res.headers) {
1221cb0ef41Sopenharmony_ci            throw Error('Response headers missing');
1231cb0ef41Sopenharmony_ci        }
1241cb0ef41Sopenharmony_ci        this._assertRequestHasHeaders(req);
1251cb0ef41Sopenharmony_ci
1261cb0ef41Sopenharmony_ci        this._responseTime = this.now();
1271cb0ef41Sopenharmony_ci        this._isShared = shared !== false;
1281cb0ef41Sopenharmony_ci        this._cacheHeuristic =
1291cb0ef41Sopenharmony_ci            undefined !== cacheHeuristic ? cacheHeuristic : 0.1; // 10% matches IE
1301cb0ef41Sopenharmony_ci        this._immutableMinTtl =
1311cb0ef41Sopenharmony_ci            undefined !== immutableMinTimeToLive
1321cb0ef41Sopenharmony_ci                ? immutableMinTimeToLive
1331cb0ef41Sopenharmony_ci                : 24 * 3600 * 1000;
1341cb0ef41Sopenharmony_ci
1351cb0ef41Sopenharmony_ci        this._status = 'status' in res ? res.status : 200;
1361cb0ef41Sopenharmony_ci        this._resHeaders = res.headers;
1371cb0ef41Sopenharmony_ci        this._rescc = parseCacheControl(res.headers['cache-control']);
1381cb0ef41Sopenharmony_ci        this._method = 'method' in req ? req.method : 'GET';
1391cb0ef41Sopenharmony_ci        this._url = req.url;
1401cb0ef41Sopenharmony_ci        this._host = req.headers.host;
1411cb0ef41Sopenharmony_ci        this._noAuthorization = !req.headers.authorization;
1421cb0ef41Sopenharmony_ci        this._reqHeaders = res.headers.vary ? req.headers : null; // Don't keep all request headers if they won't be used
1431cb0ef41Sopenharmony_ci        this._reqcc = parseCacheControl(req.headers['cache-control']);
1441cb0ef41Sopenharmony_ci
1451cb0ef41Sopenharmony_ci        // Assume that if someone uses legacy, non-standard uncecessary options they don't understand caching,
1461cb0ef41Sopenharmony_ci        // so there's no point stricly adhering to the blindly copy&pasted directives.
1471cb0ef41Sopenharmony_ci        if (
1481cb0ef41Sopenharmony_ci            ignoreCargoCult &&
1491cb0ef41Sopenharmony_ci            'pre-check' in this._rescc &&
1501cb0ef41Sopenharmony_ci            'post-check' in this._rescc
1511cb0ef41Sopenharmony_ci        ) {
1521cb0ef41Sopenharmony_ci            delete this._rescc['pre-check'];
1531cb0ef41Sopenharmony_ci            delete this._rescc['post-check'];
1541cb0ef41Sopenharmony_ci            delete this._rescc['no-cache'];
1551cb0ef41Sopenharmony_ci            delete this._rescc['no-store'];
1561cb0ef41Sopenharmony_ci            delete this._rescc['must-revalidate'];
1571cb0ef41Sopenharmony_ci            this._resHeaders = Object.assign({}, this._resHeaders, {
1581cb0ef41Sopenharmony_ci                'cache-control': formatCacheControl(this._rescc),
1591cb0ef41Sopenharmony_ci            });
1601cb0ef41Sopenharmony_ci            delete this._resHeaders.expires;
1611cb0ef41Sopenharmony_ci            delete this._resHeaders.pragma;
1621cb0ef41Sopenharmony_ci        }
1631cb0ef41Sopenharmony_ci
1641cb0ef41Sopenharmony_ci        // When the Cache-Control header field is not present in a request, caches MUST consider the no-cache request pragma-directive
1651cb0ef41Sopenharmony_ci        // as having the same effect as if "Cache-Control: no-cache" were present (see Section 5.2.1).
1661cb0ef41Sopenharmony_ci        if (
1671cb0ef41Sopenharmony_ci            res.headers['cache-control'] == null &&
1681cb0ef41Sopenharmony_ci            /no-cache/.test(res.headers.pragma)
1691cb0ef41Sopenharmony_ci        ) {
1701cb0ef41Sopenharmony_ci            this._rescc['no-cache'] = true;
1711cb0ef41Sopenharmony_ci        }
1721cb0ef41Sopenharmony_ci    }
1731cb0ef41Sopenharmony_ci
1741cb0ef41Sopenharmony_ci    now() {
1751cb0ef41Sopenharmony_ci        return Date.now();
1761cb0ef41Sopenharmony_ci    }
1771cb0ef41Sopenharmony_ci
1781cb0ef41Sopenharmony_ci    storable() {
1791cb0ef41Sopenharmony_ci        // The "no-store" request directive indicates that a cache MUST NOT store any part of either this request or any response to it.
1801cb0ef41Sopenharmony_ci        return !!(
1811cb0ef41Sopenharmony_ci            !this._reqcc['no-store'] &&
1821cb0ef41Sopenharmony_ci            // A cache MUST NOT store a response to any request, unless:
1831cb0ef41Sopenharmony_ci            // The request method is understood by the cache and defined as being cacheable, and
1841cb0ef41Sopenharmony_ci            ('GET' === this._method ||
1851cb0ef41Sopenharmony_ci                'HEAD' === this._method ||
1861cb0ef41Sopenharmony_ci                ('POST' === this._method && this._hasExplicitExpiration())) &&
1871cb0ef41Sopenharmony_ci            // the response status code is understood by the cache, and
1881cb0ef41Sopenharmony_ci            understoodStatuses.has(this._status) &&
1891cb0ef41Sopenharmony_ci            // the "no-store" cache directive does not appear in request or response header fields, and
1901cb0ef41Sopenharmony_ci            !this._rescc['no-store'] &&
1911cb0ef41Sopenharmony_ci            // the "private" response directive does not appear in the response, if the cache is shared, and
1921cb0ef41Sopenharmony_ci            (!this._isShared || !this._rescc.private) &&
1931cb0ef41Sopenharmony_ci            // the Authorization header field does not appear in the request, if the cache is shared,
1941cb0ef41Sopenharmony_ci            (!this._isShared ||
1951cb0ef41Sopenharmony_ci                this._noAuthorization ||
1961cb0ef41Sopenharmony_ci                this._allowsStoringAuthenticated()) &&
1971cb0ef41Sopenharmony_ci            // the response either:
1981cb0ef41Sopenharmony_ci            // contains an Expires header field, or
1991cb0ef41Sopenharmony_ci            (this._resHeaders.expires ||
2001cb0ef41Sopenharmony_ci                // contains a max-age response directive, or
2011cb0ef41Sopenharmony_ci                // contains a s-maxage response directive and the cache is shared, or
2021cb0ef41Sopenharmony_ci                // contains a public response directive.
2031cb0ef41Sopenharmony_ci                this._rescc['max-age'] ||
2041cb0ef41Sopenharmony_ci                (this._isShared && this._rescc['s-maxage']) ||
2051cb0ef41Sopenharmony_ci                this._rescc.public ||
2061cb0ef41Sopenharmony_ci                // has a status code that is defined as cacheable by default
2071cb0ef41Sopenharmony_ci                statusCodeCacheableByDefault.has(this._status))
2081cb0ef41Sopenharmony_ci        );
2091cb0ef41Sopenharmony_ci    }
2101cb0ef41Sopenharmony_ci
2111cb0ef41Sopenharmony_ci    _hasExplicitExpiration() {
2121cb0ef41Sopenharmony_ci        // 4.2.1 Calculating Freshness Lifetime
2131cb0ef41Sopenharmony_ci        return (
2141cb0ef41Sopenharmony_ci            (this._isShared && this._rescc['s-maxage']) ||
2151cb0ef41Sopenharmony_ci            this._rescc['max-age'] ||
2161cb0ef41Sopenharmony_ci            this._resHeaders.expires
2171cb0ef41Sopenharmony_ci        );
2181cb0ef41Sopenharmony_ci    }
2191cb0ef41Sopenharmony_ci
2201cb0ef41Sopenharmony_ci    _assertRequestHasHeaders(req) {
2211cb0ef41Sopenharmony_ci        if (!req || !req.headers) {
2221cb0ef41Sopenharmony_ci            throw Error('Request headers missing');
2231cb0ef41Sopenharmony_ci        }
2241cb0ef41Sopenharmony_ci    }
2251cb0ef41Sopenharmony_ci
2261cb0ef41Sopenharmony_ci    satisfiesWithoutRevalidation(req) {
2271cb0ef41Sopenharmony_ci        this._assertRequestHasHeaders(req);
2281cb0ef41Sopenharmony_ci
2291cb0ef41Sopenharmony_ci        // When presented with a request, a cache MUST NOT reuse a stored response, unless:
2301cb0ef41Sopenharmony_ci        // the presented request does not contain the no-cache pragma (Section 5.4), nor the no-cache cache directive,
2311cb0ef41Sopenharmony_ci        // unless the stored response is successfully validated (Section 4.3), and
2321cb0ef41Sopenharmony_ci        const requestCC = parseCacheControl(req.headers['cache-control']);
2331cb0ef41Sopenharmony_ci        if (requestCC['no-cache'] || /no-cache/.test(req.headers.pragma)) {
2341cb0ef41Sopenharmony_ci            return false;
2351cb0ef41Sopenharmony_ci        }
2361cb0ef41Sopenharmony_ci
2371cb0ef41Sopenharmony_ci        if (requestCC['max-age'] && this.age() > requestCC['max-age']) {
2381cb0ef41Sopenharmony_ci            return false;
2391cb0ef41Sopenharmony_ci        }
2401cb0ef41Sopenharmony_ci
2411cb0ef41Sopenharmony_ci        if (
2421cb0ef41Sopenharmony_ci            requestCC['min-fresh'] &&
2431cb0ef41Sopenharmony_ci            this.timeToLive() < 1000 * requestCC['min-fresh']
2441cb0ef41Sopenharmony_ci        ) {
2451cb0ef41Sopenharmony_ci            return false;
2461cb0ef41Sopenharmony_ci        }
2471cb0ef41Sopenharmony_ci
2481cb0ef41Sopenharmony_ci        // the stored response is either:
2491cb0ef41Sopenharmony_ci        // fresh, or allowed to be served stale
2501cb0ef41Sopenharmony_ci        if (this.stale()) {
2511cb0ef41Sopenharmony_ci            const allowsStale =
2521cb0ef41Sopenharmony_ci                requestCC['max-stale'] &&
2531cb0ef41Sopenharmony_ci                !this._rescc['must-revalidate'] &&
2541cb0ef41Sopenharmony_ci                (true === requestCC['max-stale'] ||
2551cb0ef41Sopenharmony_ci                    requestCC['max-stale'] > this.age() - this.maxAge());
2561cb0ef41Sopenharmony_ci            if (!allowsStale) {
2571cb0ef41Sopenharmony_ci                return false;
2581cb0ef41Sopenharmony_ci            }
2591cb0ef41Sopenharmony_ci        }
2601cb0ef41Sopenharmony_ci
2611cb0ef41Sopenharmony_ci        return this._requestMatches(req, false);
2621cb0ef41Sopenharmony_ci    }
2631cb0ef41Sopenharmony_ci
2641cb0ef41Sopenharmony_ci    _requestMatches(req, allowHeadMethod) {
2651cb0ef41Sopenharmony_ci        // The presented effective request URI and that of the stored response match, and
2661cb0ef41Sopenharmony_ci        return (
2671cb0ef41Sopenharmony_ci            (!this._url || this._url === req.url) &&
2681cb0ef41Sopenharmony_ci            this._host === req.headers.host &&
2691cb0ef41Sopenharmony_ci            // the request method associated with the stored response allows it to be used for the presented request, and
2701cb0ef41Sopenharmony_ci            (!req.method ||
2711cb0ef41Sopenharmony_ci                this._method === req.method ||
2721cb0ef41Sopenharmony_ci                (allowHeadMethod && 'HEAD' === req.method)) &&
2731cb0ef41Sopenharmony_ci            // selecting header fields nominated by the stored response (if any) match those presented, and
2741cb0ef41Sopenharmony_ci            this._varyMatches(req)
2751cb0ef41Sopenharmony_ci        );
2761cb0ef41Sopenharmony_ci    }
2771cb0ef41Sopenharmony_ci
2781cb0ef41Sopenharmony_ci    _allowsStoringAuthenticated() {
2791cb0ef41Sopenharmony_ci        //  following Cache-Control response directives (Section 5.2.2) have such an effect: must-revalidate, public, and s-maxage.
2801cb0ef41Sopenharmony_ci        return (
2811cb0ef41Sopenharmony_ci            this._rescc['must-revalidate'] ||
2821cb0ef41Sopenharmony_ci            this._rescc.public ||
2831cb0ef41Sopenharmony_ci            this._rescc['s-maxage']
2841cb0ef41Sopenharmony_ci        );
2851cb0ef41Sopenharmony_ci    }
2861cb0ef41Sopenharmony_ci
2871cb0ef41Sopenharmony_ci    _varyMatches(req) {
2881cb0ef41Sopenharmony_ci        if (!this._resHeaders.vary) {
2891cb0ef41Sopenharmony_ci            return true;
2901cb0ef41Sopenharmony_ci        }
2911cb0ef41Sopenharmony_ci
2921cb0ef41Sopenharmony_ci        // A Vary header field-value of "*" always fails to match
2931cb0ef41Sopenharmony_ci        if (this._resHeaders.vary === '*') {
2941cb0ef41Sopenharmony_ci            return false;
2951cb0ef41Sopenharmony_ci        }
2961cb0ef41Sopenharmony_ci
2971cb0ef41Sopenharmony_ci        const fields = this._resHeaders.vary
2981cb0ef41Sopenharmony_ci            .trim()
2991cb0ef41Sopenharmony_ci            .toLowerCase()
3001cb0ef41Sopenharmony_ci            .split(/\s*,\s*/);
3011cb0ef41Sopenharmony_ci        for (const name of fields) {
3021cb0ef41Sopenharmony_ci            if (req.headers[name] !== this._reqHeaders[name]) return false;
3031cb0ef41Sopenharmony_ci        }
3041cb0ef41Sopenharmony_ci        return true;
3051cb0ef41Sopenharmony_ci    }
3061cb0ef41Sopenharmony_ci
3071cb0ef41Sopenharmony_ci    _copyWithoutHopByHopHeaders(inHeaders) {
3081cb0ef41Sopenharmony_ci        const headers = {};
3091cb0ef41Sopenharmony_ci        for (const name in inHeaders) {
3101cb0ef41Sopenharmony_ci            if (hopByHopHeaders[name]) continue;
3111cb0ef41Sopenharmony_ci            headers[name] = inHeaders[name];
3121cb0ef41Sopenharmony_ci        }
3131cb0ef41Sopenharmony_ci        // 9.1.  Connection
3141cb0ef41Sopenharmony_ci        if (inHeaders.connection) {
3151cb0ef41Sopenharmony_ci            const tokens = inHeaders.connection.trim().split(/\s*,\s*/);
3161cb0ef41Sopenharmony_ci            for (const name of tokens) {
3171cb0ef41Sopenharmony_ci                delete headers[name];
3181cb0ef41Sopenharmony_ci            }
3191cb0ef41Sopenharmony_ci        }
3201cb0ef41Sopenharmony_ci        if (headers.warning) {
3211cb0ef41Sopenharmony_ci            const warnings = headers.warning.split(/,/).filter(warning => {
3221cb0ef41Sopenharmony_ci                return !/^\s*1[0-9][0-9]/.test(warning);
3231cb0ef41Sopenharmony_ci            });
3241cb0ef41Sopenharmony_ci            if (!warnings.length) {
3251cb0ef41Sopenharmony_ci                delete headers.warning;
3261cb0ef41Sopenharmony_ci            } else {
3271cb0ef41Sopenharmony_ci                headers.warning = warnings.join(',').trim();
3281cb0ef41Sopenharmony_ci            }
3291cb0ef41Sopenharmony_ci        }
3301cb0ef41Sopenharmony_ci        return headers;
3311cb0ef41Sopenharmony_ci    }
3321cb0ef41Sopenharmony_ci
3331cb0ef41Sopenharmony_ci    responseHeaders() {
3341cb0ef41Sopenharmony_ci        const headers = this._copyWithoutHopByHopHeaders(this._resHeaders);
3351cb0ef41Sopenharmony_ci        const age = this.age();
3361cb0ef41Sopenharmony_ci
3371cb0ef41Sopenharmony_ci        // A cache SHOULD generate 113 warning if it heuristically chose a freshness
3381cb0ef41Sopenharmony_ci        // lifetime greater than 24 hours and the response's age is greater than 24 hours.
3391cb0ef41Sopenharmony_ci        if (
3401cb0ef41Sopenharmony_ci            age > 3600 * 24 &&
3411cb0ef41Sopenharmony_ci            !this._hasExplicitExpiration() &&
3421cb0ef41Sopenharmony_ci            this.maxAge() > 3600 * 24
3431cb0ef41Sopenharmony_ci        ) {
3441cb0ef41Sopenharmony_ci            headers.warning =
3451cb0ef41Sopenharmony_ci                (headers.warning ? `${headers.warning}, ` : '') +
3461cb0ef41Sopenharmony_ci                '113 - "rfc7234 5.5.4"';
3471cb0ef41Sopenharmony_ci        }
3481cb0ef41Sopenharmony_ci        headers.age = `${Math.round(age)}`;
3491cb0ef41Sopenharmony_ci        headers.date = new Date(this.now()).toUTCString();
3501cb0ef41Sopenharmony_ci        return headers;
3511cb0ef41Sopenharmony_ci    }
3521cb0ef41Sopenharmony_ci
3531cb0ef41Sopenharmony_ci    /**
3541cb0ef41Sopenharmony_ci     * Value of the Date response header or current time if Date was invalid
3551cb0ef41Sopenharmony_ci     * @return timestamp
3561cb0ef41Sopenharmony_ci     */
3571cb0ef41Sopenharmony_ci    date() {
3581cb0ef41Sopenharmony_ci        const serverDate = Date.parse(this._resHeaders.date);
3591cb0ef41Sopenharmony_ci        if (isFinite(serverDate)) {
3601cb0ef41Sopenharmony_ci            return serverDate;
3611cb0ef41Sopenharmony_ci        }
3621cb0ef41Sopenharmony_ci        return this._responseTime;
3631cb0ef41Sopenharmony_ci    }
3641cb0ef41Sopenharmony_ci
3651cb0ef41Sopenharmony_ci    /**
3661cb0ef41Sopenharmony_ci     * Value of the Age header, in seconds, updated for the current time.
3671cb0ef41Sopenharmony_ci     * May be fractional.
3681cb0ef41Sopenharmony_ci     *
3691cb0ef41Sopenharmony_ci     * @return Number
3701cb0ef41Sopenharmony_ci     */
3711cb0ef41Sopenharmony_ci    age() {
3721cb0ef41Sopenharmony_ci        let age = this._ageValue();
3731cb0ef41Sopenharmony_ci
3741cb0ef41Sopenharmony_ci        const residentTime = (this.now() - this._responseTime) / 1000;
3751cb0ef41Sopenharmony_ci        return age + residentTime;
3761cb0ef41Sopenharmony_ci    }
3771cb0ef41Sopenharmony_ci
3781cb0ef41Sopenharmony_ci    _ageValue() {
3791cb0ef41Sopenharmony_ci        return toNumberOrZero(this._resHeaders.age);
3801cb0ef41Sopenharmony_ci    }
3811cb0ef41Sopenharmony_ci
3821cb0ef41Sopenharmony_ci    /**
3831cb0ef41Sopenharmony_ci     * Value of applicable max-age (or heuristic equivalent) in seconds. This counts since response's `Date`.
3841cb0ef41Sopenharmony_ci     *
3851cb0ef41Sopenharmony_ci     * For an up-to-date value, see `timeToLive()`.
3861cb0ef41Sopenharmony_ci     *
3871cb0ef41Sopenharmony_ci     * @return Number
3881cb0ef41Sopenharmony_ci     */
3891cb0ef41Sopenharmony_ci    maxAge() {
3901cb0ef41Sopenharmony_ci        if (!this.storable() || this._rescc['no-cache']) {
3911cb0ef41Sopenharmony_ci            return 0;
3921cb0ef41Sopenharmony_ci        }
3931cb0ef41Sopenharmony_ci
3941cb0ef41Sopenharmony_ci        // Shared responses with cookies are cacheable according to the RFC, but IMHO it'd be unwise to do so by default
3951cb0ef41Sopenharmony_ci        // so this implementation requires explicit opt-in via public header
3961cb0ef41Sopenharmony_ci        if (
3971cb0ef41Sopenharmony_ci            this._isShared &&
3981cb0ef41Sopenharmony_ci            (this._resHeaders['set-cookie'] &&
3991cb0ef41Sopenharmony_ci                !this._rescc.public &&
4001cb0ef41Sopenharmony_ci                !this._rescc.immutable)
4011cb0ef41Sopenharmony_ci        ) {
4021cb0ef41Sopenharmony_ci            return 0;
4031cb0ef41Sopenharmony_ci        }
4041cb0ef41Sopenharmony_ci
4051cb0ef41Sopenharmony_ci        if (this._resHeaders.vary === '*') {
4061cb0ef41Sopenharmony_ci            return 0;
4071cb0ef41Sopenharmony_ci        }
4081cb0ef41Sopenharmony_ci
4091cb0ef41Sopenharmony_ci        if (this._isShared) {
4101cb0ef41Sopenharmony_ci            if (this._rescc['proxy-revalidate']) {
4111cb0ef41Sopenharmony_ci                return 0;
4121cb0ef41Sopenharmony_ci            }
4131cb0ef41Sopenharmony_ci            // if a response includes the s-maxage directive, a shared cache recipient MUST ignore the Expires field.
4141cb0ef41Sopenharmony_ci            if (this._rescc['s-maxage']) {
4151cb0ef41Sopenharmony_ci                return toNumberOrZero(this._rescc['s-maxage']);
4161cb0ef41Sopenharmony_ci            }
4171cb0ef41Sopenharmony_ci        }
4181cb0ef41Sopenharmony_ci
4191cb0ef41Sopenharmony_ci        // If a response includes a Cache-Control field with the max-age directive, a recipient MUST ignore the Expires field.
4201cb0ef41Sopenharmony_ci        if (this._rescc['max-age']) {
4211cb0ef41Sopenharmony_ci            return toNumberOrZero(this._rescc['max-age']);
4221cb0ef41Sopenharmony_ci        }
4231cb0ef41Sopenharmony_ci
4241cb0ef41Sopenharmony_ci        const defaultMinTtl = this._rescc.immutable ? this._immutableMinTtl : 0;
4251cb0ef41Sopenharmony_ci
4261cb0ef41Sopenharmony_ci        const serverDate = this.date();
4271cb0ef41Sopenharmony_ci        if (this._resHeaders.expires) {
4281cb0ef41Sopenharmony_ci            const expires = Date.parse(this._resHeaders.expires);
4291cb0ef41Sopenharmony_ci            // A cache recipient MUST interpret invalid date formats, especially the value "0", as representing a time in the past (i.e., "already expired").
4301cb0ef41Sopenharmony_ci            if (Number.isNaN(expires) || expires < serverDate) {
4311cb0ef41Sopenharmony_ci                return 0;
4321cb0ef41Sopenharmony_ci            }
4331cb0ef41Sopenharmony_ci            return Math.max(defaultMinTtl, (expires - serverDate) / 1000);
4341cb0ef41Sopenharmony_ci        }
4351cb0ef41Sopenharmony_ci
4361cb0ef41Sopenharmony_ci        if (this._resHeaders['last-modified']) {
4371cb0ef41Sopenharmony_ci            const lastModified = Date.parse(this._resHeaders['last-modified']);
4381cb0ef41Sopenharmony_ci            if (isFinite(lastModified) && serverDate > lastModified) {
4391cb0ef41Sopenharmony_ci                return Math.max(
4401cb0ef41Sopenharmony_ci                    defaultMinTtl,
4411cb0ef41Sopenharmony_ci                    ((serverDate - lastModified) / 1000) * this._cacheHeuristic
4421cb0ef41Sopenharmony_ci                );
4431cb0ef41Sopenharmony_ci            }
4441cb0ef41Sopenharmony_ci        }
4451cb0ef41Sopenharmony_ci
4461cb0ef41Sopenharmony_ci        return defaultMinTtl;
4471cb0ef41Sopenharmony_ci    }
4481cb0ef41Sopenharmony_ci
4491cb0ef41Sopenharmony_ci    timeToLive() {
4501cb0ef41Sopenharmony_ci        const age = this.maxAge() - this.age();
4511cb0ef41Sopenharmony_ci        const staleIfErrorAge = age + toNumberOrZero(this._rescc['stale-if-error']);
4521cb0ef41Sopenharmony_ci        const staleWhileRevalidateAge = age + toNumberOrZero(this._rescc['stale-while-revalidate']);
4531cb0ef41Sopenharmony_ci        return Math.max(0, age, staleIfErrorAge, staleWhileRevalidateAge) * 1000;
4541cb0ef41Sopenharmony_ci    }
4551cb0ef41Sopenharmony_ci
4561cb0ef41Sopenharmony_ci    stale() {
4571cb0ef41Sopenharmony_ci        return this.maxAge() <= this.age();
4581cb0ef41Sopenharmony_ci    }
4591cb0ef41Sopenharmony_ci
4601cb0ef41Sopenharmony_ci    _useStaleIfError() {
4611cb0ef41Sopenharmony_ci        return this.maxAge() + toNumberOrZero(this._rescc['stale-if-error']) > this.age();
4621cb0ef41Sopenharmony_ci    }
4631cb0ef41Sopenharmony_ci
4641cb0ef41Sopenharmony_ci    useStaleWhileRevalidate() {
4651cb0ef41Sopenharmony_ci        return this.maxAge() + toNumberOrZero(this._rescc['stale-while-revalidate']) > this.age();
4661cb0ef41Sopenharmony_ci    }
4671cb0ef41Sopenharmony_ci
4681cb0ef41Sopenharmony_ci    static fromObject(obj) {
4691cb0ef41Sopenharmony_ci        return new this(undefined, undefined, { _fromObject: obj });
4701cb0ef41Sopenharmony_ci    }
4711cb0ef41Sopenharmony_ci
4721cb0ef41Sopenharmony_ci    _fromObject(obj) {
4731cb0ef41Sopenharmony_ci        if (this._responseTime) throw Error('Reinitialized');
4741cb0ef41Sopenharmony_ci        if (!obj || obj.v !== 1) throw Error('Invalid serialization');
4751cb0ef41Sopenharmony_ci
4761cb0ef41Sopenharmony_ci        this._responseTime = obj.t;
4771cb0ef41Sopenharmony_ci        this._isShared = obj.sh;
4781cb0ef41Sopenharmony_ci        this._cacheHeuristic = obj.ch;
4791cb0ef41Sopenharmony_ci        this._immutableMinTtl =
4801cb0ef41Sopenharmony_ci            obj.imm !== undefined ? obj.imm : 24 * 3600 * 1000;
4811cb0ef41Sopenharmony_ci        this._status = obj.st;
4821cb0ef41Sopenharmony_ci        this._resHeaders = obj.resh;
4831cb0ef41Sopenharmony_ci        this._rescc = obj.rescc;
4841cb0ef41Sopenharmony_ci        this._method = obj.m;
4851cb0ef41Sopenharmony_ci        this._url = obj.u;
4861cb0ef41Sopenharmony_ci        this._host = obj.h;
4871cb0ef41Sopenharmony_ci        this._noAuthorization = obj.a;
4881cb0ef41Sopenharmony_ci        this._reqHeaders = obj.reqh;
4891cb0ef41Sopenharmony_ci        this._reqcc = obj.reqcc;
4901cb0ef41Sopenharmony_ci    }
4911cb0ef41Sopenharmony_ci
4921cb0ef41Sopenharmony_ci    toObject() {
4931cb0ef41Sopenharmony_ci        return {
4941cb0ef41Sopenharmony_ci            v: 1,
4951cb0ef41Sopenharmony_ci            t: this._responseTime,
4961cb0ef41Sopenharmony_ci            sh: this._isShared,
4971cb0ef41Sopenharmony_ci            ch: this._cacheHeuristic,
4981cb0ef41Sopenharmony_ci            imm: this._immutableMinTtl,
4991cb0ef41Sopenharmony_ci            st: this._status,
5001cb0ef41Sopenharmony_ci            resh: this._resHeaders,
5011cb0ef41Sopenharmony_ci            rescc: this._rescc,
5021cb0ef41Sopenharmony_ci            m: this._method,
5031cb0ef41Sopenharmony_ci            u: this._url,
5041cb0ef41Sopenharmony_ci            h: this._host,
5051cb0ef41Sopenharmony_ci            a: this._noAuthorization,
5061cb0ef41Sopenharmony_ci            reqh: this._reqHeaders,
5071cb0ef41Sopenharmony_ci            reqcc: this._reqcc,
5081cb0ef41Sopenharmony_ci        };
5091cb0ef41Sopenharmony_ci    }
5101cb0ef41Sopenharmony_ci
5111cb0ef41Sopenharmony_ci    /**
5121cb0ef41Sopenharmony_ci     * Headers for sending to the origin server to revalidate stale response.
5131cb0ef41Sopenharmony_ci     * Allows server to return 304 to allow reuse of the previous response.
5141cb0ef41Sopenharmony_ci     *
5151cb0ef41Sopenharmony_ci     * Hop by hop headers are always stripped.
5161cb0ef41Sopenharmony_ci     * Revalidation headers may be added or removed, depending on request.
5171cb0ef41Sopenharmony_ci     */
5181cb0ef41Sopenharmony_ci    revalidationHeaders(incomingReq) {
5191cb0ef41Sopenharmony_ci        this._assertRequestHasHeaders(incomingReq);
5201cb0ef41Sopenharmony_ci        const headers = this._copyWithoutHopByHopHeaders(incomingReq.headers);
5211cb0ef41Sopenharmony_ci
5221cb0ef41Sopenharmony_ci        // This implementation does not understand range requests
5231cb0ef41Sopenharmony_ci        delete headers['if-range'];
5241cb0ef41Sopenharmony_ci
5251cb0ef41Sopenharmony_ci        if (!this._requestMatches(incomingReq, true) || !this.storable()) {
5261cb0ef41Sopenharmony_ci            // revalidation allowed via HEAD
5271cb0ef41Sopenharmony_ci            // not for the same resource, or wasn't allowed to be cached anyway
5281cb0ef41Sopenharmony_ci            delete headers['if-none-match'];
5291cb0ef41Sopenharmony_ci            delete headers['if-modified-since'];
5301cb0ef41Sopenharmony_ci            return headers;
5311cb0ef41Sopenharmony_ci        }
5321cb0ef41Sopenharmony_ci
5331cb0ef41Sopenharmony_ci        /* MUST send that entity-tag in any cache validation request (using If-Match or If-None-Match) if an entity-tag has been provided by the origin server. */
5341cb0ef41Sopenharmony_ci        if (this._resHeaders.etag) {
5351cb0ef41Sopenharmony_ci            headers['if-none-match'] = headers['if-none-match']
5361cb0ef41Sopenharmony_ci                ? `${headers['if-none-match']}, ${this._resHeaders.etag}`
5371cb0ef41Sopenharmony_ci                : this._resHeaders.etag;
5381cb0ef41Sopenharmony_ci        }
5391cb0ef41Sopenharmony_ci
5401cb0ef41Sopenharmony_ci        // Clients MAY issue simple (non-subrange) GET requests with either weak validators or strong validators. Clients MUST NOT use weak validators in other forms of request.
5411cb0ef41Sopenharmony_ci        const forbidsWeakValidators =
5421cb0ef41Sopenharmony_ci            headers['accept-ranges'] ||
5431cb0ef41Sopenharmony_ci            headers['if-match'] ||
5441cb0ef41Sopenharmony_ci            headers['if-unmodified-since'] ||
5451cb0ef41Sopenharmony_ci            (this._method && this._method != 'GET');
5461cb0ef41Sopenharmony_ci
5471cb0ef41Sopenharmony_ci        /* SHOULD send the Last-Modified value in non-subrange cache validation requests (using If-Modified-Since) if only a Last-Modified value has been provided by the origin server.
5481cb0ef41Sopenharmony_ci        Note: This implementation does not understand partial responses (206) */
5491cb0ef41Sopenharmony_ci        if (forbidsWeakValidators) {
5501cb0ef41Sopenharmony_ci            delete headers['if-modified-since'];
5511cb0ef41Sopenharmony_ci
5521cb0ef41Sopenharmony_ci            if (headers['if-none-match']) {
5531cb0ef41Sopenharmony_ci                const etags = headers['if-none-match']
5541cb0ef41Sopenharmony_ci                    .split(/,/)
5551cb0ef41Sopenharmony_ci                    .filter(etag => {
5561cb0ef41Sopenharmony_ci                        return !/^\s*W\//.test(etag);
5571cb0ef41Sopenharmony_ci                    });
5581cb0ef41Sopenharmony_ci                if (!etags.length) {
5591cb0ef41Sopenharmony_ci                    delete headers['if-none-match'];
5601cb0ef41Sopenharmony_ci                } else {
5611cb0ef41Sopenharmony_ci                    headers['if-none-match'] = etags.join(',').trim();
5621cb0ef41Sopenharmony_ci                }
5631cb0ef41Sopenharmony_ci            }
5641cb0ef41Sopenharmony_ci        } else if (
5651cb0ef41Sopenharmony_ci            this._resHeaders['last-modified'] &&
5661cb0ef41Sopenharmony_ci            !headers['if-modified-since']
5671cb0ef41Sopenharmony_ci        ) {
5681cb0ef41Sopenharmony_ci            headers['if-modified-since'] = this._resHeaders['last-modified'];
5691cb0ef41Sopenharmony_ci        }
5701cb0ef41Sopenharmony_ci
5711cb0ef41Sopenharmony_ci        return headers;
5721cb0ef41Sopenharmony_ci    }
5731cb0ef41Sopenharmony_ci
5741cb0ef41Sopenharmony_ci    /**
5751cb0ef41Sopenharmony_ci     * Creates new CachePolicy with information combined from the previews response,
5761cb0ef41Sopenharmony_ci     * and the new revalidation response.
5771cb0ef41Sopenharmony_ci     *
5781cb0ef41Sopenharmony_ci     * Returns {policy, modified} where modified is a boolean indicating
5791cb0ef41Sopenharmony_ci     * whether the response body has been modified, and old cached body can't be used.
5801cb0ef41Sopenharmony_ci     *
5811cb0ef41Sopenharmony_ci     * @return {Object} {policy: CachePolicy, modified: Boolean}
5821cb0ef41Sopenharmony_ci     */
5831cb0ef41Sopenharmony_ci    revalidatedPolicy(request, response) {
5841cb0ef41Sopenharmony_ci        this._assertRequestHasHeaders(request);
5851cb0ef41Sopenharmony_ci        if(this._useStaleIfError() && isErrorResponse(response)) {  // I consider the revalidation request unsuccessful
5861cb0ef41Sopenharmony_ci          return {
5871cb0ef41Sopenharmony_ci            modified: false,
5881cb0ef41Sopenharmony_ci            matches: false,
5891cb0ef41Sopenharmony_ci            policy: this,
5901cb0ef41Sopenharmony_ci          };
5911cb0ef41Sopenharmony_ci        }
5921cb0ef41Sopenharmony_ci        if (!response || !response.headers) {
5931cb0ef41Sopenharmony_ci            throw Error('Response headers missing');
5941cb0ef41Sopenharmony_ci        }
5951cb0ef41Sopenharmony_ci
5961cb0ef41Sopenharmony_ci        // These aren't going to be supported exactly, since one CachePolicy object
5971cb0ef41Sopenharmony_ci        // doesn't know about all the other cached objects.
5981cb0ef41Sopenharmony_ci        let matches = false;
5991cb0ef41Sopenharmony_ci        if (response.status !== undefined && response.status != 304) {
6001cb0ef41Sopenharmony_ci            matches = false;
6011cb0ef41Sopenharmony_ci        } else if (
6021cb0ef41Sopenharmony_ci            response.headers.etag &&
6031cb0ef41Sopenharmony_ci            !/^\s*W\//.test(response.headers.etag)
6041cb0ef41Sopenharmony_ci        ) {
6051cb0ef41Sopenharmony_ci            // "All of the stored responses with the same strong validator are selected.
6061cb0ef41Sopenharmony_ci            // If none of the stored responses contain the same strong validator,
6071cb0ef41Sopenharmony_ci            // then the cache MUST NOT use the new response to update any stored responses."
6081cb0ef41Sopenharmony_ci            matches =
6091cb0ef41Sopenharmony_ci                this._resHeaders.etag &&
6101cb0ef41Sopenharmony_ci                this._resHeaders.etag.replace(/^\s*W\//, '') ===
6111cb0ef41Sopenharmony_ci                    response.headers.etag;
6121cb0ef41Sopenharmony_ci        } else if (this._resHeaders.etag && response.headers.etag) {
6131cb0ef41Sopenharmony_ci            // "If the new response contains a weak validator and that validator corresponds
6141cb0ef41Sopenharmony_ci            // to one of the cache's stored responses,
6151cb0ef41Sopenharmony_ci            // then the most recent of those matching stored responses is selected for update."
6161cb0ef41Sopenharmony_ci            matches =
6171cb0ef41Sopenharmony_ci                this._resHeaders.etag.replace(/^\s*W\//, '') ===
6181cb0ef41Sopenharmony_ci                response.headers.etag.replace(/^\s*W\//, '');
6191cb0ef41Sopenharmony_ci        } else if (this._resHeaders['last-modified']) {
6201cb0ef41Sopenharmony_ci            matches =
6211cb0ef41Sopenharmony_ci                this._resHeaders['last-modified'] ===
6221cb0ef41Sopenharmony_ci                response.headers['last-modified'];
6231cb0ef41Sopenharmony_ci        } else {
6241cb0ef41Sopenharmony_ci            // If the new response does not include any form of validator (such as in the case where
6251cb0ef41Sopenharmony_ci            // a client generates an If-Modified-Since request from a source other than the Last-Modified
6261cb0ef41Sopenharmony_ci            // response header field), and there is only one stored response, and that stored response also
6271cb0ef41Sopenharmony_ci            // lacks a validator, then that stored response is selected for update.
6281cb0ef41Sopenharmony_ci            if (
6291cb0ef41Sopenharmony_ci                !this._resHeaders.etag &&
6301cb0ef41Sopenharmony_ci                !this._resHeaders['last-modified'] &&
6311cb0ef41Sopenharmony_ci                !response.headers.etag &&
6321cb0ef41Sopenharmony_ci                !response.headers['last-modified']
6331cb0ef41Sopenharmony_ci            ) {
6341cb0ef41Sopenharmony_ci                matches = true;
6351cb0ef41Sopenharmony_ci            }
6361cb0ef41Sopenharmony_ci        }
6371cb0ef41Sopenharmony_ci
6381cb0ef41Sopenharmony_ci        if (!matches) {
6391cb0ef41Sopenharmony_ci            return {
6401cb0ef41Sopenharmony_ci                policy: new this.constructor(request, response),
6411cb0ef41Sopenharmony_ci                // Client receiving 304 without body, even if it's invalid/mismatched has no option
6421cb0ef41Sopenharmony_ci                // but to reuse a cached body. We don't have a good way to tell clients to do
6431cb0ef41Sopenharmony_ci                // error recovery in such case.
6441cb0ef41Sopenharmony_ci                modified: response.status != 304,
6451cb0ef41Sopenharmony_ci                matches: false,
6461cb0ef41Sopenharmony_ci            };
6471cb0ef41Sopenharmony_ci        }
6481cb0ef41Sopenharmony_ci
6491cb0ef41Sopenharmony_ci        // use other header fields provided in the 304 (Not Modified) response to replace all instances
6501cb0ef41Sopenharmony_ci        // of the corresponding header fields in the stored response.
6511cb0ef41Sopenharmony_ci        const headers = {};
6521cb0ef41Sopenharmony_ci        for (const k in this._resHeaders) {
6531cb0ef41Sopenharmony_ci            headers[k] =
6541cb0ef41Sopenharmony_ci                k in response.headers && !excludedFromRevalidationUpdate[k]
6551cb0ef41Sopenharmony_ci                    ? response.headers[k]
6561cb0ef41Sopenharmony_ci                    : this._resHeaders[k];
6571cb0ef41Sopenharmony_ci        }
6581cb0ef41Sopenharmony_ci
6591cb0ef41Sopenharmony_ci        const newResponse = Object.assign({}, response, {
6601cb0ef41Sopenharmony_ci            status: this._status,
6611cb0ef41Sopenharmony_ci            method: this._method,
6621cb0ef41Sopenharmony_ci            headers,
6631cb0ef41Sopenharmony_ci        });
6641cb0ef41Sopenharmony_ci        return {
6651cb0ef41Sopenharmony_ci            policy: new this.constructor(request, newResponse, {
6661cb0ef41Sopenharmony_ci                shared: this._isShared,
6671cb0ef41Sopenharmony_ci                cacheHeuristic: this._cacheHeuristic,
6681cb0ef41Sopenharmony_ci                immutableMinTimeToLive: this._immutableMinTtl,
6691cb0ef41Sopenharmony_ci            }),
6701cb0ef41Sopenharmony_ci            modified: false,
6711cb0ef41Sopenharmony_ci            matches: true,
6721cb0ef41Sopenharmony_ci        };
6731cb0ef41Sopenharmony_ci    }
6741cb0ef41Sopenharmony_ci};
675