LibHTTP: Implement the must-understand cache directive

This implements the must-understand response cache directive per RFC
9111 Section 5.2.2.3. When a response contains must-understand, this
cache now ignores the no-store directive for status codes whose
caching behavior it implements. For status codes the cache does not
understand, the response is not stored.
This commit is contained in:
Praise-Garfield 2026-02-14 12:54:37 +00:00 committed by Tim Flynn
parent bc93ba4530
commit 9b8e341828
3 changed files with 64 additions and 5 deletions

View file

@ -195,12 +195,25 @@ bool is_cacheable(u32 status_code, HeaderList const& headers)
// * if the response status code is 206 or 304, or the must-understand cache directive (see Section 5.2.2.3) is
// present: the cache understands the response status code;
//
// This cache implements the semantics of 206 and 304, so no check is needed here.
// FIXME: must-understand is not implemented.
// NB: This cache implements the semantics of 304 for revalidation. 206 is excluded above.
bool has_must_understand = cache_control.has_value() && contains_cache_control_directive(*cache_control, "must-understand"sv);
// * the no-store cache directive is not present in the response (see Section 5.2.2.5);
if (cache_control.has_value() && contains_cache_control_directive(*cache_control, "no-store"sv))
return false;
if (has_must_understand) {
if (!is_heuristically_cacheable_status(status_code) && status_code != 304)
return false;
// https://httpwg.org/specs/rfc9111.html#cache-response-directive.must-understand
// The must-understand response directive limits caching of the response to a cache that understands and conforms
// to the requirements for that response's status code.
//
// A response that contains the must-understand directive SHOULD also contain the no-store directive. When a cache
// that implements the must-understand directive receives a response that includes it, the cache SHOULD ignore the
// no-store directive if it understands and implements the status code's caching requirements.
} else {
// * the no-store cache directive is not present in the response (see Section 5.2.2.5);
if (cache_control.has_value() && contains_cache_control_directive(*cache_control, "no-store"sv))
return false;
}
// * if the cache is shared: the private response directive is either not present or allows a shared cache to store
// a modified response; see Section 5.2.2.7);

View file

@ -1,4 +1,5 @@
set(TEST_SOURCES
TestCacheUtilities.cpp
TestHTTPUtils.cpp
)

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2026, the Ladybird developers.
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibHTTP/Cache/Utilities.h>
#include <LibHTTP/HeaderList.h>
#include <LibTest/TestCase.h>
TEST_CASE(is_cacheable_must_understand_ignores_no_store_for_understood_status)
{
auto headers = HTTP::HeaderList::create({ { "Cache-Control", "must-understand, no-store, max-age=3600" } });
EXPECT(HTTP::is_cacheable(200, *headers));
}
TEST_CASE(is_cacheable_must_understand_rejects_unknown_status)
{
auto headers = HTTP::HeaderList::create({ { "Cache-Control", "must-understand, no-store, max-age=3600" } });
EXPECT(!HTTP::is_cacheable(202, *headers));
}
TEST_CASE(is_cacheable_no_store_without_must_understand)
{
auto headers = HTTP::HeaderList::create({ { "Cache-Control", "no-store, max-age=3600" } });
EXPECT(!HTTP::is_cacheable(200, *headers));
}
TEST_CASE(is_cacheable_must_understand_without_no_store_understood_status)
{
auto headers = HTTP::HeaderList::create({ { "Cache-Control", "must-understand, max-age=3600" } });
EXPECT(HTTP::is_cacheable(200, *headers));
}
TEST_CASE(is_cacheable_must_understand_without_no_store_unknown_status)
{
auto headers = HTTP::HeaderList::create({ { "Cache-Control", "must-understand, max-age=3600" } });
EXPECT(!HTTP::is_cacheable(299, *headers));
}
TEST_CASE(is_cacheable_must_understand_accepts_304_status)
{
auto headers = HTTP::HeaderList::create({ { "Cache-Control", "must-understand, no-store, max-age=3600" } });
EXPECT(HTTP::is_cacheable(304, *headers));
}