/* * Copyright (c) 2025, Tim Flynn * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include #include #include namespace RequestServer { struct [[gnu::packed]] CacheHeader { static ErrorOr read_from_stream(Stream&); ErrorOr write_to_stream(Stream&) const; static constexpr auto CACHE_MAGIC = 0xcafef00du; static constexpr auto CACHE_VERSION = 1; u32 magic { CACHE_MAGIC }; u32 version { CACHE_VERSION }; u32 url_size { 0 }; u32 url_hash { 0 }; u32 status_code { 0 }; u32 reason_phrase_size { 0 }; u32 reason_phrase_hash { 0 }; u32 headers_size { 0 }; u32 headers_hash { 0 }; }; struct [[gnu::packed]] CacheFooter { static ErrorOr read_from_stream(Stream&); ErrorOr write_to_stream(Stream&) const; u64 data_size { 0 }; u32 crc32 { 0 }; }; // A cache entry is an amalgamation of all information needed to reconstruct HTTP responses. It is created once we have // received the response headers for a request. The body is streamed into the entry as it is received. The cache format // on disk is: // // [CacheHeader][URL][ReasonPhrase][HttpHeaders][Data][CacheFooter] class CacheEntry { public: virtual ~CacheEntry() = default; void remove(); void mark_for_deletion(Badge) { m_marked_for_deletion = true; } protected: CacheEntry(DiskCache&, CacheIndex&, u64 cache_key, String url, LexicalPath, CacheHeader); void close_and_destory_cache_entry(); DiskCache& m_disk_cache; CacheIndex& m_index; u64 m_cache_key { 0 }; String m_url; LexicalPath m_path; CacheHeader m_cache_header; CacheFooter m_cache_footer; bool m_marked_for_deletion { false }; }; class CacheEntryWriter : public CacheEntry { public: static ErrorOr> create(DiskCache&, CacheIndex&, u64 cache_key, String url, u32 status_code, Optional reason_phrase, HTTP::HeaderMap const&, UnixDateTime request_time); virtual ~CacheEntryWriter() override = default; ErrorOr write_data(ReadonlyBytes); ErrorOr flush(); private: CacheEntryWriter(DiskCache&, CacheIndex&, u64 cache_key, String url, LexicalPath, NonnullOwnPtr, CacheHeader, UnixDateTime request_time); NonnullOwnPtr m_file; UnixDateTime m_request_time; UnixDateTime m_response_time; }; class CacheEntryReader : public CacheEntry { public: static ErrorOr> create(DiskCache&, CacheIndex&, u64 cache_key, u64 data_size); virtual ~CacheEntryReader() override = default; 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::HeaderMap const& headers() const { return m_headers; } private: CacheEntryReader(DiskCache&, CacheIndex&, u64 cache_key, String url, LexicalPath, NonnullOwnPtr, int fd, CacheHeader, Optional reason_phrase, HTTP::HeaderMap, u64 data_offset, u64 data_size); void pipe_without_blocking(); void pipe_complete(); void pipe_error(Error); ErrorOr read_and_validate_footer(); NonnullOwnPtr m_file; int m_fd { -1 }; RefPtr m_pipe_write_notifier; int m_pipe_fd { -1 }; Function m_on_pipe_complete; Function m_on_pipe_error; u64 m_bytes_piped { 0 }; Optional m_reason_phrase; HTTP::HeaderMap m_headers; u64 const m_data_offset { 0 }; u64 const m_data_size { 0 }; }; }