* Process a request for a given resource and return it, or return null if it's not available.
(req: Request, _event: ExtendableEvent)
| 119 | * Process a request for a given resource and return it, or return null if it's not available. |
| 120 | */ |
| 121 | async handleFetch(req: Request, _event: ExtendableEvent): Promise<Response | null> { |
| 122 | const url = this.adapter.normalizeUrl(req.url); |
| 123 | // Either the request matches one of the known resource URLs, one of the patterns for |
| 124 | // dynamically matched URLs, or neither. Determine which is the case for this request in |
| 125 | // order to decide how to handle it. |
| 126 | if (this.urls.indexOf(url) !== -1 || this.patterns.some((pattern) => pattern.test(url))) { |
| 127 | // This URL matches a known resource. Either it's been cached already or it's missing, in |
| 128 | // which case it needs to be loaded from the network. |
| 129 | |
| 130 | // Open the cache to check whether this resource is present. |
| 131 | const cache = await this.cache; |
| 132 | |
| 133 | // Look for a cached response. If one exists, it can be used to resolve the fetch |
| 134 | // operation. |
| 135 | let cachedResponse: Response | undefined; |
| 136 | try { |
| 137 | // Safari 16.4/17 is known to sometimes throw an unexpected internal error on cache access |
| 138 | // This try/catch is here as a workaround to prevent a failure of the handleFetch |
| 139 | // as the Driver falls back to safeFetch on critical errors. |
| 140 | // See #50378 |
| 141 | cachedResponse = await cache.match(req, this.config.cacheQueryOptions); |
| 142 | } catch (error) { |
| 143 | throw new SwCriticalError(`Cache is throwing while looking for a match: ${error}`); |
| 144 | } |
| 145 | |
| 146 | if (cachedResponse !== undefined) { |
| 147 | // A response has already been cached (which presumably matches the hash for this |
| 148 | // resource). Check whether it's safe to serve this resource from cache. |
| 149 | if (this.hashes.has(url)) { |
| 150 | // This resource has a hash, and thus is versioned by the manifest. It's safe to return |
| 151 | // the response. |
| 152 | return cachedResponse; |
| 153 | } else { |
| 154 | // This resource has no hash, and yet exists in the cache. Check how old this request is |
| 155 | // to make sure it's still usable. |
| 156 | if (await this.needToRevalidate(req, cachedResponse)) { |
| 157 | this.idle.schedule(`revalidate(${cache.name}): ${req.url}`, async () => { |
| 158 | await this.fetchAndCacheOnce(req); |
| 159 | }); |
| 160 | } |
| 161 | |
| 162 | // In either case (revalidation or not), the cached response must be good. |
| 163 | return cachedResponse; |
| 164 | } |
| 165 | } |
| 166 | |
| 167 | // No already-cached response exists, so attempt a fetch/cache operation. |
| 168 | const res = await this.fetchAndCacheOnce(this.newRequestWithMetadata(req.url, req)); |
| 169 | |
| 170 | // If this is successful, the response needs to be cloned as it might be used to respond to |
| 171 | // multiple fetch operations at the same time. |
| 172 | return res.clone(); |
| 173 | } else { |
| 174 | return null; |
| 175 | } |
| 176 | } |
| 177 | |
| 178 | /** |
nothing calls this directly
no test coverage detected
searching dependent graphs…