diff --git a/Libraries/LibWebView/Application.cpp b/Libraries/LibWebView/Application.cpp index 040e97bc374..525efcb9247 100644 --- a/Libraries/LibWebView/Application.cpp +++ b/Libraries/LibWebView/Application.cpp @@ -824,7 +824,10 @@ void Application::initialize_actions() m_debug_menu->add_separator(); m_debug_menu->add_action(Action::create("Collect Garbage"sv, ActionID::CollectGarbage, debug_request("collect-garbage"sv))); - m_debug_menu->add_action(Action::create("Clear Cache"sv, ActionID::ClearCache, debug_request("clear-cache"sv))); + m_debug_menu->add_action(Action::create("Clear Cache"sv, ActionID::ClearCache, [this, clear_memory_cache = debug_request("clear_cache")]() { + m_request_server_client->async_clear_cache(); + clear_memory_cache(); + })); m_debug_menu->add_action(Action::create("Clear All Cookies"sv, ActionID::ClearCookies, [this]() { m_cookie_jar->clear_all_cookies(); })); m_debug_menu->add_separator(); diff --git a/Services/RequestServer/Cache/CacheEntry.cpp b/Services/RequestServer/Cache/CacheEntry.cpp index 98ce603fe1d..b975af67e94 100644 --- a/Services/RequestServer/Cache/CacheEntry.cpp +++ b/Services/RequestServer/Cache/CacheEntry.cpp @@ -153,6 +153,11 @@ CacheEntryWriter::CacheEntryWriter(DiskCache& disk_cache, CacheIndex& index, u64 ErrorOr CacheEntryWriter::write_data(ReadonlyBytes data) { + if (m_marked_for_deletion) { + close_and_destory_cache_entry(); + return Error::from_string_literal("Cache entry has been deleted"); + } + if (auto result = m_file->write_until_depleted(data); result.is_error()) { dbgln("\033[31;1mUnable to write to cache entry for{}\033[0m {}: {}", m_url, result.error()); @@ -174,6 +179,9 @@ ErrorOr CacheEntryWriter::flush() { ScopeGuard guard { [&]() { close_and_destory_cache_entry(); } }; + if (m_marked_for_deletion) + return Error::from_string_literal("Cache entry has been deleted"); + if (auto result = m_file->write_value(m_cache_footer); result.is_error()) { dbgln("\033[31;1mUnable to flush cache entry for{}\033[0m {}: {}", m_url, result.error()); remove(); @@ -272,6 +280,11 @@ void CacheEntryReader::pipe_to(int pipe_fd, Function on_complete, Fun m_on_pipe_complete = move(on_complete); m_on_pipe_error = move(on_error); + if (m_marked_for_deletion) { + pipe_error(Error::from_string_literal("Cache entry has been deleted")); + return; + } + m_pipe_write_notifier = Core::Notifier::construct(m_pipe_fd, Core::NotificationType::Write); m_pipe_write_notifier->set_enabled(false); @@ -285,19 +298,18 @@ void CacheEntryReader::pipe_to(int pipe_fd, Function on_complete, Fun void CacheEntryReader::pipe_without_blocking() { + if (m_marked_for_deletion) { + pipe_error(Error::from_string_literal("Cache entry has been deleted")); + return; + } + auto result = Core::System::transfer_file_through_pipe(m_fd, m_pipe_fd, m_data_offset + m_bytes_piped, m_data_size - m_bytes_piped); if (result.is_error()) { - if (result.error().code() != EAGAIN && result.error().code() != EWOULDBLOCK) { - dbgln("\033[31;1mError transferring cache to pipe for\033[0m {}: {}", m_url, result.error()); - - if (m_on_pipe_error) - m_on_pipe_error(m_bytes_piped); - - close_and_destory_cache_entry(); - } else { + if (result.error().code() != EAGAIN && result.error().code() != EWOULDBLOCK) + pipe_error(result.release_error()); + else m_pipe_write_notifier->set_enabled(true); - } return; } @@ -330,6 +342,16 @@ void CacheEntryReader::pipe_complete() close_and_destory_cache_entry(); } +void CacheEntryReader::pipe_error(Error error) +{ + dbgln("\033[31;1mError transferring cache to pipe for\033[0m {}: {}", m_url, error); + + if (m_on_pipe_error) + m_on_pipe_error(m_bytes_piped); + + close_and_destory_cache_entry(); +} + ErrorOr CacheEntryReader::read_and_validate_footer() { TRY(m_file->seek(m_data_offset + m_data_size, SeekMode::SetPosition)); diff --git a/Services/RequestServer/Cache/CacheEntry.h b/Services/RequestServer/Cache/CacheEntry.h index 62a60c7f14c..1c8ab8f8323 100644 --- a/Services/RequestServer/Cache/CacheEntry.h +++ b/Services/RequestServer/Cache/CacheEntry.h @@ -57,6 +57,8 @@ public: void remove(); + void mark_for_deletion(Badge) { m_marked_for_deletion = true; } + protected: CacheEntry(DiskCache&, CacheIndex&, u64 cache_key, String url, LexicalPath, CacheHeader); @@ -72,6 +74,8 @@ protected: CacheHeader m_cache_header; CacheFooter m_cache_footer; + + bool m_marked_for_deletion { false }; }; class CacheEntryWriter : public CacheEntry { @@ -107,6 +111,7 @@ private: void pipe_without_blocking(); void pipe_complete(); + void pipe_error(Error); ErrorOr read_and_validate_footer(); diff --git a/Services/RequestServer/Cache/CacheIndex.cpp b/Services/RequestServer/Cache/CacheIndex.cpp index f454076ffec..9b6da3e020e 100644 --- a/Services/RequestServer/Cache/CacheIndex.cpp +++ b/Services/RequestServer/Cache/CacheIndex.cpp @@ -25,6 +25,7 @@ ErrorOr CacheIndex::create(Database::Database& database) Statements statements {}; statements.insert_entry = TRY(database.prepare_statement("INSERT OR REPLACE INTO CacheIndex VALUES (?, ?, ?, ?, ?, ?);"sv)); statements.remove_entry = TRY(database.prepare_statement("DELETE FROM CacheIndex WHERE cache_key = ?;"sv)); + statements.remove_all_entries = TRY(database.prepare_statement("DELETE FROM CacheIndex;"sv)); statements.select_entry = TRY(database.prepare_statement("SELECT * FROM CacheIndex WHERE cache_key = ?;"sv)); statements.update_last_access_time = TRY(database.prepare_statement("UPDATE CacheIndex SET last_access_time = ? WHERE cache_key = ?;"sv)); @@ -60,6 +61,12 @@ void CacheIndex::remove_entry(u64 cache_key) m_entries.remove(cache_key); } +void CacheIndex::remove_all_entries() +{ + m_database.execute_statement(m_statements.remove_all_entries, {}); + m_entries.clear(); +} + void CacheIndex::update_last_access_time(u64 cache_key) { auto entry = m_entries.get(cache_key); diff --git a/Services/RequestServer/Cache/CacheIndex.h b/Services/RequestServer/Cache/CacheIndex.h index a1b2e4174d7..47fe55a0853 100644 --- a/Services/RequestServer/Cache/CacheIndex.h +++ b/Services/RequestServer/Cache/CacheIndex.h @@ -33,6 +33,7 @@ public: void create_entry(u64 cache_key, String url, u64 data_size, UnixDateTime request_time, UnixDateTime response_time); void remove_entry(u64 cache_key); + void remove_all_entries(); Optional find_entry(u64 cache_key); @@ -42,6 +43,7 @@ private: struct Statements { Database::StatementID insert_entry { 0 }; Database::StatementID remove_entry { 0 }; + Database::StatementID remove_all_entries { 0 }; Database::StatementID select_entry { 0 }; Database::StatementID update_last_access_time { 0 }; }; diff --git a/Services/RequestServer/Cache/DiskCache.cpp b/Services/RequestServer/Cache/DiskCache.cpp index 9d4bf4953a1..f0cf3a8c093 100644 --- a/Services/RequestServer/Cache/DiskCache.cpp +++ b/Services/RequestServer/Cache/DiskCache.cpp @@ -4,7 +4,9 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include +#include #include #include #include @@ -90,6 +92,28 @@ Optional DiskCache::open_entry(URL::URL const& url, StringVie return static_cast(**m_open_cache_entries.get(address)); } +void DiskCache::clear_cache() +{ + for (auto& [_, cache_entry] : m_open_cache_entries) + cache_entry->mark_for_deletion({}); + + m_index.remove_all_entries(); + + Core::DirIterator it { m_cache_directory.string(), Core::DirIterator::SkipDots }; + size_t cache_entries { 0 }; + + while (it.has_next()) { + auto entry = it.next_full_path(); + if (LexicalPath { entry }.title() == INDEX_DATABASE) + continue; + + (void)FileSystem::remove(entry, FileSystem::RecursionMode::Disallowed); + ++cache_entries; + } + + dbgln("Cleared {} disk cache entries", cache_entries); +} + void DiskCache::cache_entry_closed(Badge, CacheEntry const& cache_entry) { auto address = reinterpret_cast(&cache_entry); diff --git a/Services/RequestServer/Cache/DiskCache.h b/Services/RequestServer/Cache/DiskCache.h index 09cdc42e48d..9f21dd1ae8b 100644 --- a/Services/RequestServer/Cache/DiskCache.h +++ b/Services/RequestServer/Cache/DiskCache.h @@ -26,6 +26,7 @@ public: Optional create_entry(URL::URL const&, StringView method, u32 status_code, Optional reason_phrase, HTTP::HeaderMap const&, UnixDateTime request_time); Optional open_entry(URL::URL const&, StringView method); + void clear_cache(); LexicalPath const& cache_directory() { return m_cache_directory; } diff --git a/Services/RequestServer/ConnectionFromClient.cpp b/Services/RequestServer/ConnectionFromClient.cpp index 6461ce88954..95583e84509 100644 --- a/Services/RequestServer/ConnectionFromClient.cpp +++ b/Services/RequestServer/ConnectionFromClient.cpp @@ -861,6 +861,12 @@ void ConnectionFromClient::ensure_connection(URL::URL url, ::RequestServer::Cach } } +void ConnectionFromClient::clear_cache() +{ + if (g_disk_cache.has_value()) + g_disk_cache->clear_cache(); +} + void ConnectionFromClient::websocket_connect(i64 websocket_id, URL::URL url, ByteString origin, Vector protocols, Vector extensions, HTTP::HeaderMap additional_request_headers) { auto host = url.serialized_host().to_byte_string(); diff --git a/Services/RequestServer/ConnectionFromClient.h b/Services/RequestServer/ConnectionFromClient.h index 0a6baaa1618..7eafa4f1b0f 100644 --- a/Services/RequestServer/ConnectionFromClient.h +++ b/Services/RequestServer/ConnectionFromClient.h @@ -49,6 +49,8 @@ private: virtual Messages::RequestServer::SetCertificateResponse set_certificate(i32, ByteString, ByteString) override; virtual void ensure_connection(URL::URL url, ::RequestServer::CacheLevel cache_level) override; + virtual void clear_cache() override; + virtual void websocket_connect(i64 websocket_id, URL::URL, ByteString, Vector, Vector, HTTP::HeaderMap) override; virtual void websocket_send(i64 websocket_id, bool, ByteBuffer) override; virtual void websocket_close(i64 websocket_id, u16, ByteString) override; diff --git a/Services/RequestServer/RequestServer.ipc b/Services/RequestServer/RequestServer.ipc index ea4979d66b4..848efc91a97 100644 --- a/Services/RequestServer/RequestServer.ipc +++ b/Services/RequestServer/RequestServer.ipc @@ -22,6 +22,8 @@ endpoint RequestServer ensure_connection(URL::URL url, ::RequestServer::CacheLevel cache_level) =| + clear_cache() =| + // Websocket Connection API websocket_connect(i64 websocket_id, URL::URL url, ByteString origin, Vector protocols, Vector extensions, HTTP::HeaderMap additional_request_headers) =| websocket_send(i64 websocket_id, bool is_text, ByteBuffer data) =|