mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-10-19 07:33:20 +00:00
LibWebView+RequestServer: Support clearing the HTTP disk cache
This is a bit of a blunt hammer, but this hooks an action to clear the HTTP disk cache into the existing Clear Cache action. Upon invocation, it stops all existing cache entries from making further progress, and then deletes the entire cache index and all cache files. In the future, we will of course want more fine-grained control over cache deletion, e.g. via an about:history page.
This commit is contained in:
parent
42eaea1043
commit
163e8e5b44
Notes:
github-actions[bot]
2025-10-14 11:41:38 +00:00
Author: https://github.com/trflynn89
Commit: 163e8e5b44
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/6435
10 changed files with 84 additions and 10 deletions
|
@ -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();
|
||||
|
||||
|
|
|
@ -153,6 +153,11 @@ CacheEntryWriter::CacheEntryWriter(DiskCache& disk_cache, CacheIndex& index, u64
|
|||
|
||||
ErrorOr<void> 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<void> 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<void(u64)> 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<void(u64)> 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<void> CacheEntryReader::read_and_validate_footer()
|
||||
{
|
||||
TRY(m_file->seek(m_data_offset + m_data_size, SeekMode::SetPosition));
|
||||
|
|
|
@ -57,6 +57,8 @@ public:
|
|||
|
||||
void remove();
|
||||
|
||||
void mark_for_deletion(Badge<DiskCache>) { 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<void> read_and_validate_footer();
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ ErrorOr<CacheIndex> 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);
|
||||
|
|
|
@ -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<Entry&> 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 };
|
||||
};
|
||||
|
|
|
@ -4,7 +4,9 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibCore/DirIterator.h>
|
||||
#include <LibCore/StandardPaths.h>
|
||||
#include <LibFileSystem/FileSystem.h>
|
||||
#include <LibURL/URL.h>
|
||||
#include <RequestServer/Cache/DiskCache.h>
|
||||
#include <RequestServer/Cache/Utilities.h>
|
||||
|
@ -90,6 +92,28 @@ Optional<CacheEntryReader&> DiskCache::open_entry(URL::URL const& url, StringVie
|
|||
return static_cast<CacheEntryReader&>(**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>, CacheEntry const& cache_entry)
|
||||
{
|
||||
auto address = reinterpret_cast<FlatPtr>(&cache_entry);
|
||||
|
|
|
@ -26,6 +26,7 @@ public:
|
|||
|
||||
Optional<CacheEntryWriter&> create_entry(URL::URL const&, StringView method, u32 status_code, Optional<String> reason_phrase, HTTP::HeaderMap const&, UnixDateTime request_time);
|
||||
Optional<CacheEntryReader&> open_entry(URL::URL const&, StringView method);
|
||||
void clear_cache();
|
||||
|
||||
LexicalPath const& cache_directory() { return m_cache_directory; }
|
||||
|
||||
|
|
|
@ -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<ByteString> protocols, Vector<ByteString> extensions, HTTP::HeaderMap additional_request_headers)
|
||||
{
|
||||
auto host = url.serialized_host().to_byte_string();
|
||||
|
|
|
@ -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<ByteString>, Vector<ByteString>, HTTP::HeaderMap) override;
|
||||
virtual void websocket_send(i64 websocket_id, bool, ByteBuffer) override;
|
||||
virtual void websocket_close(i64 websocket_id, u16, ByteString) override;
|
||||
|
|
|
@ -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<ByteString> protocols, Vector<ByteString> extensions, HTTP::HeaderMap additional_request_headers) =|
|
||||
websocket_send(i64 websocket_id, bool is_text, ByteBuffer data) =|
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue