@fly/cdn
Version:
Fly's TypeScript CDN
164 lines • 19 kB
JavaScript
/**
* @module Middleware
*/
import cache from "@fly/v8env/lib/fly/cache";
/**
* Cache HTTP responses with `cache-control` headers.
*
* Basic example:
* ```typescript
* import httpCache from "./src/middleware/http-cache";
* import backends from "./src/backends";
*
* const glitch = backends.glitch("fly-example");
*
* const origin = httpCache(glitch, { overrideMaxAge: 3600 });
*
* fly.http.respondWith(origin);
* ```
*
* @param fetch
* @param options
*/
export function httpCache(fetch, options) {
return async function httpCacheFetch(req, init) {
if (!options)
options = {};
if (typeof req === "string") {
req = new Request(req, init);
init = undefined;
}
// check the cache
let cacheable = true;
for (const h of ["Authorization", "Cookie"]) {
if (req.headers.get(h)) {
console.warn(h + " headers are not supported in http-cache");
cacheable = false;
}
}
let resp = cacheable ? await storage.match(req) : undefined;
if (resp) {
// got a hit
resp.headers.set("Fly-Cache", "hit");
return resp;
}
resp = await fetch(req, init);
// this should do nothing if the response can't be cached
const cacheHappened = cacheable ? await storage.put(req, resp, options.overrideMaxAge) : false;
if (cacheHappened) {
resp.headers.set("Fly-Cache", "miss");
}
return resp;
};
}
/**
* Configurable HTTP caching middleware. This is extremely useful within a `pipeline`:
*
* ```typescript
* const app = pipeline(
* httpsUpgrader,
* httpCaching.configure({overrideMaxAge: 3600}),
* glitch("fly-example")
* )
*
*/
httpCache.configure = (options) => {
return (fetch) => httpCache(fetch, options);
};
// copied from fly v8env
const CachePolicy = require("http-cache-semantics");
/**
* export:
* match(req): res | null
* add(req): void
* put(req, res): void
* @private
*/
const storage = {
async match(req) {
const hashed = hashData(req);
const key = "httpcache:policy:" + hashed; // first try with no vary variant
for (let i = 0; i < 5; i++) {
const policyRaw = await cache.getString(key);
console.debug("Got policy:", key, policyRaw);
if (!policyRaw) {
return undefined;
}
const policy = CachePolicy.fromObject(JSON.parse(policyRaw));
// if it fits i sits
if (policy.satisfiesWithoutRevalidation(req)) {
const headers = policy.responseHeaders();
const bodyKey = "httpcache:body:" + hashed;
const body = await cache.get(bodyKey);
console.debug("Got body", body.constructor.name, body.byteLength);
return new Response(body, { status: policy._status, headers });
// }else if(policy._headers){
// TODO: try a new vary based key
// policy._headers has the varies / vary values
// key = hashData(req, policy._headers)
// return undefined
}
else {
return undefined;
}
}
return undefined; // no matches found
},
async add(req) {
console.debug("cache add");
const res = await fetch(req);
return await storage.put(req, res);
},
async put(req, res, ttl) {
const resHeaders = {};
const key = hashData(req);
if (res.headers.get("vary")) {
console.warn("Vary headers are not supported in http-cache");
return false;
}
for (const [name, value] of res.headers) {
resHeaders[name] = value;
}
const cacheableRes = {
status: res.status,
headers: resHeaders
};
const policy = new CachePolicy({
url: req.url,
method: req.method,
headers: req.headers || {}
}, cacheableRes);
if (typeof ttl === "number") {
policy._rescc['max-age'] = ttl; // hack to make policy handle overridden ttl
console.warn("ttl:", ttl, "storable:", policy.storable());
}
ttl = typeof ttl === "number" ? ttl : Math.floor(policy.timeToLive() / 1000);
if (policy.storable() && ttl > 0) {
console.debug("Setting cache policy:", "httpcache:policy:" + key, ttl);
await cache.set("httpcache:policy:" + key, JSON.stringify(policy.toObject()), ttl);
const respBody = await res.arrayBuffer();
await cache.set("httpcache:body:" + key, respBody, ttl);
return true;
}
return false;
}
};
function hashData(req) {
let toHash = ``;
const u = normalizeURL(req.url);
toHash += u.toString();
toHash += req.method;
// TODO: cacheable cookies
// TODO: cache version for grand busting
console.debug("hashData", toHash);
return crypto.subtle.digestSync("sha-1", toHash, "hex");
}
function normalizeURL(u) {
const url = new URL(u);
url.hash = "";
const sp = url.searchParams;
sp.sort();
url.search = sp.toString();
return url;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaHR0cC1jYWNoZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9taWRkbGV3YXJlL2h0dHAtY2FjaGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7O0dBRUc7QUFDSCxPQUFPLEtBQUssTUFBTSwwQkFBMEIsQ0FBQztBQVU3Qzs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FpQkc7QUFDSCxNQUFNLFVBQVUsU0FBUyxDQUFDLEtBQW9CLEVBQUUsT0FBMEI7SUFDeEUsT0FBTyxLQUFLLFVBQVUsY0FBYyxDQUFDLEdBQWdCLEVBQUUsSUFBa0I7UUFDdkUsSUFBRyxDQUFDLE9BQU87WUFBRSxPQUFPLEdBQUcsRUFBRSxDQUFDO1FBQzFCLElBQUcsT0FBTyxHQUFHLEtBQUssUUFBUSxFQUFDO1lBQ3pCLEdBQUcsR0FBRyxJQUFJLE9BQU8sQ0FBQyxHQUFHLEVBQUUsSUFBSSxDQUFDLENBQUM7WUFDN0IsSUFBSSxHQUFHLFNBQVMsQ0FBQztTQUNsQjtRQUVELGtCQUFrQjtRQUNsQixJQUFJLFNBQVMsR0FBRyxJQUFJLENBQUM7UUFDckIsS0FBSSxNQUFNLENBQUMsSUFBSSxDQUFDLGVBQWUsRUFBRSxRQUFRLENBQUMsRUFBQztZQUN6QyxJQUFHLEdBQUcsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxFQUFDO2dCQUNwQixPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRywwQ0FBMEMsQ0FBQyxDQUFBO2dCQUM1RCxTQUFTLEdBQUcsS0FBSyxDQUFDO2FBQ25CO1NBQ0Y7UUFDRCxJQUFJLElBQUksR0FBRyxTQUFTLENBQUMsQ0FBQyxDQUFDLE1BQU0sT0FBTyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDO1FBRTVELElBQUcsSUFBSSxFQUFDO1lBQ04sWUFBWTtZQUNaLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLFdBQVcsRUFBRSxLQUFLLENBQUMsQ0FBQztZQUNyQyxP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QyxDQUFBO0lBQ3RCLEdBQUcsQ0FBQyxJQUFJLEdBQUcsRUFBRSxDQUFBO0lBQ2IsTUFBTSxFQUFFLEdBQUcsR0FBRyxDQUFDLFlBQVksQ0FBQTtJQUMzQixFQUFFLENBQUMsSUFBSSxFQUFFLENBQUE7SUFDVCxHQUFHLENBQUMsTUFBTSxHQUFHLEVBQUUsQ0FBQyxRQUFRLEVBQUUsQ0FBQTtJQUUxQixPQUFPLEdBQUcsQ0FBQTtBQUNaLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEBtb2R1bGUgTWlkZGxld2FyZVxuICovXG5pbXBvcnQgY2FjaGUgZnJvbSBcIkBmbHkvdjhlbnYvbGliL2ZseS9jYWNoZVwiO1xuaW1wb3J0IHsgRmV0Y2hGdW5jdGlvbiB9IGZyb20gXCIuLi9mZXRjaFwiO1xuXG4vKipcbiAqIEhUVFAgY2FjaGluZyBvcHRpb25zLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIEhUVFBDYWNoZU9wdGlvbnN7XG4gIC8qKiBPdmVycmlkZXMgdGhlIGNhY2hlIFRUTCBmb3IgYWxsIGNhY2hlYWJsZSByZXF1ZXN0cyAqL1xuICBvdmVycmlkZU1heEFnZT86IG51bWJlclxufVxuLyoqXG4gKiBDYWNoZSBIVFRQIHJlc3BvbnNlcyB3aXRoIGBjYWNoZS1jb250cm9sYCBoZWFkZXJzLlxuICogXG4gKiBCYXNpYyBleGFtcGxlOlxuICogYGBgdHlwZXNjcmlwdFxuICogaW1wb3J0IGh0dHBDYWNoZSBmcm9tIFwiLi9zcmMvbWlkZGxld2FyZS9odHRwLWNhY2hlXCI7XG4gKiBpbXBvcnQgYmFja2VuZHMgZnJvbSBcIi4vc3JjL2JhY2tlbmRzXCI7XG4gKiBcbiAqIGNvbnN0IGdsaXRjaCA9IGJhY2tlbmRzLmdsaXRjaChcImZseS1leGFtcGxlXCIpO1xuICogXG4gKiBjb25zdCBvcmlnaW4gPSBodHRwQ2FjaGUoZ2xpdGNoLCB7IG92ZXJyaWRlTWF4QWdlOiAzNjAwIH0pO1xuICogXG4gKiBmbHkuaHR0cC5yZXNwb25kV2l0aChvcmlnaW4pO1xuICogYGBgXG4gKiBcbiAqIEBwYXJhbSBmZXRjaCBcbiAqIEBwYXJhbSBvcHRpb25zIFxuICovXG5leHBvcnQgZnVuY3Rpb24gaHR0cENhY2hlKGZldGNoOiBGZXRjaEZ1bmN0aW9uLCBvcHRpb25zPzogSFRUUENhY2hlT3B0aW9ucyk6IEZldGNoRnVuY3Rpb257XG4gIHJldHVybiBhc3luYyBmdW5jdGlvbiBodHRwQ2FjaGVGZXRjaChyZXE6IFJlcXVlc3RJbmZvLCBpbml0PzogUmVxdWVzdEluaXQpOiBQcm9taXNlPFJlc3BvbnNlPntcbiAgICBpZighb3B0aW9ucykgb3B0aW9ucyA9IHt9O1xuICAgIGlmKHR5cGVvZiByZXEgPT09IFwic3RyaW5nXCIpe1xuICAgICAgcmVxID0gbmV3IFJlcXVlc3QocmVxLCBpbml0KTtcbiAgICAgIGluaXQgPSB1bmRlZmluZWQ7XG4gICAgfVxuXG4gICAgLy8gY2hlY2sgdGhlIGNhY2hlXG4gICAgbGV0IGNhY2hlYWJsZSA9IHRydWU7XG4gICAgZm9yKGNvbnN0IGggb2YgW1wiQXV0aG9yaXphdGlvblwiLCBcIkNvb2tpZVwiXSl7XG4gICAgICBpZihyZXEuaGVhZGVycy5nZXQoaCkpe1xuICAgICAgICBjb25zb2xlLndhcm4oaCArIFwiIGhlYWRlcnMgYXJlIG5vdCBzdXBwb3J0ZWQgaW4gaHR0cC1jYWNoZVwiKVxuICAgICAgICBjYWNoZWFibGUgPSBmYWxzZTtcbiAgICAgIH1cbiAgICB9XG4gICAgbGV0IHJlc3AgPSBjYWNoZWFibGUgPyBhd2FpdCBzdG9yYWdlLm1hdGNoKHJlcSkgOiB1bmRlZmluZWQ7XG5cbiAgICBpZihyZXNwKXtcbiAgICAgIC8vIGdvdCBhIGhpdFxuICAgICAgcmVzcC5oZWFkZXJzLnNldChcIkZseS1DYWNoZVwiLCBcImhpdFwiKTtcbiAgICAgIHJldHVybiByZXNwO1xuICAgIH1cblxuICAgIHJlc3AgPSBhd2FpdCBmZXRjaChyZXEsIGluaXQpO1xuXG4gICAgLy8gdGhpcyBzaG91bGQgZG8gbm90aGluZyBpZiB0aGUgcmVzcG9uc2UgY2FuJ3QgYmUgY2FjaGVkXG4gICAgY29uc3QgY2FjaGVIYXBwZW5lZCA9IGNhY2hlYWJsZSA/IGF3YWl0IHN0b3JhZ2UucHV0KHJlcSwgcmVzcCwgb3B0aW9ucy5vdmVycmlkZU1heEFnZSkgOiBmYWxzZTtcblxuICAgIGlmKGNhY2hlSGFwcGVuZWQpe1xuICAgICAgcmVzcC5oZWFkZXJzLnNldChcIkZseS1DYWNoZVwiLCBcIm1pc3NcIik7XG4gICAgfVxuICAgIHJldHVybiByZXNwO1xuICB9XG59XG5cbi8qKlxuICogQ29uZmlndXJhYmxlIEhUVFAgY2FjaGluZyBtaWRkbGV3YXJlLiBUaGlzIGlzIGV4dHJlbWVseSB1c2VmdWwgd2l0aGluIGEgYHBpcGVsaW5lYDpcbiAqIFxuICogYGBgdHlwZXNjcmlwdFxuICogY29uc3QgYXBwID0gcGlwZWxpbmUoXG4gKiAgICAgaHR0cHNVcGdyYWRlcixcbiAqICAgICBodHRwQ2FjaGluZy5jb25maWd1cmUoe292ZXJyaWRlTWF4QWdlOiAzNjAwfSksXG4gKiAgICAgZ2xpdGNoKFwiZmx5LWV4YW1wbGVcIilcbiAqIClcbiAqIFxuICovXG5odHRwQ2FjaGUuY29uZmlndXJlID0gKG9wdGlvbnM/OiBIVFRQQ2FjaGVPcHRpb25zKSA9PiB7XG4gIHJldHVybiAoZmV0Y2g6IEZldGNoRnVuY3Rpb24pID0+IGh0dHBDYWNoZShmZXRjaCwgb3B0aW9ucylcbn1cblxuLy8gY29waWVkIGZyb20gZmx5IHY4ZW52XG5jb25zdCBDYWNoZVBvbGljeSA9IHJlcXVpcmUoXCJodHRwLWNhY2hlLXNlbWFudGljc1wiKTtcbi8qKlxuICogZXhwb3J0OlxuICogXHRtYXRjaChyZXEpOiByZXMgfCBudWxsXG4gKiBcdGFkZChyZXEpOiB2b2lkXG4gKiBcdHB1dChyZXEsIHJlcyk6IHZvaWRcbiAqIEBwcml2YXRlXG4gKi9cblxuY29uc3Qgc3RvcmFnZSA9IHtcbiAgYXN5bmMgbWF0Y2gocmVxOiBSZXF1ZXN0KSB7XG4gICAgY29uc3QgaGFzaGVkID0gaGFzaERhdGEocmVxKVxuICAgIGNvbnN0IGtleSA9IFwiaHR0cGNhY2hlOnBvbGljeTpcIiArIGhhc2hlZCAvLyBmaXJzdCB0cnkgd2l0aCBubyB2YXJ5IHZhcmlhbnRcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IDU7IGkrKykge1xuICAgICAgY29uc3QgcG9saWN5UmF3ID0gYXdhaXQgY2FjaGUuZ2V0U3RyaW5nKGtleSlcbiAgICAgIGNvbnNvbGUuZGVidWcoXCJHb3QgcG9saWN5OlwiLCBrZXksIHBvbGljeVJhdylcbiAgICAgIGlmICghcG9saWN5UmF3KSB7XG4gICAgICAgIHJldHVybiB1bmRlZmluZWRcbiAgICAgIH1cbiAgICAgIGNvbnN0IHBvbGljeSA9IENhY2hlUG9saWN5LmZyb21PYmplY3QoSlNPTi5wYXJzZShwb2xpY3lSYXcpKVxuXG4gICAgICAvLyBpZiBpdCBmaXRzIGkgc2l0c1xuICAgICAgaWYgKHBvbGljeS5zYXRpc2ZpZXNXaXRob3V0UmV2YWxpZGF0aW9uKHJlcSkpIHtcbiAgICAgICAgY29uc3QgaGVhZGVycyA9IHBvbGljeS5yZXNwb25zZUhlYWRlcnMoKVxuICAgICAgICBjb25zdCBib2R5S2V5ID0gXCJodHRwY2FjaGU6Ym9keTpcIiArIGhhc2hlZFxuXG4gICAgICAgIGNvbnN0IGJvZHkgPSBhd2FpdCBjYWNoZS5nZXQoYm9keUtleSlcbiAgICAgICAgY29uc29sZS5kZWJ1ZyhcIkdvdCBib2R5XCIsIGJvZHkuY29uc3RydWN0b3IubmFtZSwgYm9keS5ieXRlTGVuZ3RoKVxuICAgICAgICByZXR1cm4gbmV3IFJlc3BvbnNlKGJvZHksIHsgc3RhdHVzOiBwb2xpY3kuX3N0YXR1cywgaGVhZGVycyB9KVxuICAgICAgICAvLyB9ZWxzZSBpZihwb2xpY3kuX2hlYWRlcnMpe1xuICAgICAgICAvLyBUT0RPOiB0cnkgYSBuZXcgdmFyeSBiYXNlZCBrZXlcbiAgICAgICAgLy8gcG9saWN5Ll9oZWFkZXJzIGhhcyB0aGUgdmFyaWVzIC8gdmFyeSB2YWx1ZXNcbiAgICAgICAgLy8ga2V5ID0gaGFzaERhdGEocmVxLCBwb2xpY3kuX2hlYWRlcnMpXG4gICAgICAgIC8vIHJldHVybiB1bmRlZmluZWRcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJldHVybiB1bmRlZmluZWRcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHVuZGVmaW5lZCAvLyBubyBtYXRjaGVzIGZvdW5kXG4gIH0sXG4gIGFzeW5jIGFkZChyZXE6IFJlcXVlc3QpIHtcbiAgICBjb25zb2xlLmRlYnVnKFwiY2FjaGUgYWRkXCIpXG5cbiAgICBjb25zdCByZXMgPSBhd2FpdCBmZXRjaChyZXEpXG4gICAgcmV0dXJuIGF3YWl0IHN0b3JhZ2UucHV0KHJlcSwgcmVzKVxuICB9LFxuICBhc3luYyBwdXQocmVxOiBSZXF1ZXN0LCByZXM6IFJlc3BvbnNlLCB0dGw/OiBudW1iZXIpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgICBjb25zdCByZXNIZWFkZXJzOiBhbnkgPSB7fVxuICAgIGNvbnN0IGtleSA9IGhhc2hEYXRhKHJlcSlcblxuICAgIGlmKHJlcy5oZWFkZXJzLmdldChcInZhcnlcIikpe1xuICAgICAgY29uc29sZS53YXJuKFwiVmFyeSBoZWFkZXJzIGFyZSBub3Qgc3VwcG9ydGVkIGluIGh0dHAtY2FjaGVcIilcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG5cbiAgICBmb3IgKGNvbnN0IFtuYW1lLCB2YWx1ZV0gb2YgKHJlcyBhcyBhbnkpLmhlYWRlcnMpIHtcbiAgICAgIHJlc0hlYWRlcnNbbmFtZV0gPSB2YWx1ZVxuICAgIH1cbiAgICBjb25zdCBjYWNoZWFibGVSZXMgPSB7XG4gICAgICBzdGF0dXM6IHJlcy5zdGF0dXMsXG4gICAgICBoZWFkZXJzOiByZXNIZWFkZXJzXG4gICAgfVxuICAgIGNvbnN0IHBvbGljeSA9IG5ldyBDYWNoZVBvbGljeShcbiAgICAgIHtcbiAgICAgICAgdXJsOiByZXEudXJsLFxuICAgICAgICBtZXRob2Q6IHJlcS5tZXRob2QsXG4gICAgICAgIGhlYWRlcnM6IHJlcS5oZWFkZXJzIHx8IHt9XG4gICAgICB9LFxuICAgICAgY2FjaGVhYmxlUmVzXG4gICAgKVxuXG4gICAgaWYodHlwZW9mIHR0bCA9PT0gXCJudW1iZXJcIil7XG4gICAgICBwb2xpY3kuX3Jlc2NjWydtYXgtYWdlJ10gPSB0dGw7IC8vIGhhY2sgdG8gbWFrZSBwb2xpY3kgaGFuZGxlIG92ZXJyaWRkZW4gdHRsXG4gICAgICBjb25zb2xlLndhcm4oXCJ0dGw6XCIsIHR0bCwgXCJzdG9yYWJsZTpcIiwgcG9saWN5LnN0b3JhYmxlKCkpO1xuICAgIH1cblxuICAgIHR0bCA9IHR5cGVvZiB0dGwgPT09IFwibnVtYmVyXCIgPyB0dGwgOiBNYXRoLmZsb29yKHBvbGljeS50aW1lVG9MaXZlKCkgLyAxMDAwKTtcbiAgICBpZiAocG9saWN5LnN0b3JhYmxlKCkgJiYgdHRsID4gMCkge1xuICAgICAgY29uc29sZS5kZWJ1ZyhcIlNldHRpbmcgY2FjaGUgcG9saWN5OlwiLCBcImh0dHBjYWNoZTpwb2xpY3k6XCIgKyBrZXksIHR0bClcbiAgICAgIGF3YWl0IGNhY2hlLnNldChcImh0dHBjYWNoZTpwb2xpY3k6XCIgKyBrZXksIEpTT04uc3RyaW5naWZ5KHBvbGljeS50b09iamVjdCgpKSwgdHRsKVxuICAgICAgY29uc3QgcmVzcEJvZHkgPSBhd2FpdCByZXMuYXJyYXlCdWZmZXIoKVxuICAgICAgYXdhaXQgY2FjaGUuc2V0KFwiaHR0cGNhY2hlOmJvZHk6XCIgKyBrZXksIHJlc3BCb2R5LCB0dGwpXG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG4gICAgcmV0dXJuIGZhbHNlO1xuICB9XG59XG5cbmZ1bmN0aW9uIGhhc2hEYXRhKHJlcTogUmVxdWVzdCkge1xuICBsZXQgdG9IYXNoID0gYGBcblxuICBjb25zdCB1ID0gbm9ybWFsaXplVVJMKHJlcS51cmwpXG5cbiAgdG9IYXNoICs9IHUudG9TdHJpbmcoKVxuICB0b0hhc2ggKz0gcmVxLm1ldGhvZFxuXG4gIC8vIFRPRE86IGNhY2hlYWJsZSBjb29raWVzXG4gIC8vIFRPRE86IGNhY2hlIHZlcnNpb24gZm9yIGdyYW5kIGJ1c3RpbmdcblxuICBjb25zb2xlLmRlYnVnKFwiaGFzaERhdGFcIiwgdG9IYXNoKVxuICByZXR1cm4gKGNyeXB0byBhcyBhbnkpLnN1YnRsZS5kaWdlc3RTeW5jKFwic2hhLTFcIiwgdG9IYXNoLCBcImhleFwiKVxufVxuXG5mdW5jdGlvbiBub3JtYWxpemVVUkwodTpzdHJpbmcpIHtcbiAgY29uc3QgdXJsID0gbmV3IFVSTCh1KVxuICB1cmwuaGFzaCA9IFwiXCJcbiAgY29uc3Qgc3AgPSB1cmwuc2VhcmNoUGFyYW1zXG4gIHNwLnNvcnQoKVxuICB1cmwuc2VhcmNoID0gc3AudG9TdHJpbmcoKVxuXG4gIHJldHVybiB1cmxcbn1cbiJdfQ==