1212#include < AK/Base64.h>
1313#include < AK/Debug.h>
1414#include < AK/ScopeGuard.h>
15+ #include < LibHTTP/Cache/Utilities.h>
1516#include < LibHTTP/Method.h>
1617#include < LibJS/Runtime/Completion.h>
1718#include < LibRequests/RequestTimingInfo.h>
@@ -1416,7 +1417,7 @@ GC::Ptr<PendingResponse> http_redirect_fetch(JS::Realm& realm, Infrastructure::F
14161417class CachePartition : public RefCounted <CachePartition> {
14171418public:
14181419 // https://httpwg.org/specs/rfc9111.html#constructing.responses.from.caches
1419- GC::Ptr<Infrastructure::Response> select_response (JS::Realm& realm, URL::URL const & url, StringView method, HTTP::HeaderList const & headers, Vector<GC::Ptr<Infrastructure::Response>>& initial_set_of_stored_responses ) const
1420+ GC::Ptr<Infrastructure::Response> select_response (JS::Realm& realm, URL::URL const & url, StringView method, HTTP::HeaderList const & headers) const
14201421 {
14211422 // When presented with a request, a cache MUST NOT reuse a stored response unless:
14221423
@@ -1439,8 +1440,6 @@ class CachePartition : public RefCounted<CachePartition> {
14391440
14401441 // FIXME: - the stored response does not contain the no-cache directive (Section 5.2.2.4), unless it is successfully validated (Section 4.3), and
14411442
1442- initial_set_of_stored_responses.append (*cached_response);
1443-
14441443 // FIXME: - the stored response is one of the following:
14451444 // + fresh (see Section 4.2), or
14461445 // + allowed to be served stale (see Section 4.2.4), or
@@ -1452,190 +1451,23 @@ class CachePartition : public RefCounted<CachePartition> {
14521451
14531452 void store_response (JS::Realm& realm, Infrastructure::Request const & http_request, Infrastructure::Response const & response)
14541453 {
1455- if (!is_cacheable (http_request, response))
1454+ if (!HTTP::is_cacheable (http_request.method ()))
1455+ return ;
1456+ if (!HTTP::is_cacheable (response.status (), response.header_list ()))
14561457 return ;
14571458
14581459 auto cached_response = Infrastructure::Response::create (realm.vm ());
14591460
1460- store_header_and_trailer_fields (response, * cached_response->header_list ());
1461+ HTTP:: store_header_and_trailer_fields (cached_response->header_list (), response. header_list ());
14611462 cached_response->set_body (response.body ()->clone (realm));
14621463 cached_response->set_body_info (response.body_info ());
14631464 cached_response->set_method (http_request.method ());
14641465 cached_response->set_status (response.status ());
14651466 cached_response->url_list ().append (http_request.current_url ());
1466- m_cache.set (http_request.current_url (), move (cached_response));
1467- }
1468-
1469- // https://httpwg.org/specs/rfc9111.html#freshening.responses
1470- void freshen_stored_responses_upon_validation (Infrastructure::Response const & response, Vector<GC::Ptr<Infrastructure::Response>>& initial_set_of_stored_responses)
1471- {
1472- // When a cache receives a 304 (Not Modified) response, it needs to identify stored
1473- // responses that are suitable for updating with the new information provided, and then do so.
1474-
1475- // The initial set of stored responses to update are those that could have been
1476- // chosen for that request — i.e., those that meet the requirements in Section 4,
1477- // except the last requirement to be fresh, able to be served stale, or just validated.
1478- for (auto stored_response : initial_set_of_stored_responses) {
1479- // Then, that initial set of stored responses is further filtered by the first match of:
1480-
1481- // - FIXME: If the new response contains one or more strong validators (see Section 8.8.1 of [HTTP]),
1482- // then each of those strong validators identifies a selected representation for update.
1483- // All the stored responses in the initial set with one of those same strong validators
1484- // are identified for update.
1485- // If none of the initial set contains at least one of the same strong validators,
1486- // then the cache MUST NOT use the new response to update any stored responses.
1487- // - FIXME: If the new response contains no strong validators but does contain one or more weak validators,
1488- // and those validators correspond to one of the initial set's stored responses,
1489- // then the most recent of those matching stored responses is identified for update.
1490- // - FIXME: If the new response does not include any form of validator (such as where a client generates an
1491- // `If-Modified-Since` request from a source other than the `Last-Modified` response header field),
1492- // and there is only one stored response in the initial set, and that stored response also lacks a validator,
1493- // then that stored response is identified for update.
1494-
1495- // For each stored response identified, the cache MUST update its header fields
1496- // with the header fields provided in the 304 (Not Modified) response, as per Section 3.2.
1497- update_stored_header_fields (response, stored_response->header_list ());
1498- }
1467+ m_cache.set (http_request.current_url (), cached_response);
14991468 }
15001469
15011470private:
1502- // https://httpwg.org/specs/rfc9111.html#storing.fields
1503- bool is_exempted_for_storage (StringView header_name)
1504- {
1505- // Caches MUST include all received response header fields — including unrecognized ones — when storing a response;
1506- // this assures that new HTTP header fields can be successfully deployed. However, the following exceptions are made:
1507-
1508- // - The Connection header field and fields whose names are listed in it are required by Section 7.6.1 of [HTTP]
1509- // to be removed before forwarding the message. This MAY be implemented by doing so before storage.
1510-
1511- // - Likewise, some fields' semantics require them to be removed before forwarding the message, and this MAY be
1512- // implemented by doing so before storage; see Section 7.6.1 of [HTTP] for some examples.
1513-
1514- // FIXME: - The no-cache (Section 5.2.2.4) and private (Section 5.2.2.7) cache directives can have arguments that
1515- // prevent storage of header fields by all caches and shared caches, respectively.
1516-
1517- // FIXME: - Header fields that are specific to the proxy that a cache uses when forwarding a request MUST NOT be stored,
1518- // unless the cache incorporates the identity of the proxy into the cache key.
1519- // Effectively, this is limited to Proxy-Authenticate (Section 11.7.1 of [HTTP]), Proxy-Authentication-Info (Section 11.7.3 of [HTTP]), and Proxy-Authorization (Section 11.7.2 of [HTTP]).
1520-
1521- return header_name.is_one_of_ignoring_ascii_case (
1522- " Connection" sv,
1523- " Proxy-Connection" sv,
1524- " Keep-Alive" sv,
1525- " TE" sv,
1526- " Transfer-Encoding" sv,
1527- " Upgrade" sv);
1528- }
1529-
1530- // https://httpwg.org/specs/rfc9111.html#update
1531- bool is_exempted_for_updating (StringView header_name)
1532- {
1533- // Caches are required to update a stored response's header fields from another
1534- // (typically newer) response in several situations; for example, see Sections 3.4, 4.3.4, and 4.3.5.
1535-
1536- // When doing so, the cache MUST add each header field in the provided response to the stored response,
1537- // replacing field values that are already present, with the following exceptions:
1538-
1539- // - Header fields excepted from storage in Section 3.1,
1540- return is_exempted_for_storage (header_name)
1541- // - Header fields that the cache's stored response depends upon, as described below,
1542- || false
1543- // - Header fields that are automatically processed and removed by the recipient, as described below, and
1544- || false
1545- // - The Content-Length header field.
1546- || header_name.equals_ignoring_ascii_case (" Content-Length" sv);
1547-
1548- // In some cases, caches (especially in user agents) store the results of processing
1549- // the received response, rather than the response itself, and updating header fields
1550- // that affect that processing can result in inconsistent behavior and security issues.
1551- // Caches in this situation MAY omit these header fields from updating stored responses
1552- // on an exceptional basis but SHOULD limit such omission to those fields necessary to
1553- // assure integrity of the stored response.
1554-
1555- // For example, a browser might decode the content coding of a response while it is being received,
1556- // creating a disconnect between the data it has stored and the response's original metadata.
1557- // Updating that stored metadata with a different Content-Encoding header field would be problematic.
1558- // Likewise, a browser might store a post-parse HTML tree rather than the content received in the response;
1559- // updating the Content-Type header field would not be workable in this case because any assumptions about
1560- // the format made in parsing would now be invalid.
1561-
1562- // Furthermore, some fields are automatically processed and removed by the HTTP implementation,
1563- // such as the Content-Range header field. Implementations MAY automatically omit such header fields from updates,
1564- // even when the processing does not actually occur.
1565-
1566- // Note that the Content-* prefix is not a signal that a header field is omitted from update; it is a convention for MIME header fields, not HTTP.
1567- }
1568-
1569- // https://httpwg.org/specs/rfc9111.html#update
1570- void update_stored_header_fields (Infrastructure::Response const & response, HTTP::HeaderList& headers)
1571- {
1572- for (auto const & header : *response.header_list ()) {
1573- if (!is_exempted_for_updating (header.name ))
1574- headers.delete_ (header.name );
1575- }
1576-
1577- for (auto const & header : *response.header_list ()) {
1578- if (!is_exempted_for_updating (header.name ))
1579- headers.append (header);
1580- }
1581- }
1582-
1583- // https://httpwg.org/specs/rfc9111.html#storing.fields
1584- void store_header_and_trailer_fields (Infrastructure::Response const & response, HTTP::HeaderList& headers)
1585- {
1586- for (auto const & header : *response.header_list ()) {
1587- if (!is_exempted_for_storage (header.name ))
1588- headers.append (header);
1589- }
1590- }
1591-
1592- // https://httpwg.org/specs/rfc9111.html#response.cacheability
1593- static bool is_cacheable (Infrastructure::Request const & request, Infrastructure::Response const & response)
1594- {
1595- // A cache MUST NOT store a response to a request unless:
1596-
1597- // - AD-HOC: For now, we simply don't cache responses without a simple ByteBuffer body.
1598- if (!response.body () || !response.body ()->source ().has <ByteBuffer>())
1599- return false ;
1600-
1601- // - the request method is understood by the cache;
1602- if (request.method () != " GET" sv && request.method () != " HEAD" sv)
1603- return false ;
1604-
1605- // - the response status code is final (see Section 15 of [HTTP]);
1606- if (response.status () < 200 )
1607- return false ;
1608-
1609- // - if the response status code is 206 or 304,
1610- // or the must-understand cache directive (see Section 5.2.2.3) is present:
1611- // the cache understands the response status code;
1612- if (response.status () == 206 || response.status () == 304 ) {
1613- // FIXME: Implement must-understand cache directive
1614- }
1615-
1616- // - the no-store cache directive is not present in the response (see Section 5.2.2.5);
1617- if (request.cache_mode () == Infrastructure::Request::CacheMode::NoStore)
1618- return false ;
1619-
1620- // FIXME: - if the cache is shared: the private response directive is either not present
1621- // or allows a shared cache to store a modified response; see Section 5.2.2.7);
1622-
1623- // FIXME: - if the cache is shared: the Authorization header field is not present in the
1624- // request (see Section 11.6.2 of [HTTP]) or a response directive is present
1625- // that explicitly allows shared caching (see Section 3.5); and
1626-
1627- // FIXME: - the response contains at least one of the following:
1628- // + a public response directive (see Section 5.2.2.9);
1629- // + a private response directive, if the cache is not shared (see Section 5.2.2.7);
1630- // + an Expires header field (see Section 5.3);
1631- // + a max-age response directive (see Section 5.2.2.1);
1632- // + if the cache is shared: an s-maxage response directive (see Section 5.2.2.10);
1633- // + a cache extension that allows it to be cached (see Section 5.2.3); or
1634- // + a status code that is defined as heuristically cacheable (see Section 4.2.2).
1635-
1636- return true ;
1637- }
1638-
16391471 HashMap<URL::URL, GC::Root<Infrastructure::Response>> m_cache;
16401472};
16411473
@@ -1702,7 +1534,6 @@ GC::Ref<PendingResponse> http_network_or_cache_fetch(JS::Realm& realm, Infrastru
17021534
17031535 // 5. Let storedResponse be null.
17041536 GC::Ptr<Infrastructure::Response> stored_response;
1705- GC::RootVector<GC::Ptr<Infrastructure::Response>> initial_set_of_stored_responses (realm.heap ());
17061537
17071538 // 6. Let httpCache be null.
17081539 // (Typeless until we actually implement it, needed for checks below)
@@ -1971,7 +1802,7 @@ GC::Ref<PendingResponse> http_network_or_cache_fetch(JS::Realm& realm, Infrastru
19711802 // validation, as per the "Constructing Responses from Caches" chapter of HTTP Caching [HTTP-CACHING],
19721803 // if any.
19731804 // NOTE: As mandated by HTTP, this still takes the `Vary` header into account.
1974- stored_response = http_cache->select_response (realm, http_request->current_url (), http_request->method (), *http_request->header_list (), initial_set_of_stored_responses );
1805+ stored_response = http_cache->select_response (realm, http_request->current_url (), http_request->method (), *http_request->header_list ());
19751806
19761807 // 2. If storedResponse is non-null, then:
19771808 if (stored_response) {
@@ -2056,7 +1887,7 @@ GC::Ref<PendingResponse> http_network_or_cache_fetch(JS::Realm& realm, Infrastru
20561887
20571888 auto returned_pending_response = PendingResponse::create (vm, request);
20581889
2059- pending_forward_response->when_loaded ([&realm, &vm, &fetch_params, request, response, stored_response, initial_set_of_stored_responses, http_request, returned_pending_response, is_authentication_fetch, is_new_connection_fetch, revalidating_flag, include_credentials, response_was_null = !response, http_cache](GC::Ref<Infrastructure::Response> resolved_forward_response) mutable {
1890+ pending_forward_response->when_loaded ([&realm, &vm, &fetch_params, request, response, stored_response, http_request, returned_pending_response, is_authentication_fetch, is_new_connection_fetch, revalidating_flag, include_credentials, response_was_null = !response, http_cache](GC::Ref<Infrastructure::Response> resolved_forward_response) mutable {
20601891 dbgln_if (WEB_FETCH_DEBUG, " Fetch: Running 'HTTP-network-or-cache fetch' pending_forward_response load callback" );
20611892 if (response_was_null) {
20621893 auto forward_response = resolved_forward_response;
@@ -2079,7 +1910,7 @@ GC::Ref<PendingResponse> http_network_or_cache_fetch(JS::Realm& realm, Infrastru
20791910 // 1. Update storedResponse’s header list using forwardResponse’s header list, as per the "Freshening
20801911 // Stored Responses upon Validation" chapter of HTTP Caching.
20811912 // NOTE: This updates the stored response in cache as well.
2082- http_cache-> freshen_stored_responses_upon_validation (*forward_response, initial_set_of_stored_responses );
1913+ HTTP::update_header_fields (stored_response-> header_list (), forward_response-> header_list () );
20831914
20841915 // 2. Set response to storedResponse.
20851916 response = stored_response;
0 commit comments