1
#pragma once
2

            
3
#include <memory>
4

            
5
#include "source/extensions/common/async_files/async_file_handle.h"
6
#include "source/extensions/filters/http/cache/http_cache.h"
7
#include "source/extensions/http/cache/file_system_http_cache/cache_file_fixed_block.h"
8
#include "source/extensions/http/cache/file_system_http_cache/cache_file_header.pb.h"
9

            
10
namespace Envoy {
11
namespace Extensions {
12
namespace HttpFilters {
13
namespace Cache {
14
namespace FileSystemHttpCache {
15

            
16
using ::Envoy::Extensions::Common::AsyncFiles::AsyncFileHandle;
17
using ::Envoy::Extensions::Common::AsyncFiles::CancelFunction;
18

            
19
class FileLookupContext;
20
class FileSystemHttpCache;
21

            
22
class DontInsertContext : public InsertContext {
23
public:
24
1
  explicit DontInsertContext(Event::Dispatcher& dispatcher) : dispatcher_(dispatcher) {}
25
  void insertHeaders(const Http::ResponseHeaderMap&, const ResponseMetadata&,
26
1
                     InsertCallback insert_complete, bool) override {
27
1
    dispatcher_.post([cb = std::move(insert_complete)]() mutable { cb(false); });
28
1
  }
29
1
  void insertBody(const Buffer::Instance&, InsertCallback ready_for_next_chunk, bool) override {
30
1
    dispatcher_.post([cb = std::move(ready_for_next_chunk)]() mutable { cb(false); });
31
1
  }
32
1
  void insertTrailers(const Http::ResponseTrailerMap&, InsertCallback insert_complete) override {
33
1
    dispatcher_.post([cb = std::move(insert_complete)]() mutable { cb(false); });
34
1
  }
35
1
  void onDestroy() override {};
36

            
37
private:
38
  Event::Dispatcher& dispatcher_;
39
};
40

            
41
class FileInsertContext : public InsertContext, public Logger::Loggable<Logger::Id::cache_filter> {
42
public:
43
  FileInsertContext(std::shared_ptr<FileSystemHttpCache> cache,
44
                    std::unique_ptr<FileLookupContext> lookup_context);
45
  void insertHeaders(const Http::ResponseHeaderMap& response_headers,
46
                     const ResponseMetadata& metadata, InsertCallback insert_complete,
47
                     bool end_stream) override;
48
  void insertBody(const Buffer::Instance& chunk, InsertCallback ready_for_next_chunk,
49
                  bool end_stream) override;
50
  void insertTrailers(const Http::ResponseTrailerMap& trailers,
51
                      InsertCallback insert_complete) override;
52
  void onDestroy() override;
53

            
54
private:
55
  Event::Dispatcher* dispatcher() const;
56
  // The sequence of actions involved in writing the cache entry to a file. Each
57
  // of these actions are posted to an async file thread, and the results posted back
58
  // to the dispatcher, so the callbacks are run on the original filter's thread.
59

            
60
  // The first step of writing the cache entry to a file. On success calls
61
  // writeEmptyHeaderBlock, on failure calls the InsertCallback with false which
62
  // should abort the operation.
63
  void createFile();
64
  // An empty header block is written at the start of the file, making room for
65
  // a populated header block to be written later. On success calls writeHeaderProto,
66
  // on failure calls the InsertCallback with false which should abort the operation.
67
  void writeEmptyHeaderBlock();
68
  // Writes the http headers and updates the headers size in the in-memory header_block_.
69
  // On success and end_stream, calls commit. On success and not end_stream, calls the
70
  // InsertCallback with true to move on to the data section. On failure calls the
71
  // InsertCallback with false which should abort the operation.
72
  void writeHeaderProto();
73
  // Helper to call the InsertCallback with true.
74
  void succeedCurrentAction();
75
  // Returns the full path for the cache file matching key_.
76
  std::string pathAndFilename();
77
  // Starts the commit process; rewrites the header block of the current file. On
78
  // success calls commitMeasureExisting. On failure calls the InsertCallback with false
79
  // which should abort the operation.
80
  void commit();
81
  // Checks for the presence and size of a pre-existing cache entry at the destination
82
  // path. If the file does not exist or on failure, calls commitUnlinkExisting, as it
83
  // doesn't hurt to try the delete in case the stat failure was e.g. a lack of read
84
  // permission. On success passes the size from stat, on failure passes 0.
85
  void commitMeasureExisting();
86
  // Deletes the pre-existing file in the pathAndFilename() location. On success updates
87
  // cache metrics with the measured size. Regardless of success calls commitCreateHardLink.
88
  void commitUnlinkExisting(size_t file_size);
89
  // Creates a hard link at pathAndFilename() to the current file. On success calls
90
  // InsertCallback with true. On failure calls it with false which should abort the
91
  // operation.
92
  void commitCreateHardLink();
93
  CacheFileHeader cache_file_header_proto_;
94
  bool end_stream_after_headers_ = false;
95
  std::unique_ptr<FileLookupContext> lookup_context_;
96
  Key key_;
97
  std::shared_ptr<FileSystemHttpCache> cache_;
98
  std::shared_ptr<Cleanup> cleanup_;
99
  AsyncFileHandle file_handle_;
100
  absl::AnyInvocable<void(bool)> callback_in_flight_;
101
  CancelFunction cancel_action_in_flight_;
102
  CacheFileFixedBlock header_block_;
103

            
104
  /**
105
   * If seen_end_stream_ is not true (i.e. InsertContext has not yet delivered the
106
   * entire response), cancel insertion. Called by InsertContext onDestroy.
107
   */
108
  void cancelIfIncomplete();
109

            
110
  /**
111
   * Cancels any action in flight, calls any uncalled completion callbacks with false,
112
   * and closes the file if open.
113
   * @param err a string to log with the failure.
114
   */
115
  void cancelInsert(absl::string_view err = "");
116
};
117

            
118
} // namespace FileSystemHttpCache
119
} // namespace Cache
120
} // namespace HttpFilters
121
} // namespace Extensions
122
} // namespace Envoy