* Some resources are cached without a hash, meaning that their expiration is controlled * by HTTP caching headers. Check whether the given request/response pair is still valid * per the caching headers.
(req: Request, res: Response)
| 181 | * per the caching headers. |
| 182 | */ |
| 183 | private async needToRevalidate(req: Request, res: Response): Promise<boolean> { |
| 184 | // Three different strategies apply here: |
| 185 | // 1) The request has a Cache-Control header, and thus expiration needs to be based on its age. |
| 186 | // 2) The request has an Expires header, and expiration is based on the current timestamp. |
| 187 | // 3) The request has no applicable caching headers, and must be revalidated. |
| 188 | if (res.headers.has('Cache-Control')) { |
| 189 | // Figure out if there is a max-age directive in the Cache-Control header. |
| 190 | const cacheControl = res.headers.get('Cache-Control')!; |
| 191 | const cacheDirectives = cacheControl |
| 192 | // Directives are comma-separated within the Cache-Control header value. |
| 193 | .split(',') |
| 194 | // Make sure each directive doesn't have extraneous whitespace. |
| 195 | .map((v) => v.trim()) |
| 196 | // Some directives have values (like maxage and s-maxage) |
| 197 | .map((v) => v.split('=')); |
| 198 | |
| 199 | // Lowercase all the directive names. |
| 200 | cacheDirectives.forEach((v) => (v[0] = v[0].toLowerCase())); |
| 201 | |
| 202 | // Find the max-age directive, if one exists. |
| 203 | const maxAgeDirective = cacheDirectives.find((v) => v[0] === 'max-age'); |
| 204 | const cacheAge = maxAgeDirective ? maxAgeDirective[1] : undefined; |
| 205 | |
| 206 | if (!cacheAge) { |
| 207 | // No usable TTL defined. Must assume that the response is stale. |
| 208 | return true; |
| 209 | } |
| 210 | try { |
| 211 | const maxAge = 1000 * parseInt(cacheAge); |
| 212 | |
| 213 | // Determine the origin time of this request. If the SW has metadata on the request (which |
| 214 | // it |
| 215 | // should), it will have the time the request was added to the cache. If it doesn't for some |
| 216 | // reason, the request may have a Date header which will serve the same purpose. |
| 217 | let ts: number; |
| 218 | try { |
| 219 | // Check the metadata table. If a timestamp is there, use it. |
| 220 | const metaTable = await this.metadata; |
| 221 | ts = (await metaTable.read<UrlMetadata>(req.url)).ts; |
| 222 | } catch { |
| 223 | // Otherwise, look for a Date header. |
| 224 | const date = res.headers.get('Date'); |
| 225 | if (date === null) { |
| 226 | // Unable to determine when this response was created. Assume that it's stale, and |
| 227 | // revalidate it. |
| 228 | return true; |
| 229 | } |
| 230 | ts = Date.parse(date); |
| 231 | } |
| 232 | const age = this.adapter.time - ts; |
| 233 | return age < 0 || age > maxAge; |
| 234 | } catch { |
| 235 | // Assume stale. |
| 236 | return true; |
| 237 | } |
| 238 | } else if (res.headers.has('Expires')) { |
| 239 | // Determine if the expiration time has passed. |
| 240 | const expiresStr = res.headers.get('Expires')!; |