120 lines
4.9 KiB
C++
120 lines
4.9 KiB
C++
|
|
/*
|
||
|
|
* Copyright (c) 2026-present, the Ladybird developers.
|
||
|
|
*
|
||
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
||
|
|
*/
|
||
|
|
|
||
|
|
#include <LibCore/ImmutableBytes.h>
|
||
|
|
#include <LibHTTP/Cache/MemoryCache.h>
|
||
|
|
#include <LibHTTP/Cache/Utilities.h>
|
||
|
|
#include <LibHTTP/HeaderList.h>
|
||
|
|
#include <LibTest/TestCase.h>
|
||
|
|
#include <LibURL/Parser.h>
|
||
|
|
|
||
|
|
static URL::URL parse_url(StringView url)
|
||
|
|
{
|
||
|
|
return URL::Parser::basic_parse(url).release_value();
|
||
|
|
}
|
||
|
|
|
||
|
|
static NonnullRefPtr<HTTP::HeaderList> create_cacheable_request_headers()
|
||
|
|
{
|
||
|
|
return HTTP::HeaderList::create({
|
||
|
|
{ "Accept"sv, "*/*"sv },
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
static NonnullRefPtr<HTTP::HeaderList> create_cacheable_response_headers()
|
||
|
|
{
|
||
|
|
return HTTP::HeaderList::create({
|
||
|
|
{ "Cache-Control"sv, "max-age=60"sv },
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
static NonnullRefPtr<HTTP::HeaderList> create_vary_user_agent_response_headers()
|
||
|
|
{
|
||
|
|
return HTTP::HeaderList::create({
|
||
|
|
{ "Cache-Control"sv, "max-age=60"sv },
|
||
|
|
{ "Vary"sv, "User-Agent"sv },
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
static Core::ImmutableBytes immutable_bytes(StringView bytes)
|
||
|
|
{
|
||
|
|
return MUST(Core::ImmutableBytes::copy(bytes.bytes()));
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_CASE(javascript_bytecode_cache_round_trips_with_memory_cache_entry)
|
||
|
|
{
|
||
|
|
auto cache = HTTP::MemoryCache::create();
|
||
|
|
auto url = parse_url("https://example.com/script.js"sv);
|
||
|
|
auto request_headers = create_cacheable_request_headers();
|
||
|
|
auto response_headers = create_cacheable_response_headers();
|
||
|
|
auto bytecode = immutable_bytes("cached bytecode"sv);
|
||
|
|
|
||
|
|
cache->create_entry(url, "GET"sv, *request_headers, UnixDateTime::now(), 200, "OK"sv, *response_headers, bytecode, 0);
|
||
|
|
cache->finalize_entry(url, "GET"sv, *request_headers, 200, *response_headers, immutable_bytes("console.log('hello');"sv));
|
||
|
|
|
||
|
|
auto entry = cache->open_entry(url, "GET"sv, *request_headers, HTTP::CacheMode::Default);
|
||
|
|
VERIFY(entry.has_value());
|
||
|
|
VERIFY(entry->javascript_bytecode_cache.has_value());
|
||
|
|
EXPECT_EQ(entry->javascript_bytecode_cache->bytes(), bytecode.bytes());
|
||
|
|
EXPECT_EQ(entry->javascript_bytecode_cache_vary_key, Optional<u64> { 0 });
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_CASE(javascript_bytecode_cache_update_matches_memory_cache_vary_headers)
|
||
|
|
{
|
||
|
|
auto cache = HTTP::MemoryCache::create();
|
||
|
|
auto url = parse_url("https://example.com/script.js"sv);
|
||
|
|
auto script_request_headers = create_cacheable_request_headers();
|
||
|
|
auto cache_request_headers = HTTP::HeaderList::create({
|
||
|
|
{ "Accept"sv, "*/*"sv },
|
||
|
|
{ "User-Agent"sv, "Ladybird"sv },
|
||
|
|
});
|
||
|
|
auto response_headers = create_vary_user_agent_response_headers();
|
||
|
|
auto bytecode = immutable_bytes("generated bytecode"sv);
|
||
|
|
auto vary_key = HTTP::create_vary_key(*cache_request_headers, *response_headers);
|
||
|
|
|
||
|
|
cache->create_entry(url, "GET"sv, *cache_request_headers, UnixDateTime::now(), 200, "OK"sv, *response_headers);
|
||
|
|
cache->finalize_entry(url, "GET"sv, *cache_request_headers, 200, *response_headers, immutable_bytes("console.log('hello');"sv));
|
||
|
|
|
||
|
|
cache->update_javascript_bytecode_cache(url, "GET"sv, *script_request_headers, vary_key, bytecode);
|
||
|
|
|
||
|
|
auto entry = cache->open_entry(url, "GET"sv, *cache_request_headers, HTTP::CacheMode::Default);
|
||
|
|
VERIFY(entry.has_value());
|
||
|
|
EXPECT(!entry->javascript_bytecode_cache.has_value());
|
||
|
|
EXPECT(!entry->javascript_bytecode_cache_vary_key.has_value());
|
||
|
|
|
||
|
|
cache->update_javascript_bytecode_cache(url, "GET"sv, *cache_request_headers, vary_key, bytecode);
|
||
|
|
|
||
|
|
entry = cache->open_entry(url, "GET"sv, *cache_request_headers, HTTP::CacheMode::Default);
|
||
|
|
VERIFY(entry.has_value());
|
||
|
|
VERIFY(entry->javascript_bytecode_cache.has_value());
|
||
|
|
EXPECT_EQ(entry->javascript_bytecode_cache->bytes(), bytecode.bytes());
|
||
|
|
EXPECT_EQ(entry->javascript_bytecode_cache_vary_key, Optional<u64> { vary_key });
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST_CASE(javascript_bytecode_cache_can_be_added_after_memory_cache_entry_is_complete)
|
||
|
|
{
|
||
|
|
auto cache = HTTP::MemoryCache::create();
|
||
|
|
auto url = parse_url("https://example.com/script.js"sv);
|
||
|
|
auto request_headers = create_cacheable_request_headers();
|
||
|
|
auto response_headers = create_cacheable_response_headers();
|
||
|
|
auto bytecode = immutable_bytes("generated bytecode"sv);
|
||
|
|
|
||
|
|
cache->create_entry(url, "GET"sv, *request_headers, UnixDateTime::now(), 200, "OK"sv, *response_headers);
|
||
|
|
cache->finalize_entry(url, "GET"sv, *request_headers, 200, *response_headers, immutable_bytes("console.log('hello');"sv));
|
||
|
|
|
||
|
|
auto entry = cache->open_entry(url, "GET"sv, *request_headers, HTTP::CacheMode::Default);
|
||
|
|
VERIFY(entry.has_value());
|
||
|
|
EXPECT(!entry->javascript_bytecode_cache.has_value());
|
||
|
|
EXPECT(!entry->javascript_bytecode_cache_vary_key.has_value());
|
||
|
|
|
||
|
|
cache->update_javascript_bytecode_cache(url, "GET"sv, *request_headers, 0, bytecode);
|
||
|
|
|
||
|
|
entry = cache->open_entry(url, "GET"sv, *request_headers, HTTP::CacheMode::Default);
|
||
|
|
VERIFY(entry.has_value());
|
||
|
|
VERIFY(entry->javascript_bytecode_cache.has_value());
|
||
|
|
EXPECT_EQ(entry->javascript_bytecode_cache->bytes(), bytecode.bytes());
|
||
|
|
EXPECT_EQ(entry->javascript_bytecode_cache_vary_key, Optional<u64> { 0 });
|
||
|
|
}
|