From 21bbbacd078ece144e4bf436bde7187edfbc23a6 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Fri, 28 Nov 2025 10:04:59 -0500 Subject: [PATCH] LibHTTP+RequestServer: Move the HTTP cache implementation to LibHTTP We currently have two ongoing implementations of RFC 9111, HTTP caching. In order to consolidate these, this patch moves the implementation from RequestServer to LibHTTP for re-use within LibWeb. --- Libraries/LibHTTP/CMakeLists.txt | 6 +- .../LibHTTP}/Cache/CacheEntry.cpp | 21 ++-- .../LibHTTP}/Cache/CacheEntry.h | 23 ++--- .../LibHTTP}/Cache/CacheIndex.cpp | 56 +++++------ .../LibHTTP}/Cache/CacheIndex.h | 13 +-- Libraries/LibHTTP/Cache/CacheRequest.h | 35 +++++++ .../LibHTTP}/Cache/DiskCache.cpp | 71 ++++++++------ .../LibHTTP}/Cache/DiskCache.h | 21 ++-- .../LibHTTP}/Cache/Utilities.cpp | 22 ++--- .../LibHTTP}/Cache/Utilities.h | 18 ++-- .../LibHTTP}/Cache/Version.h | 2 +- Libraries/LibHTTP/Forward.h | 6 ++ Services/RequestServer/CMakeLists.txt | 6 +- .../RequestServer/ConnectionFromClient.cpp | 4 +- Services/RequestServer/Forward.h | 5 - Services/RequestServer/Request.cpp | 96 +++++++++---------- Services/RequestServer/Request.h | 28 ++---- Services/RequestServer/main.cpp | 10 +- 18 files changed, 240 insertions(+), 203 deletions(-) rename {Services/RequestServer => Libraries/LibHTTP}/Cache/CacheEntry.cpp (95%) rename {Services/RequestServer => Libraries/LibHTTP}/Cache/CacheEntry.h (85%) rename {Services/RequestServer => Libraries/LibHTTP}/Cache/CacheIndex.cpp (76%) rename {Services/RequestServer => Libraries/LibHTTP}/Cache/CacheIndex.h (84%) create mode 100644 Libraries/LibHTTP/Cache/CacheRequest.h rename {Services/RequestServer => Libraries/LibHTTP}/Cache/DiskCache.cpp (72%) rename {Services/RequestServer => Libraries/LibHTTP}/Cache/DiskCache.h (71%) rename {Services/RequestServer => Libraries/LibHTTP}/Cache/Utilities.cpp (94%) rename {Services/RequestServer => Libraries/LibHTTP}/Cache/Utilities.h (60%) rename {Services/RequestServer => Libraries/LibHTTP}/Cache/Version.h (92%) diff --git a/Libraries/LibHTTP/CMakeLists.txt b/Libraries/LibHTTP/CMakeLists.txt index 32178819653..596ec225254 100644 --- a/Libraries/LibHTTP/CMakeLists.txt +++ b/Libraries/LibHTTP/CMakeLists.txt @@ -1,4 +1,8 @@ set(SOURCES + Cache/CacheEntry.cpp + Cache/CacheIndex.cpp + Cache/DiskCache.cpp + Cache/Utilities.cpp Header.cpp HeaderList.cpp HTTP.cpp @@ -7,4 +11,4 @@ set(SOURCES ) ladybird_lib(LibHTTP http) -target_link_libraries(LibHTTP PRIVATE LibCompress LibCore LibIPC LibRegex LibTextCodec LibTLS LibURL) +target_link_libraries(LibHTTP PRIVATE LibCompress LibCore LibCrypto LibDatabase LibFileSystem LibIPC LibRegex LibTextCodec LibTLS LibURL) diff --git a/Services/RequestServer/Cache/CacheEntry.cpp b/Libraries/LibHTTP/Cache/CacheEntry.cpp similarity index 95% rename from Services/RequestServer/Cache/CacheEntry.cpp rename to Libraries/LibHTTP/Cache/CacheEntry.cpp index cf142ad102f..25b9f2a549c 100644 --- a/Services/RequestServer/Cache/CacheEntry.cpp +++ b/Libraries/LibHTTP/Cache/CacheEntry.cpp @@ -6,15 +6,14 @@ #include #include -#include #include #include -#include -#include -#include -#include +#include +#include +#include +#include -namespace RequestServer { +namespace HTTP { ErrorOr CacheHeader::read_from_stream(Stream& stream) { @@ -117,7 +116,7 @@ CacheEntryWriter::CacheEntryWriter(DiskCache& disk_cache, CacheIndex& index, u64 { } -ErrorOr CacheEntryWriter::write_status_and_reason(u32 status_code, Optional reason_phrase, HTTP::HeaderList const& response_headers) +ErrorOr CacheEntryWriter::write_status_and_reason(u32 status_code, Optional reason_phrase, HeaderList const& response_headers) { if (m_marked_for_deletion) { close_and_destroy_cache_entry(); @@ -183,7 +182,7 @@ ErrorOr CacheEntryWriter::write_data(ReadonlyBytes data) return {}; } -ErrorOr CacheEntryWriter::flush(NonnullRefPtr response_headers) +ErrorOr CacheEntryWriter::flush(NonnullRefPtr response_headers) { ScopeGuard guard { [&]() { close_and_destroy_cache_entry(); } }; @@ -205,7 +204,7 @@ ErrorOr CacheEntryWriter::flush(NonnullRefPtr response_h return {}; } -ErrorOr> CacheEntryReader::create(DiskCache& disk_cache, CacheIndex& index, u64 cache_key, NonnullRefPtr response_headers, u64 data_size) +ErrorOr> CacheEntryReader::create(DiskCache& disk_cache, CacheIndex& index, u64 cache_key, NonnullRefPtr response_headers, u64 data_size) { auto path = path_for_cache_key(disk_cache.cache_directory(), cache_key); @@ -253,7 +252,7 @@ ErrorOr> CacheEntryReader::create(DiskCache& dis return adopt_own(*new CacheEntryReader { disk_cache, index, cache_key, move(url), move(path), move(file), fd, cache_header, move(reason_phrase), move(response_headers), data_offset, data_size }); } -CacheEntryReader::CacheEntryReader(DiskCache& disk_cache, CacheIndex& index, u64 cache_key, String url, LexicalPath path, NonnullOwnPtr file, int fd, CacheHeader cache_header, Optional reason_phrase, NonnullRefPtr response_headers, u64 data_offset, u64 data_size) +CacheEntryReader::CacheEntryReader(DiskCache& disk_cache, CacheIndex& index, u64 cache_key, String url, LexicalPath path, NonnullOwnPtr file, int fd, CacheHeader cache_header, Optional reason_phrase, NonnullRefPtr response_headers, u64 data_offset, u64 data_size) : CacheEntry(disk_cache, index, cache_key, move(url), move(path), cache_header) , m_file(move(file)) , m_fd(fd) @@ -264,7 +263,7 @@ CacheEntryReader::CacheEntryReader(DiskCache& disk_cache, CacheIndex& index, u64 { } -void CacheEntryReader::revalidation_succeeded(HTTP::HeaderList const& response_headers) +void CacheEntryReader::revalidation_succeeded(HeaderList const& response_headers) { dbgln("\033[34;1mCache revalidation succeeded for\033[0m {}", m_url); diff --git a/Services/RequestServer/Cache/CacheEntry.h b/Libraries/LibHTTP/Cache/CacheEntry.h similarity index 85% rename from Services/RequestServer/Cache/CacheEntry.h rename to Libraries/LibHTTP/Cache/CacheEntry.h index 8c8084d2199..ced69a2c0ef 100644 --- a/Services/RequestServer/Cache/CacheEntry.h +++ b/Libraries/LibHTTP/Cache/CacheEntry.h @@ -13,11 +13,12 @@ #include #include #include +#include +#include +#include #include -#include -#include -namespace RequestServer { +namespace HTTP { struct CacheHeader { static ErrorOr read_from_stream(Stream&); @@ -87,9 +88,9 @@ public: static ErrorOr> create(DiskCache&, CacheIndex&, u64 cache_key, String url, UnixDateTime request_time, AK::Duration current_time_offset_for_testing); virtual ~CacheEntryWriter() override = default; - ErrorOr write_status_and_reason(u32 status_code, Optional reason_phrase, HTTP::HeaderList const&); + ErrorOr write_status_and_reason(u32 status_code, Optional reason_phrase, HeaderList const&); ErrorOr write_data(ReadonlyBytes); - ErrorOr flush(NonnullRefPtr); + ErrorOr flush(NonnullRefPtr); private: CacheEntryWriter(DiskCache&, CacheIndex&, u64 cache_key, String url, LexicalPath, NonnullOwnPtr, CacheHeader, UnixDateTime request_time, AK::Duration current_time_offset_for_testing); @@ -104,24 +105,24 @@ private: class CacheEntryReader : public CacheEntry { public: - static ErrorOr> create(DiskCache&, CacheIndex&, u64 cache_key, NonnullRefPtr, u64 data_size); + static ErrorOr> create(DiskCache&, CacheIndex&, u64 cache_key, NonnullRefPtr, u64 data_size); virtual ~CacheEntryReader() override = default; bool must_revalidate() const { return m_must_revalidate; } void set_must_revalidate() { m_must_revalidate = true; } - void revalidation_succeeded(HTTP::HeaderList const&); + void revalidation_succeeded(HeaderList const&); void revalidation_failed(); void pipe_to(int pipe_fd, Function on_complete, Function on_error); u32 status_code() const { return m_cache_header.status_code; } Optional const& reason_phrase() const { return m_reason_phrase; } - HTTP::HeaderList& response_headers() { return m_response_headers; } - HTTP::HeaderList const& response_headers() const { return m_response_headers; } + HeaderList& response_headers() { return m_response_headers; } + HeaderList const& response_headers() const { return m_response_headers; } private: - CacheEntryReader(DiskCache&, CacheIndex&, u64 cache_key, String url, LexicalPath, NonnullOwnPtr, int fd, CacheHeader, Optional reason_phrase, NonnullRefPtr, u64 data_offset, u64 data_size); + CacheEntryReader(DiskCache&, CacheIndex&, u64 cache_key, String url, LexicalPath, NonnullOwnPtr, int fd, CacheHeader, Optional reason_phrase, NonnullRefPtr, u64 data_offset, u64 data_size); void pipe_without_blocking(); void pipe_complete(); @@ -140,7 +141,7 @@ private: u64 m_bytes_piped { 0 }; Optional m_reason_phrase; - NonnullRefPtr m_response_headers; + NonnullRefPtr m_response_headers; bool m_must_revalidate { false }; diff --git a/Services/RequestServer/Cache/CacheIndex.cpp b/Libraries/LibHTTP/Cache/CacheIndex.cpp similarity index 76% rename from Services/RequestServer/Cache/CacheIndex.cpp rename to Libraries/LibHTTP/Cache/CacheIndex.cpp index 79c0c2aa14e..57b31c3ed6b 100644 --- a/Services/RequestServer/Cache/CacheIndex.cpp +++ b/Libraries/LibHTTP/Cache/CacheIndex.cpp @@ -5,15 +5,15 @@ */ #include -#include -#include -#include +#include +#include +#include -namespace RequestServer { +namespace HTTP { static constexpr u32 CACHE_METADATA_KEY = 12389u; -static ByteString serialize_headers(HTTP::HeaderList const& headers) +static ByteString serialize_headers(HeaderList const& headers) { StringBuilder builder; @@ -27,9 +27,9 @@ static ByteString serialize_headers(HTTP::HeaderList const& headers) return builder.to_byte_string(); } -static NonnullRefPtr deserialize_headers(StringView serialized_headers) +static NonnullRefPtr deserialize_headers(StringView serialized_headers) { - auto headers = HTTP::HeaderList::create(); + auto headers = HeaderList::create(); serialized_headers.for_each_split_view('\n', SplitBehavior::Nothing, [&](StringView serialized_header) { auto index = serialized_header.find(':'); @@ -110,7 +110,7 @@ CacheIndex::CacheIndex(Database::Database& database, Statements statements) { } -void CacheIndex::create_entry(u64 cache_key, String url, NonnullRefPtr response_headers, u64 data_size, UnixDateTime request_time, UnixDateTime response_time) +void CacheIndex::create_entry(u64 cache_key, String url, NonnullRefPtr response_headers, u64 data_size, UnixDateTime request_time, UnixDateTime response_time) { auto now = UnixDateTime::now(); @@ -133,22 +133,22 @@ void CacheIndex::create_entry(u64 cache_key, String url, NonnullRefPtrexecute_statement(m_statements.insert_entry, {}, entry.cache_key, entry.url, serialize_headers(entry.response_headers), entry.data_size, entry.request_time, entry.response_time, entry.last_access_time); m_entries.set(cache_key, move(entry)); } void CacheIndex::remove_entry(u64 cache_key) { - m_database.execute_statement(m_statements.remove_entry, {}, cache_key); + m_database->execute_statement(m_statements.remove_entry, {}, cache_key); m_entries.remove(cache_key); } void CacheIndex::remove_entries_accessed_since(UnixDateTime since, Function on_entry_removed) { - m_database.execute_statement( + m_database->execute_statement( m_statements.remove_entries_accessed_since, [&](auto statement_id) { - auto cache_key = m_database.result_column(statement_id, 0); + auto cache_key = m_database->result_column(statement_id, 0); m_entries.remove(cache_key); on_entry_removed(cache_key); @@ -156,13 +156,13 @@ void CacheIndex::remove_entries_accessed_since(UnixDateTime since, Function response_headers) +void CacheIndex::update_response_headers(u64 cache_key, NonnullRefPtr response_headers) { auto entry = m_entries.get(cache_key); if (!entry.has_value()) return; - m_database.execute_statement(m_statements.update_response_headers, {}, serialize_headers(response_headers), cache_key); + m_database->execute_statement(m_statements.update_response_headers, {}, serialize_headers(response_headers), cache_key); entry->response_headers = move(response_headers); } @@ -174,7 +174,7 @@ void CacheIndex::update_last_access_time(u64 cache_key) auto now = UnixDateTime::now(); - m_database.execute_statement(m_statements.update_last_access_time, {}, now, cache_key); + m_database->execute_statement(m_statements.update_last_access_time, {}, now, cache_key); entry->last_access_time = now; } @@ -183,17 +183,17 @@ Optional CacheIndex::find_entry(u64 cache_key) if (auto entry = m_entries.get(cache_key); entry.has_value()) return entry; - m_database.execute_statement( + m_database->execute_statement( m_statements.select_entry, [&](auto statement_id) { int column = 0; - auto cache_key = m_database.result_column(statement_id, column++); - auto url = m_database.result_column(statement_id, column++); - auto response_headers = m_database.result_column(statement_id, column++); - auto data_size = m_database.result_column(statement_id, column++); - auto request_time = m_database.result_column(statement_id, column++); - auto response_time = m_database.result_column(statement_id, column++); - auto last_access_time = m_database.result_column(statement_id, column++); + auto cache_key = m_database->result_column(statement_id, column++); + auto url = m_database->result_column(statement_id, column++); + auto response_headers = m_database->result_column(statement_id, column++); + auto data_size = m_database->result_column(statement_id, column++); + auto request_time = m_database->result_column(statement_id, column++); + auto response_time = m_database->result_column(statement_id, column++); + auto last_access_time = m_database->result_column(statement_id, column++); Entry entry { cache_key, move(url), deserialize_headers(response_headers), data_size, request_time, response_time, last_access_time }; m_entries.set(cache_key, move(entry)); @@ -203,18 +203,18 @@ Optional CacheIndex::find_entry(u64 cache_key) return m_entries.get(cache_key); } -Requests::CacheSizes CacheIndex::estimate_cache_size_accessed_since(UnixDateTime since) const +Requests::CacheSizes CacheIndex::estimate_cache_size_accessed_since(UnixDateTime since) { Requests::CacheSizes sizes; - m_database.execute_statement( + m_database->execute_statement( m_statements.estimate_cache_size_accessed_since, - [&](auto statement_id) { sizes.since_requested_time = m_database.result_column(statement_id, 0); }, + [&](auto statement_id) { sizes.since_requested_time = m_database->result_column(statement_id, 0); }, since); - m_database.execute_statement( + m_database->execute_statement( m_statements.estimate_cache_size_accessed_since, - [&](auto statement_id) { sizes.total = m_database.result_column(statement_id, 0); }, + [&](auto statement_id) { sizes.total = m_database->result_column(statement_id, 0); }, UnixDateTime::earliest()); return sizes; diff --git a/Services/RequestServer/Cache/CacheIndex.h b/Libraries/LibHTTP/Cache/CacheIndex.h similarity index 84% rename from Services/RequestServer/Cache/CacheIndex.h rename to Libraries/LibHTTP/Cache/CacheIndex.h index b8ba51d4d86..187c4a6b34a 100644 --- a/Services/RequestServer/Cache/CacheIndex.h +++ b/Libraries/LibHTTP/Cache/CacheIndex.h @@ -8,13 +8,14 @@ #include #include +#include #include #include #include #include #include -namespace RequestServer { +namespace HTTP { // The cache index is a SQL database containing metadata about each cache entry. An entry in the index is created once // the entire cache entry has been successfully written to disk. @@ -23,7 +24,7 @@ class CacheIndex { u64 cache_key { 0 }; String url; - NonnullRefPtr response_headers; + NonnullRefPtr response_headers; u64 data_size { 0 }; UnixDateTime request_time; @@ -34,16 +35,16 @@ class CacheIndex { public: static ErrorOr create(Database::Database&); - void create_entry(u64 cache_key, String url, NonnullRefPtr, u64 data_size, UnixDateTime request_time, UnixDateTime response_time); + void create_entry(u64 cache_key, String url, NonnullRefPtr, u64 data_size, UnixDateTime request_time, UnixDateTime response_time); void remove_entry(u64 cache_key); void remove_entries_accessed_since(UnixDateTime, Function on_entry_removed); Optional find_entry(u64 cache_key); - void update_response_headers(u64 cache_key, NonnullRefPtr); + void update_response_headers(u64 cache_key, NonnullRefPtr); void update_last_access_time(u64 cache_key); - Requests::CacheSizes estimate_cache_size_accessed_since(UnixDateTime since) const; + Requests::CacheSizes estimate_cache_size_accessed_since(UnixDateTime since); private: struct Statements { @@ -58,7 +59,7 @@ private: CacheIndex(Database::Database&, Statements); - Database::Database& m_database; + NonnullRawPtr m_database; Statements m_statements; HashMap m_entries; diff --git a/Libraries/LibHTTP/Cache/CacheRequest.h b/Libraries/LibHTTP/Cache/CacheRequest.h new file mode 100644 index 00000000000..9506a8dadd1 --- /dev/null +++ b/Libraries/LibHTTP/Cache/CacheRequest.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2025, Tim Flynn + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace HTTP { + +class CacheRequest : public Weakable { +public: + virtual ~CacheRequest() = default; + + virtual void notify_request_unblocked(Badge) = 0; + +protected: + enum class CacheStatus : u8 { + Unknown, + NotCached, + WrittenToCache, + ReadFromCache, + }; + + Optional m_cache_entry_reader; + Optional m_cache_entry_writer; + CacheStatus m_cache_status { CacheStatus::Unknown }; +}; + +} diff --git a/Services/RequestServer/Cache/DiskCache.cpp b/Libraries/LibHTTP/Cache/DiskCache.cpp similarity index 72% rename from Services/RequestServer/Cache/DiskCache.cpp rename to Libraries/LibHTTP/Cache/DiskCache.cpp index da3b1b2cbdf..e16afe86199 100644 --- a/Services/RequestServer/Cache/DiskCache.cpp +++ b/Libraries/LibHTTP/Cache/DiskCache.cpp @@ -4,14 +4,15 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include #include +#include +#include +#include #include -#include -#include -#include -namespace RequestServer { +namespace HTTP { static constexpr auto INDEX_DATABASE = "INDEX"sv; @@ -37,29 +38,37 @@ DiskCache::DiskCache(Mode mode, NonnullRefPtr database, Lexi remove_entries_accessed_since(UnixDateTime::earliest()); } -Variant, DiskCache::CacheHasOpenEntry> DiskCache::create_entry(Request& request) +DiskCache::DiskCache(DiskCache&&) = default; +DiskCache& DiskCache::operator=(DiskCache&&) = default; + +DiskCache::~DiskCache() = default; + +Variant, DiskCache::CacheHasOpenEntry> DiskCache::create_entry(CacheRequest& request, URL::URL const& url, StringView method, HeaderList const& request_headers, UnixDateTime request_start_time) { - if (!is_cacheable(request.method())) + if (!is_cacheable(method)) return Optional {}; if (m_mode == Mode::Testing) { - if (!request.request_headers().contains(TEST_CACHE_ENABLED_HEADER)) + if (!request_headers.contains(TEST_CACHE_ENABLED_HEADER)) return Optional {}; } - auto serialized_url = serialize_url_for_cache_storage(request.url()); - auto cache_key = create_cache_key(serialized_url, request.method()); + auto serialized_url = serialize_url_for_cache_storage(url); + auto cache_key = create_cache_key(serialized_url, method); - if (check_if_cache_has_open_entry(request, cache_key, CheckReaderEntries::Yes)) + if (check_if_cache_has_open_entry(request, cache_key, url, CheckReaderEntries::Yes)) return CacheHasOpenEntry {}; - auto cache_entry = CacheEntryWriter::create(*this, m_index, cache_key, move(serialized_url), request.request_start_time(), request.current_time_offset_for_testing()); + auto current_time_offset_for_testing = compute_current_time_offset_for_testing(*this, request_headers); + request_start_time += current_time_offset_for_testing; + + auto cache_entry = CacheEntryWriter::create(*this, m_index, cache_key, move(serialized_url), request_start_time, current_time_offset_for_testing); if (cache_entry.is_error()) { - dbgln("\033[31;1mUnable to create cache entry for\033[0m {}: {}", request.url(), cache_entry.error()); + dbgln("\033[31;1mUnable to create cache entry for\033[0m {}: {}", url, cache_entry.error()); return Optional {}; } - dbgln("\033[32;1mCreated disk cache entry for\033[0m {}", request.url()); + dbgln("\033[32;1mCreated disk cache entry for\033[0m {}", url); auto* cache_entry_pointer = cache_entry.value().ptr(); m_open_cache_entries.ensure(cache_key).append(cache_entry.release_value()); @@ -67,52 +76,54 @@ Variant, DiskCache::CacheHasOpenEntry> DiskCache::cr return Optional { *cache_entry_pointer }; } -Variant, DiskCache::CacheHasOpenEntry> DiskCache::open_entry(Request& request) +Variant, DiskCache::CacheHasOpenEntry> DiskCache::open_entry(CacheRequest& request, URL::URL const& url, StringView method, HeaderList const& request_headers) { - if (!is_cacheable(request.method())) + if (!is_cacheable(method)) return Optional {}; - auto serialized_url = serialize_url_for_cache_storage(request.url()); - auto cache_key = create_cache_key(serialized_url, request.method()); + auto serialized_url = serialize_url_for_cache_storage(url); + auto cache_key = create_cache_key(serialized_url, method); - if (check_if_cache_has_open_entry(request, cache_key, CheckReaderEntries::No)) + if (check_if_cache_has_open_entry(request, cache_key, url, CheckReaderEntries::No)) return CacheHasOpenEntry {}; auto index_entry = m_index.find_entry(cache_key); if (!index_entry.has_value()) { - dbgln("\033[35;1mNo disk cache entry for\033[0m {}", request.url()); + dbgln("\033[35;1mNo disk cache entry for\033[0m {}", url); return Optional {}; } auto cache_entry = CacheEntryReader::create(*this, m_index, cache_key, index_entry->response_headers, index_entry->data_size); if (cache_entry.is_error()) { - dbgln("\033[31;1mUnable to open cache entry for\033[0m {}: {}", request.url(), cache_entry.error()); + dbgln("\033[31;1mUnable to open cache entry for\033[0m {}: {}", url, cache_entry.error()); m_index.remove_entry(cache_key); return Optional {}; } + auto current_time_offset_for_testing = compute_current_time_offset_for_testing(*this, request_headers); + auto const& response_headers = cache_entry.value()->response_headers(); - auto freshness_lifetime = calculate_freshness_lifetime(cache_entry.value()->status_code(), response_headers, request.current_time_offset_for_testing()); - auto current_age = calculate_age(response_headers, index_entry->request_time, index_entry->response_time, request.current_time_offset_for_testing()); + auto freshness_lifetime = calculate_freshness_lifetime(cache_entry.value()->status_code(), response_headers, current_time_offset_for_testing); + auto current_age = calculate_age(response_headers, index_entry->request_time, index_entry->response_time, current_time_offset_for_testing); switch (cache_lifetime_status(response_headers, freshness_lifetime, current_age)) { case CacheLifetimeStatus::Fresh: - dbgln("\033[32;1mOpened disk cache entry for\033[0m {} (lifetime={}s age={}s) ({} bytes)", request.url(), freshness_lifetime.to_seconds(), current_age.to_seconds(), index_entry->data_size); + dbgln("\033[32;1mOpened disk cache entry for\033[0m {} (lifetime={}s age={}s) ({} bytes)", url, freshness_lifetime.to_seconds(), current_age.to_seconds(), index_entry->data_size); break; case CacheLifetimeStatus::Expired: - dbgln("\033[33;1mCache entry expired for\033[0m {} (lifetime={}s age={}s)", request.url(), freshness_lifetime.to_seconds(), current_age.to_seconds()); + dbgln("\033[33;1mCache entry expired for\033[0m {} (lifetime={}s age={}s)", url, freshness_lifetime.to_seconds(), current_age.to_seconds()); cache_entry.value()->remove(); return Optional {}; case CacheLifetimeStatus::MustRevalidate: // We will hold an exclusive lock on the cache entry for revalidation requests. - if (check_if_cache_has_open_entry(request, cache_key, CheckReaderEntries::Yes)) + if (check_if_cache_has_open_entry(request, cache_key, url, CheckReaderEntries::Yes)) return Optional {}; - dbgln("\033[36;1mMust revalidate disk cache entry for\033[0m {} (lifetime={}s age={}s)", request.url(), freshness_lifetime.to_seconds(), current_age.to_seconds()); + dbgln("\033[36;1mMust revalidate disk cache entry for\033[0m {} (lifetime={}s age={}s)", url, freshness_lifetime.to_seconds(), current_age.to_seconds()); cache_entry.value()->set_must_revalidate(); break; } @@ -123,7 +134,7 @@ Variant, DiskCache::CacheHasOpenEntry> DiskCache::op return Optional { *cache_entry_pointer }; } -bool DiskCache::check_if_cache_has_open_entry(Request& request, u64 cache_key, CheckReaderEntries check_reader_entries) +bool DiskCache::check_if_cache_has_open_entry(CacheRequest& request, u64 cache_key, URL::URL const& url, CheckReaderEntries check_reader_entries) { auto open_entries = m_open_cache_entries.get(cache_key); if (!open_entries.has_value()) @@ -131,7 +142,7 @@ bool DiskCache::check_if_cache_has_open_entry(Request& request, u64 cache_key, C for (auto const& open_entry : *open_entries) { if (is(*open_entry)) { - dbgln("\033[36;1mDeferring disk cache entry for\033[0m {} (waiting for existing writer)", request.url()); + dbgln("\033[36;1mDeferring disk cache entry for\033[0m {} (waiting for existing writer)", url); m_requests_waiting_completion.ensure(cache_key).append(request); return true; } @@ -139,7 +150,7 @@ bool DiskCache::check_if_cache_has_open_entry(Request& request, u64 cache_key, C // We allow concurrent readers unless another reader is open for revalidation. That reader will issue the network // request, which may then result in the cache entry being updated or deleted. if (check_reader_entries == CheckReaderEntries::Yes || as(*open_entry).must_revalidate()) { - dbgln("\033[36;1mDeferring disk cache entry for\033[0m {} (waiting for existing reader)", request.url()); + dbgln("\033[36;1mDeferring disk cache entry for\033[0m {} (waiting for existing reader)", url); m_requests_waiting_completion.ensure(cache_key).append(request); return true; } @@ -148,7 +159,7 @@ bool DiskCache::check_if_cache_has_open_entry(Request& request, u64 cache_key, C return false; } -Requests::CacheSizes DiskCache::estimate_cache_size_accessed_since(UnixDateTime since) const +Requests::CacheSizes DiskCache::estimate_cache_size_accessed_since(UnixDateTime since) { return m_index.estimate_cache_size_accessed_since(since); } diff --git a/Services/RequestServer/Cache/DiskCache.h b/Libraries/LibHTTP/Cache/DiskCache.h similarity index 71% rename from Services/RequestServer/Cache/DiskCache.h rename to Libraries/LibHTTP/Cache/DiskCache.h index 57803760302..59edc1a7a81 100644 --- a/Services/RequestServer/Cache/DiskCache.h +++ b/Libraries/LibHTTP/Cache/DiskCache.h @@ -14,11 +14,11 @@ #include #include #include +#include +#include #include -#include -#include -namespace RequestServer { +namespace HTTP { class DiskCache { public: @@ -31,13 +31,18 @@ public: }; static ErrorOr create(Mode); + DiskCache(DiskCache&&); + DiskCache& operator=(DiskCache&&); + + ~DiskCache(); + Mode mode() const { return m_mode; } struct CacheHasOpenEntry { }; - Variant, CacheHasOpenEntry> create_entry(Request&); - Variant, CacheHasOpenEntry> open_entry(Request&); + Variant, CacheHasOpenEntry> create_entry(CacheRequest&, URL::URL const&, StringView method, HeaderList const& request_headers, UnixDateTime request_start_time); + Variant, CacheHasOpenEntry> open_entry(CacheRequest&, URL::URL const&, StringView method, HeaderList const& request_headers); - Requests::CacheSizes estimate_cache_size_accessed_since(UnixDateTime since) const; + Requests::CacheSizes estimate_cache_size_accessed_since(UnixDateTime since); void remove_entries_accessed_since(UnixDateTime since); LexicalPath const& cache_directory() { return m_cache_directory; } @@ -51,14 +56,14 @@ private: No, Yes, }; - bool check_if_cache_has_open_entry(Request&, u64 cache_key, CheckReaderEntries); + bool check_if_cache_has_open_entry(CacheRequest&, u64 cache_key, URL::URL const&, CheckReaderEntries); Mode m_mode; NonnullRefPtr m_database; HashMap, 1>> m_open_cache_entries; - HashMap, 1>> m_requests_waiting_completion; + HashMap, 1>> m_requests_waiting_completion; LexicalPath m_cache_directory; CacheIndex m_index; diff --git a/Services/RequestServer/Cache/Utilities.cpp b/Libraries/LibHTTP/Cache/Utilities.cpp similarity index 94% rename from Services/RequestServer/Cache/Utilities.cpp rename to Libraries/LibHTTP/Cache/Utilities.cpp index 140b15f293c..6964ba288a2 100644 --- a/Services/RequestServer/Cache/Utilities.cpp +++ b/Libraries/LibHTTP/Cache/Utilities.cpp @@ -5,11 +5,11 @@ */ #include +#include +#include #include -#include -#include -namespace RequestServer { +namespace HTTP { static Optional extract_cache_control_directive(StringView cache_control, StringView directive) { @@ -111,7 +111,7 @@ static bool is_heuristically_cacheable_status(u32 status_code) } // https://httpwg.org/specs/rfc9111.html#response.cacheability -bool is_cacheable(u32 status_code, HTTP::HeaderList const& headers) +bool is_cacheable(u32 status_code, HeaderList const& headers) { // A cache MUST NOT store a response to a request unless: @@ -213,7 +213,7 @@ bool is_header_exempted_from_storage(StringView name) } // https://httpwg.org/specs/rfc9111.html#heuristic.freshness -static AK::Duration calculate_heuristic_freshness_lifetime(HTTP::HeaderList const& headers, AK::Duration current_time_offset_for_testing) +static AK::Duration calculate_heuristic_freshness_lifetime(HeaderList const& headers, AK::Duration current_time_offset_for_testing) { // Since origin servers do not always provide explicit expiration times, a cache MAY assign a heuristic expiration // time when an explicit time is not specified, employing algorithms that use other field values (such as the @@ -245,7 +245,7 @@ static AK::Duration calculate_heuristic_freshness_lifetime(HTTP::HeaderList cons } // https://httpwg.org/specs/rfc9111.html#calculating.freshness.lifetime -AK::Duration calculate_freshness_lifetime(u32 status_code, HTTP::HeaderList const& headers, AK::Duration current_time_offset_for_testing) +AK::Duration calculate_freshness_lifetime(u32 status_code, HeaderList const& headers, AK::Duration current_time_offset_for_testing) { // A cache can calculate the freshness lifetime (denoted as freshness_lifetime) of a response by evaluating the // following rules and using the first match: @@ -296,7 +296,7 @@ AK::Duration calculate_freshness_lifetime(u32 status_code, HTTP::HeaderList cons } // https://httpwg.org/specs/rfc9111.html#age.calculations -AK::Duration calculate_age(HTTP::HeaderList const& headers, UnixDateTime request_time, UnixDateTime response_time, AK::Duration current_time_offset_for_testing) +AK::Duration calculate_age(HeaderList const& headers, UnixDateTime request_time, UnixDateTime response_time, AK::Duration current_time_offset_for_testing) { // The term "age_value" denotes the value of the Age header field (Section 5.1), in a form appropriate for arithmetic // operation; or 0, if not available. @@ -328,7 +328,7 @@ AK::Duration calculate_age(HTTP::HeaderList const& headers, UnixDateTime request return current_age; } -CacheLifetimeStatus cache_lifetime_status(HTTP::HeaderList const& headers, AK::Duration freshness_lifetime, AK::Duration current_age) +CacheLifetimeStatus cache_lifetime_status(HeaderList const& headers, AK::Duration freshness_lifetime, AK::Duration current_age) { auto revalidation_status = [&]() { // In order to revalidate a cache entry, we must have one of these headers to attach to the revalidation request. @@ -365,7 +365,7 @@ CacheLifetimeStatus cache_lifetime_status(HTTP::HeaderList const& headers, AK::D } // https://httpwg.org/specs/rfc9111.html#validation.sent -RevalidationAttributes RevalidationAttributes::create(HTTP::HeaderList const& headers) +RevalidationAttributes RevalidationAttributes::create(HeaderList const& headers) { RevalidationAttributes attributes; attributes.etag = headers.get("ETag"sv).map([](auto const& etag) { return etag; }); @@ -375,7 +375,7 @@ RevalidationAttributes RevalidationAttributes::create(HTTP::HeaderList const& he } // https://httpwg.org/specs/rfc9111.html#update -void update_header_fields(HTTP::HeaderList& stored_headers, HTTP::HeaderList const& updated_headers) +void update_header_fields(HeaderList& stored_headers, HeaderList const& updated_headers) { // Caches are required to update a stored response's header fields from another (typically newer) response in // several situations; for example, see Sections 3.4, 4.3.4, and 4.3.5. @@ -408,7 +408,7 @@ void update_header_fields(HTTP::HeaderList& stored_headers, HTTP::HeaderList con } } -AK::Duration compute_current_time_offset_for_testing(Optional disk_cache, HTTP::HeaderList const& request_headers) +AK::Duration compute_current_time_offset_for_testing(Optional disk_cache, HeaderList const& request_headers) { if (disk_cache.has_value() && disk_cache->mode() == DiskCache::Mode::Testing) { if (auto header = request_headers.get(TEST_CACHE_REQUEST_TIME_OFFSET); header.has_value()) { diff --git a/Services/RequestServer/Cache/Utilities.h b/Libraries/LibHTTP/Cache/Utilities.h similarity index 60% rename from Services/RequestServer/Cache/Utilities.h rename to Libraries/LibHTTP/Cache/Utilities.h index 2cb18062835..af8e700564d 100644 --- a/Services/RequestServer/Cache/Utilities.h +++ b/Libraries/LibHTTP/Cache/Utilities.h @@ -10,11 +10,11 @@ #include #include #include +#include #include #include -#include -namespace RequestServer { +namespace HTTP { constexpr inline auto TEST_CACHE_ENABLED_HEADER = "X-Ladybird-Enable-Disk-Cache"sv; constexpr inline auto TEST_CACHE_STATUS_HEADER = "X-Ladybird-Disk-Cache-Status"sv; @@ -25,28 +25,28 @@ u64 create_cache_key(StringView url, StringView method); LexicalPath path_for_cache_key(LexicalPath const& cache_directory, u64 cache_key); bool is_cacheable(StringView method); -bool is_cacheable(u32 status_code, HTTP::HeaderList const&); +bool is_cacheable(u32 status_code, HeaderList const&); bool is_header_exempted_from_storage(StringView name); -AK::Duration calculate_freshness_lifetime(u32 status_code, HTTP::HeaderList const&, AK::Duration current_time_offset_for_testing); -AK::Duration calculate_age(HTTP::HeaderList const&, UnixDateTime request_time, UnixDateTime response_time, AK::Duration current_time_offset_for_testing); +AK::Duration calculate_freshness_lifetime(u32 status_code, HeaderList const&, AK::Duration current_time_offset_for_testing); +AK::Duration calculate_age(HeaderList const&, UnixDateTime request_time, UnixDateTime response_time, AK::Duration current_time_offset_for_testing); enum class CacheLifetimeStatus { Fresh, Expired, MustRevalidate, }; -CacheLifetimeStatus cache_lifetime_status(HTTP::HeaderList const&, AK::Duration freshness_lifetime, AK::Duration current_age); +CacheLifetimeStatus cache_lifetime_status(HeaderList const&, AK::Duration freshness_lifetime, AK::Duration current_age); struct RevalidationAttributes { - static RevalidationAttributes create(HTTP::HeaderList const&); + static RevalidationAttributes create(HeaderList const&); Optional etag; Optional last_modified; }; -void update_header_fields(HTTP::HeaderList&, HTTP::HeaderList const&); +void update_header_fields(HeaderList&, HeaderList const&); -AK::Duration compute_current_time_offset_for_testing(Optional, HTTP::HeaderList const& request_headers); +AK::Duration compute_current_time_offset_for_testing(Optional, HeaderList const& request_headers); } diff --git a/Services/RequestServer/Cache/Version.h b/Libraries/LibHTTP/Cache/Version.h similarity index 92% rename from Services/RequestServer/Cache/Version.h rename to Libraries/LibHTTP/Cache/Version.h index 7097eff7cc8..b952deb1332 100644 --- a/Services/RequestServer/Cache/Version.h +++ b/Libraries/LibHTTP/Cache/Version.h @@ -8,7 +8,7 @@ #include -namespace RequestServer { +namespace HTTP { // Increment this version when a breaking change is made to the cache index or cache entry formats. static constexpr inline u32 CACHE_VERSION = 4u; diff --git a/Libraries/LibHTTP/Forward.h b/Libraries/LibHTTP/Forward.h index e929120f134..47a4172fa59 100644 --- a/Libraries/LibHTTP/Forward.h +++ b/Libraries/LibHTTP/Forward.h @@ -8,6 +8,12 @@ namespace HTTP { +class CacheEntry; +class CacheEntryReader; +class CacheEntryWriter; +class CacheIndex; +class CacheRequest; +class DiskCache; class HeaderList; class HttpRequest; class HttpResponse; diff --git a/Services/RequestServer/CMakeLists.txt b/Services/RequestServer/CMakeLists.txt index 3c06ffd95a4..db661dfd0ae 100644 --- a/Services/RequestServer/CMakeLists.txt +++ b/Services/RequestServer/CMakeLists.txt @@ -3,10 +3,6 @@ set(CMAKE_AUTORCC OFF) set(CMAKE_AUTOUIC OFF) set(SOURCES - Cache/CacheEntry.cpp - Cache/CacheIndex.cpp - Cache/DiskCache.cpp - Cache/Utilities.cpp ConnectionFromClient.cpp CURL.cpp Request.cpp @@ -41,7 +37,7 @@ target_include_directories(requestserverservice PRIVATE ${CMAKE_CURRENT_BINARY_D target_include_directories(requestserverservice PRIVATE ${LADYBIRD_SOURCE_DIR}/Services/) target_link_libraries(RequestServer PRIVATE requestserverservice) -target_link_libraries(requestserverservice PUBLIC LibCore LibDatabase LibDNS LibCrypto LibFileSystem LibHTTP LibIPC LibMain LibRequests LibTLS LibWebSocket LibURL LibTextCodec LibThreading CURL::libcurl) +target_link_libraries(requestserverservice PUBLIC LibCore LibDNS LibHTTP LibIPC LibMain LibRequests LibTLS LibWebSocket LibURL LibTextCodec CURL::libcurl) target_link_libraries(requestserverservice PRIVATE OpenSSL::Crypto OpenSSL::SSL) if (WIN32) diff --git a/Services/RequestServer/ConnectionFromClient.cpp b/Services/RequestServer/ConnectionFromClient.cpp index 13de126f91a..52b2cf523ed 100644 --- a/Services/RequestServer/ConnectionFromClient.cpp +++ b/Services/RequestServer/ConnectionFromClient.cpp @@ -10,11 +10,11 @@ #include #include #include +#include #include #include #include #include -#include #include #include #include @@ -25,7 +25,7 @@ namespace RequestServer { static HashMap> s_connections; static IDAllocator s_client_ids; -Optional g_disk_cache; +Optional g_disk_cache; ConnectionFromClient::ConnectionFromClient(NonnullOwnPtr transport) : IPC::ConnectionFromClient(*this, move(transport), s_client_ids.allocate()) diff --git a/Services/RequestServer/Forward.h b/Services/RequestServer/Forward.h index 3685ade6b30..e998df8f817 100644 --- a/Services/RequestServer/Forward.h +++ b/Services/RequestServer/Forward.h @@ -8,12 +8,7 @@ namespace RequestServer { -class CacheEntry; -class CacheEntryReader; -class CacheEntryWriter; -class CacheIndex; class ConnectionFromClient; -class DiskCache; class Request; class RequestPipe; diff --git a/Services/RequestServer/Request.cpp b/Services/RequestServer/Request.cpp index 64d89eae05f..380044d5c7c 100644 --- a/Services/RequestServer/Request.cpp +++ b/Services/RequestServer/Request.cpp @@ -5,13 +5,12 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include #include #include +#include +#include #include #include -#include -#include #include #include #include @@ -22,7 +21,7 @@ static long s_connect_timeout_seconds = 90L; NonnullOwnPtr Request::fetch( i32 request_id, - Optional disk_cache, + Optional disk_cache, ConnectionFromClient& client, void* curl_multi, Resolver& resolver, @@ -63,7 +62,7 @@ NonnullOwnPtr Request::connect( Request::Request( i32 request_id, - Optional disk_cache, + Optional disk_cache, ConnectionFromClient& client, void* curl_multi, Resolver& resolver, @@ -86,9 +85,7 @@ Request::Request( , m_alt_svc_cache_path(move(alt_svc_cache_path)) , m_proxy_data(proxy_data) , m_response_headers(HTTP::HeaderList::create()) - , m_current_time_offset_for_testing(compute_current_time_offset_for_testing(m_disk_cache, m_request_headers)) { - m_request_start_time += m_current_time_offset_for_testing; } Request::Request( @@ -105,9 +102,7 @@ Request::Request( , m_url(move(url)) , m_request_headers(HTTP::HeaderList::create()) , m_response_headers(HTTP::HeaderList::create()) - , m_current_time_offset_for_testing(compute_current_time_offset_for_testing(m_disk_cache, m_request_headers)) { - m_request_start_time += m_current_time_offset_for_testing; } Request::~Request() @@ -129,7 +124,7 @@ Request::~Request() (void)m_cache_entry_writer->flush(m_response_headers); } -void Request::notify_request_unblocked(Badge) +void Request::notify_request_unblocked(Badge) { // FIXME: We may want a timer to limit how long we are waiting for a request before proceeding with a network // request that skips the disk cache. @@ -196,38 +191,40 @@ void Request::process() void Request::handle_initial_state() { if (m_disk_cache.has_value()) { - m_disk_cache->open_entry(*this).visit( - [&](Optional cache_entry_reader) { - m_cache_entry_reader = cache_entry_reader; + m_disk_cache->open_entry(*this, m_url, m_method, m_request_headers) + .visit( + [&](Optional cache_entry_reader) { + m_cache_entry_reader = cache_entry_reader; - if (m_cache_entry_reader.has_value()) { - if (m_cache_entry_reader->must_revalidate()) - transition_to_state(State::DNSLookup); - else - transition_to_state(State::ReadCache); - } - }, - [&](DiskCache::CacheHasOpenEntry) { - // If an existing entry is open for writing, we must wait for it to complete. - transition_to_state(State::WaitForCache); - }); + if (m_cache_entry_reader.has_value()) { + if (m_cache_entry_reader->must_revalidate()) + transition_to_state(State::DNSLookup); + else + transition_to_state(State::ReadCache); + } + }, + [&](HTTP::DiskCache::CacheHasOpenEntry) { + // If an existing entry is open for writing, we must wait for it to complete. + transition_to_state(State::WaitForCache); + }); if (m_state != State::Init) return; - m_disk_cache->create_entry(*this).visit( - [&](Optional cache_entry_writer) { - m_cache_entry_writer = cache_entry_writer; + m_disk_cache->create_entry(*this, m_url, m_method, m_request_headers, m_request_start_time) + .visit( + [&](Optional cache_entry_writer) { + m_cache_entry_writer = cache_entry_writer; - if (!m_cache_entry_writer.has_value()) - m_cache_status = CacheStatus::NotCached; - }, - [&](DiskCache::CacheHasOpenEntry) { - // If an existing entry is open for reading or writing, we must wait for it to complete. An entry being - // open for reading is a rare case, but may occur if a cached response expired between the existing - // entry's cache validation and the attempted reader validation when this request was created. - transition_to_state(State::WaitForCache); - }); + if (!m_cache_entry_writer.has_value()) + m_cache_status = CacheStatus::NotCached; + }, + [&](HTTP::DiskCache::CacheHasOpenEntry) { + // If an existing entry is open for reading or writing, we must wait for it to complete. An entry being + // open for reading is a rare case, but may occur if a cached response expired between the existing + // entry's cache validation and the attempted reader validation when this request was created. + transition_to_state(State::WaitForCache); + }); if (m_state != State::Init) return; @@ -385,7 +382,7 @@ void Request::handle_fetch_state() } if (is_revalidation_request) { - auto revalidation_attributes = RevalidationAttributes::create(m_cache_entry_reader->response_headers()); + auto revalidation_attributes = HTTP::RevalidationAttributes::create(m_cache_entry_reader->response_headers()); VERIFY(revalidation_attributes.etag.has_value() || revalidation_attributes.last_modified.has_value()); if (revalidation_attributes.etag.has_value()) { @@ -512,14 +509,15 @@ size_t Request::on_data_received(void* buffer, size_t size, size_t nmemb, void* if (request.revalidation_failed().is_error()) return CURL_WRITEFUNC_ERROR; - request.m_disk_cache->create_entry(request).visit( - [&](Optional cache_entry_writer) { - request.m_cache_entry_writer = cache_entry_writer; - }, - [&](DiskCache::CacheHasOpenEntry) { - // This should not be reachable, as cache revalidation holds an exclusive lock on the cache entry. - VERIFY_NOT_REACHED(); - }); + request.m_disk_cache->create_entry(request, request.m_url, request.m_method, request.m_request_headers, request.m_request_start_time) + .visit( + [&](Optional cache_entry_writer) { + request.m_cache_entry_writer = cache_entry_writer; + }, + [&](HTTP::DiskCache::CacheHasOpenEntry) { + // This should not be reachable, as cache revalidation holds an exclusive lock on the cache entry. + VERIFY_NOT_REACHED(); + }); } request.transfer_headers_to_client_if_needed(); @@ -574,18 +572,18 @@ void Request::transfer_headers_to_client_if_needed() } } - if (m_disk_cache.has_value() && m_disk_cache->mode() == DiskCache::Mode::Testing) { + if (m_disk_cache.has_value() && m_disk_cache->mode() == HTTP::DiskCache::Mode::Testing) { switch (m_cache_status) { case CacheStatus::Unknown: break; case CacheStatus::NotCached: - m_response_headers->set({ TEST_CACHE_STATUS_HEADER, "not-cached"sv }); + m_response_headers->set({ HTTP::TEST_CACHE_STATUS_HEADER, "not-cached"sv }); break; case CacheStatus::WrittenToCache: - m_response_headers->set({ TEST_CACHE_STATUS_HEADER, "written-to-cache"sv }); + m_response_headers->set({ HTTP::TEST_CACHE_STATUS_HEADER, "written-to-cache"sv }); break; case CacheStatus::ReadFromCache: - m_response_headers->set({ TEST_CACHE_STATUS_HEADER, "read-from-cache"sv }); + m_response_headers->set({ HTTP::TEST_CACHE_STATUS_HEADER, "read-from-cache"sv }); break; } } diff --git a/Services/RequestServer/Request.h b/Services/RequestServer/Request.h index ea76017a91e..dfc9d1ba726 100644 --- a/Services/RequestServer/Request.h +++ b/Services/RequestServer/Request.h @@ -11,9 +11,9 @@ #include #include #include -#include #include #include +#include #include #include #include @@ -26,11 +26,11 @@ struct curl_slist; namespace RequestServer { -class Request : public Weakable { +class Request : public HTTP::CacheRequest { public: static NonnullOwnPtr fetch( i32 request_id, - Optional disk_cache, + Optional disk_cache, ConnectionFromClient& client, void* curl_multi, Resolver& resolver, @@ -49,15 +49,14 @@ public: URL::URL url, CacheLevel cache_level); - ~Request(); + virtual ~Request() override; URL::URL const& url() const { return m_url; } ByteString const& method() const { return m_method; } HTTP::HeaderList const& request_headers() const { return m_request_headers; } UnixDateTime request_start_time() const { return m_request_start_time; } - AK::Duration current_time_offset_for_testing() const { return m_current_time_offset_for_testing; } - void notify_request_unblocked(Badge); + virtual void notify_request_unblocked(Badge) override; void notify_fetch_complete(Badge, int result_code); private: @@ -77,16 +76,9 @@ private: Error, // Any error occured during the request's lifetime. }; - enum class CacheStatus : u8 { - Unknown, - NotCached, - WrittenToCache, - ReadFromCache, - }; - Request( i32 request_id, - Optional disk_cache, + Optional disk_cache, ConnectionFromClient& client, void* curl_multi, Resolver& resolver, @@ -132,7 +124,7 @@ private: Type m_type { Type::Fetch }; State m_state { State::Init }; - Optional m_disk_cache; + Optional m_disk_cache; ConnectionFromClient& m_client; void* m_curl_multi_handle { nullptr }; @@ -164,13 +156,7 @@ private: Optional m_client_request_pipe; size_t m_bytes_transferred_to_client { 0 }; - Optional m_cache_entry_reader; - Optional m_cache_entry_writer; - CacheStatus m_cache_status { CacheStatus::Unknown }; - Optional m_network_error; - - AK::Duration m_current_time_offset_for_testing; }; } diff --git a/Services/RequestServer/main.cpp b/Services/RequestServer/main.cpp index 1e82f7fbae2..ef48810a8d8 100644 --- a/Services/RequestServer/main.cpp +++ b/Services/RequestServer/main.cpp @@ -12,9 +12,9 @@ #include #include #include +#include #include #include -#include #include #include @@ -24,7 +24,7 @@ namespace RequestServer { -extern Optional g_disk_cache; +extern Optional g_disk_cache; } @@ -60,10 +60,10 @@ ErrorOr ladybird_main(Main::Arguments arguments) if (http_disk_cache_mode.is_one_of("enabled"sv, "testing"sv)) { auto mode = http_disk_cache_mode == "enabled"sv - ? RequestServer::DiskCache::Mode::Normal - : RequestServer::DiskCache::Mode::Testing; + ? HTTP::DiskCache::Mode::Normal + : HTTP::DiskCache::Mode::Testing; - if (auto cache = RequestServer::DiskCache::create(mode); cache.is_error()) + if (auto cache = HTTP::DiskCache::create(mode); cache.is_error()) warnln("Unable to create disk cache: {}", cache.error()); else RequestServer::g_disk_cache = cache.release_value();