@@ -114,12 +114,6 @@ bool is_cacheable(u32 status_code, HTTP::HeaderMap const& headers)
114114 // - a cache extension that allows it to be cached (see Section 5.2.3); or
115115 // - a status code that is defined as heuristically cacheable (see Section 4.2.2).
116116
117- // FIXME: Implement cache revalidation.
118- if (cache_control->contains (" no-cache" sv, CaseSensitivity::CaseInsensitive))
119- return false ;
120- if (cache_control->contains (" revalidate" sv, CaseSensitivity::CaseInsensitive))
121- return false ;
122-
123117 return true ;
124118}
125119
@@ -216,10 +210,84 @@ AK::Duration calculate_age(HTTP::HeaderMap const& headers, UnixDateTime request_
216210 return AK::Duration::from_seconds (current_age);
217211}
218212
219- // https://httpwg.org/specs/rfc9111.html#expiration.model
220- bool is_response_fresh (AK::Duration freshness_lifetime, AK::Duration current_age)
213+ CacheLifetimeStatus cache_lifetime_status (HTTP::HeaderMap const & headers, AK::Duration freshness_lifetime, AK::Duration current_age)
214+ {
215+ auto revalidation_status = [&]() {
216+ // In order to revalidate a cache entry, we must have one of these headers to attach to the revalidation request.
217+ if (headers.contains (" Last-Modified" sv) || headers.contains (" ETag" sv))
218+ return CacheLifetimeStatus::MustRevalidate;
219+ return CacheLifetimeStatus::Expired;
220+ };
221+
222+ auto cache_control = headers.get (" Cache-Control" sv);
223+
224+ // https://httpwg.org/specs/rfc9111.html#cache-response-directive.no-cache
225+ // The no-cache response directive, in its unqualified form (without an argument), indicates that the response MUST
226+ // NOT be used to satisfy any other request without forwarding it for validation and receiving a successful response
227+ //
228+ // FIXME: Handle the qualified form of the no-cache directive, which may allow us to re-use the response.
229+ if (cache_control.has_value () && cache_control->contains (" no-cache" sv, CaseSensitivity::CaseInsensitive))
230+ return revalidation_status ();
231+
232+ // https://httpwg.org/specs/rfc9111.html#expiration.model
233+ if (freshness_lifetime > current_age)
234+ return CacheLifetimeStatus::Fresh;
235+
236+ if (cache_control.has_value ()) {
237+ // https://httpwg.org/specs/rfc9111.html#cache-response-directive.must-revalidate
238+ // The must-revalidate response directive indicates that once the response has become stale, a cache MUST NOT
239+ // reuse that response to satisfy another request until it has been successfully validated by the origin
240+ if (cache_control->contains (" must-revalidate" sv, CaseSensitivity::CaseInsensitive))
241+ return revalidation_status ();
242+
243+ // FIXME: Implement stale-while-revalidate.
244+ }
245+
246+ return CacheLifetimeStatus::Expired;
247+ }
248+
249+ // https://httpwg.org/specs/rfc9111.html#validation.sent
250+ RevalidationAttributes RevalidationAttributes::create (HTTP::HeaderMap const & headers)
251+ {
252+ RevalidationAttributes attributes;
253+ attributes.etag = headers.get (" ETag" sv).map ([](auto const & etag) { return etag; });
254+ attributes.last_modified = parse_http_date (headers.get (" Last-Modified" sv));
255+
256+ return attributes;
257+ }
258+
259+ // https://httpwg.org/specs/rfc9111.html#update
260+ void update_header_fields (HTTP::HeaderMap& stored_headers, HTTP::HeaderMap const & updated_headers)
221261{
222- return freshness_lifetime > current_age;
262+ // Caches are required to update a stored response's header fields from another (typically newer) response in
263+ // several situations; for example, see Sections 3.4, 4.3.4, and 4.3.5.
264+
265+ // When doing so, the cache MUST add each header field in the provided response to the stored response, replacing
266+ // field values that are already present, with the following exceptions:
267+ auto is_header_exempted_from_update = [](StringView name) {
268+ // * Header fields excepted from storage in Section 3.1,
269+ if (is_header_exempted_from_storage (name))
270+ return true ;
271+
272+ // * Header fields that the cache's stored response depends upon, as described below,
273+ // * Header fields that are automatically processed and removed by the recipient, as described below, and
274+
275+ // * The Content-Length header field.
276+ if (name.equals_ignoring_ascii_case (" Content-Type" sv))
277+ return true ;
278+
279+ return false ;
280+ };
281+
282+ for (auto const & updated_header : updated_headers.headers ()) {
283+ if (!is_header_exempted_from_update (updated_header.name ))
284+ stored_headers.remove (updated_header.name );
285+ }
286+
287+ for (auto const & updated_header : updated_headers.headers ()) {
288+ if (!is_header_exempted_from_update (updated_header.name ))
289+ stored_headers.set (updated_header.name , updated_header.value );
290+ }
223291}
224292
225293}
0 commit comments