Skip to content

Commit 5384f84

Browse files
trflynn89awesomekling
authored andcommitted
RequestServer: Create disk cache writers for new requests immediately
We previously waited until we received all response headers before we would create the cache entry. We now create one immediately, and handle writing the headers in its own function. This will allow us to know if a cache entry writer already exists for a given cache key, and thus prevent creating a second writer at the same time.
1 parent d67dc23 commit 5384f84

File tree

7 files changed

+72
-41
lines changed

7 files changed

+72
-41
lines changed

Services/RequestServer/Cache/CacheEntry.cpp

Lines changed: 47 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -90,16 +90,49 @@ void CacheEntry::close_and_destroy_cache_entry()
9090
m_disk_cache.cache_entry_closed({}, *this);
9191
}
9292

93-
ErrorOr<NonnullOwnPtr<CacheEntryWriter>> CacheEntryWriter::create(DiskCache& disk_cache, CacheIndex& index, u64 cache_key, String url, u32 status_code, Optional<String> reason_phrase, HTTP::HeaderMap const& headers, UnixDateTime request_time)
93+
ErrorOr<NonnullOwnPtr<CacheEntryWriter>> CacheEntryWriter::create(DiskCache& disk_cache, CacheIndex& index, u64 cache_key, String url, UnixDateTime request_time)
9494
{
9595
auto path = path_for_cache_key(disk_cache.cache_directory(), cache_key);
9696

9797
auto unbuffered_file = TRY(Core::File::open(path.string(), Core::File::OpenMode::Write));
9898
auto file = TRY(Core::OutputBufferedFile::create(move(unbuffered_file)));
9999

100100
CacheHeader cache_header;
101+
cache_header.url_size = url.byte_count();
102+
cache_header.url_hash = url.hash();
103+
104+
return adopt_own(*new CacheEntryWriter { disk_cache, index, cache_key, move(url), move(path), move(file), cache_header, request_time });
105+
}
106+
107+
CacheEntryWriter::CacheEntryWriter(DiskCache& disk_cache, CacheIndex& index, u64 cache_key, String url, LexicalPath path, NonnullOwnPtr<Core::OutputBufferedFile> file, CacheHeader cache_header, UnixDateTime request_time)
108+
: CacheEntry(disk_cache, index, cache_key, move(url), move(path), cache_header)
109+
, m_file(move(file))
110+
, m_request_time(request_time)
111+
, m_response_time(UnixDateTime::now())
112+
{
113+
}
114+
115+
ErrorOr<void> CacheEntryWriter::write_headers(u32 status_code, Optional<String> reason_phrase, HTTP::HeaderMap const& headers)
116+
{
117+
if (m_marked_for_deletion) {
118+
close_and_destroy_cache_entry();
119+
return Error::from_string_literal("Cache entry has been deleted");
120+
}
121+
122+
m_cache_header.status_code = status_code;
123+
124+
if (reason_phrase.has_value()) {
125+
m_cache_header.reason_phrase_size = reason_phrase->byte_count();
126+
m_cache_header.reason_phrase_hash = reason_phrase->hash();
127+
}
101128

102129
auto result = [&]() -> ErrorOr<void> {
130+
if (!is_cacheable(status_code, headers))
131+
return Error::from_string_literal("Response is not cacheable");
132+
133+
if (auto freshness = calculate_freshness_lifetime(headers); freshness.is_negative() || freshness.is_zero())
134+
return Error::from_string_literal("Response has already expired");
135+
103136
StringBuilder builder;
104137
auto headers_serializer = TRY(JsonArraySerializer<>::try_create(builder));
105138

@@ -114,41 +147,29 @@ ErrorOr<NonnullOwnPtr<CacheEntryWriter>> CacheEntryWriter::create(DiskCache& dis
114147
}
115148

116149
TRY(headers_serializer.finish());
117-
118-
cache_header.url_size = url.byte_count();
119-
cache_header.url_hash = url.hash();
120-
121-
cache_header.status_code = status_code;
122-
cache_header.reason_phrase_size = reason_phrase.has_value() ? reason_phrase->byte_count() : 0;
123-
cache_header.reason_phrase_hash = reason_phrase.has_value() ? reason_phrase->hash() : 0;
124-
125150
auto serialized_headers = builder.string_view();
126-
cache_header.headers_size = serialized_headers.length();
127-
cache_header.headers_hash = serialized_headers.hash();
151+
m_cache_header.headers_size = serialized_headers.length();
152+
m_cache_header.headers_hash = serialized_headers.hash();
128153

129-
TRY(file->write_value(cache_header));
130-
TRY(file->write_until_depleted(url));
154+
TRY(m_file->write_value(m_cache_header));
155+
TRY(m_file->write_until_depleted(m_url));
131156
if (reason_phrase.has_value())
132-
TRY(file->write_until_depleted(*reason_phrase));
133-
TRY(file->write_until_depleted(serialized_headers));
157+
TRY(m_file->write_until_depleted(*reason_phrase));
158+
TRY(m_file->write_until_depleted(serialized_headers));
134159

135160
return {};
136161
}();
137162

138163
if (result.is_error()) {
139-
(void)FileSystem::remove(path.string(), FileSystem::RecursionMode::Disallowed);
164+
dbgln("\033[31;1mUnable to write headers to cache entry for\033[0m {}: {}", m_url, result.error());
165+
166+
remove();
167+
close_and_destroy_cache_entry();
168+
140169
return result.release_error();
141170
}
142171

143-
return adopt_own(*new CacheEntryWriter { disk_cache, index, cache_key, move(url), path, move(file), cache_header, request_time });
144-
}
145-
146-
CacheEntryWriter::CacheEntryWriter(DiskCache& disk_cache, CacheIndex& index, u64 cache_key, String url, LexicalPath path, NonnullOwnPtr<Core::OutputBufferedFile> file, CacheHeader cache_header, UnixDateTime request_time)
147-
: CacheEntry(disk_cache, index, cache_key, move(url), move(path), cache_header)
148-
, m_file(move(file))
149-
, m_request_time(request_time)
150-
, m_response_time(UnixDateTime::now())
151-
{
172+
return {};
152173
}
153174

154175
ErrorOr<void> CacheEntryWriter::write_data(ReadonlyBytes data)
@@ -159,7 +180,7 @@ ErrorOr<void> CacheEntryWriter::write_data(ReadonlyBytes data)
159180
}
160181

161182
if (auto result = m_file->write_until_depleted(data); result.is_error()) {
162-
dbgln("\033[31;1mUnable to write to cache entry for\033[0m {}: {}", m_url, result.error());
183+
dbgln("\033[31;1mUnable to write data to cache entry for\033[0m {}: {}", m_url, result.error());
163184

164185
remove();
165186
close_and_destroy_cache_entry();

Services/RequestServer/Cache/CacheEntry.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,10 @@ class CacheEntry {
8080

8181
class CacheEntryWriter : public CacheEntry {
8282
public:
83-
static ErrorOr<NonnullOwnPtr<CacheEntryWriter>> create(DiskCache&, CacheIndex&, u64 cache_key, String url, u32 status_code, Optional<String> reason_phrase, HTTP::HeaderMap const&, UnixDateTime request_time);
83+
static ErrorOr<NonnullOwnPtr<CacheEntryWriter>> create(DiskCache&, CacheIndex&, u64 cache_key, String url, UnixDateTime request_time);
8484
virtual ~CacheEntryWriter() override = default;
8585

86+
ErrorOr<void> write_headers(u32 status_code, Optional<String> reason_phrase, HTTP::HeaderMap const&);
8687
ErrorOr<void> write_data(ReadonlyBytes);
8788
ErrorOr<void> flush();
8889

Services/RequestServer/Cache/DiskCache.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,18 +32,15 @@ DiskCache::DiskCache(NonnullRefPtr<Database::Database> database, LexicalPath cac
3232
{
3333
}
3434

35-
Optional<CacheEntryWriter&> DiskCache::create_entry(URL::URL const& url, StringView method, u32 status_code, Optional<String> reason_phrase, HTTP::HeaderMap const& headers, UnixDateTime request_time)
35+
Optional<CacheEntryWriter&> DiskCache::create_entry(URL::URL const& url, StringView method, UnixDateTime request_time)
3636
{
37-
if (!is_cacheable(method, status_code, headers))
38-
return {};
39-
40-
if (auto freshness = calculate_freshness_lifetime(headers); freshness.is_negative() || freshness.is_zero())
37+
if (!is_cacheable(method))
4138
return {};
4239

4340
auto serialized_url = serialize_url_for_cache_storage(url);
4441
auto cache_key = create_cache_key(serialized_url, method);
4542

46-
auto cache_entry = CacheEntryWriter::create(*this, m_index, cache_key, move(serialized_url), status_code, move(reason_phrase), headers, request_time);
43+
auto cache_entry = CacheEntryWriter::create(*this, m_index, cache_key, move(serialized_url), request_time);
4744
if (cache_entry.is_error()) {
4845
dbgln("\033[31;1mUnable to create cache entry for\033[0m {}: {}", url, cache_entry.error());
4946
return {};
@@ -59,6 +56,9 @@ Optional<CacheEntryWriter&> DiskCache::create_entry(URL::URL const& url, StringV
5956

6057
Optional<CacheEntryReader&> DiskCache::open_entry(URL::URL const& url, StringView method)
6158
{
59+
if (!is_cacheable(method))
60+
return {};
61+
6262
auto serialized_url = serialize_url_for_cache_storage(url);
6363
auto cache_key = create_cache_key(serialized_url, method);
6464

Services/RequestServer/Cache/DiskCache.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
#include <AK/Time.h>
1414
#include <AK/Types.h>
1515
#include <LibDatabase/Database.h>
16-
#include <LibHTTP/HeaderMap.h>
1716
#include <LibURL/Forward.h>
1817
#include <RequestServer/Cache/CacheEntry.h>
1918
#include <RequestServer/Cache/CacheIndex.h>
@@ -24,7 +23,7 @@ class DiskCache {
2423
public:
2524
static ErrorOr<DiskCache> create();
2625

27-
Optional<CacheEntryWriter&> create_entry(URL::URL const&, StringView method, u32 status_code, Optional<String> reason_phrase, HTTP::HeaderMap const&, UnixDateTime request_time);
26+
Optional<CacheEntryWriter&> create_entry(URL::URL const&, StringView method, UnixDateTime request_time);
2827
Optional<CacheEntryReader&> open_entry(URL::URL const&, StringView method);
2928
void clear_cache();
3029

Services/RequestServer/Cache/Utilities.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,18 @@ u64 create_cache_key(StringView url, StringView method)
7171
}
7272

7373
// https://httpwg.org/specs/rfc9111.html#response.cacheability
74-
bool is_cacheable(StringView method, u32 status_code, HTTP::HeaderMap const& headers)
74+
bool is_cacheable(StringView method)
7575
{
7676
// A cache MUST NOT store a response to a request unless:
7777

7878
// * the request method is understood by the cache;
79-
if (!method.is_one_of("GET"sv, "HEAD"sv))
80-
return false;
79+
return method.is_one_of("GET"sv, "HEAD"sv);
80+
}
81+
82+
// https://httpwg.org/specs/rfc9111.html#response.cacheability
83+
bool is_cacheable(u32 status_code, HTTP::HeaderMap const& headers)
84+
{
85+
// A cache MUST NOT store a response to a request unless:
8186

8287
// * the response status code is final (see Section 15 of [HTTP]);
8388
if (status_code < 200)

Services/RequestServer/Cache/Utilities.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ namespace RequestServer {
1717
String serialize_url_for_cache_storage(URL::URL const&);
1818
u64 create_cache_key(StringView url, StringView method);
1919

20-
bool is_cacheable(StringView method, u32 status_code, HTTP::HeaderMap const&);
20+
bool is_cacheable(StringView method);
21+
bool is_cacheable(u32 status_code, HTTP::HeaderMap const&);
2122
bool is_header_exempted_from_storage(StringView name);
2223

2324
AK::Duration calculate_freshness_lifetime(HTTP::HeaderMap const&);

Services/RequestServer/Request.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,8 @@ void Request::handle_initial_state()
172172
transition_to_state(State::ReadCache);
173173
return;
174174
}
175+
176+
m_cache_entry_writer = m_disk_cache->create_entry(m_url, m_method, m_request_start_time);
175177
}
176178

177179
transition_to_state(State::DNSLookup);
@@ -486,8 +488,10 @@ void Request::transfer_headers_to_client_if_needed()
486488
m_status_code = acquire_status_code();
487489
m_client.async_headers_became_available(m_request_id, m_response_headers, m_status_code, m_reason_phrase);
488490

489-
if (m_disk_cache.has_value())
490-
m_cache_entry_writer = m_disk_cache->create_entry(m_url, m_method, m_status_code, m_reason_phrase, m_response_headers, m_request_start_time);
491+
if (m_cache_entry_writer.has_value()) {
492+
if (m_cache_entry_writer->write_headers(m_status_code, m_reason_phrase, m_response_headers).is_error())
493+
m_cache_entry_writer.clear();
494+
}
491495
}
492496

493497
ErrorOr<void> Request::write_queued_bytes_without_blocking()

0 commit comments

Comments
 (0)