1
#include <memory>
2
#include <string>
3

            
4
#include "envoy/extensions/http/cache/file_system_http_cache/v3/file_system_http_cache.pb.h"
5
#include "envoy/extensions/http/cache/file_system_http_cache/v3/file_system_http_cache.pb.validate.h"
6
#include "envoy/registry/registry.h"
7

            
8
#include "source/extensions/common/async_files/async_file_manager_factory.h"
9
#include "source/extensions/filters/http/cache/http_cache.h"
10
#include "source/extensions/http/cache/file_system_http_cache/cache_eviction_thread.h"
11
#include "source/extensions/http/cache/file_system_http_cache/file_system_http_cache.h"
12

            
13
namespace Envoy {
14
namespace Extensions {
15
namespace HttpFilters {
16
namespace Cache {
17
namespace FileSystemHttpCache {
18
namespace {
19

            
20
/**
21
 * Returns a copy of the original ConfigProto with a slash appended to cache_path
22
 * if one was not present.
23
 * @param original the original ConfigProto.
24
 * @return the normalized ConfigProto.
25
 */
26
65
ConfigProto normalizeConfig(const ConfigProto& original) {
27
65
  ConfigProto config = original;
28
65
  if (!absl::EndsWith(config.cache_path(), "/") && !absl::EndsWith(config.cache_path(), "\\")) {
29
3
    config.set_cache_path(absl::StrCat(config.cache_path(), "/"));
30
3
  }
31
65
  return config;
32
65
}
33

            
34
/**
35
 * A singleton that acts as a factory for generating and looking up FileSystemHttpCaches.
36
 * When given equivalent configs, the singleton returns pointers to the same cache.
37
 * When given different configs, the singleton returns different cache instances.
38
 * If given configs with the same cache_path but different configuration,
39
 * an exception is thrown, as it doesn't make sense two operate two caches in the
40
 * same path with different configurations.
41
 */
42
class CacheSingleton : public Envoy::Singleton::Instance {
43
public:
44
  CacheSingleton(
45
      std::shared_ptr<Common::AsyncFiles::AsyncFileManagerFactory>&& async_file_manager_factory,
46
      Thread::ThreadFactory& thread_factory)
47
62
      : async_file_manager_factory_(async_file_manager_factory),
48
62
        cache_eviction_thread_(thread_factory) {}
49

            
50
  std::shared_ptr<FileSystemHttpCache> get(std::shared_ptr<CacheSingleton> singleton,
51
                                           const ConfigProto& non_normalized_config,
52
65
                                           Stats::Scope& stats_scope) {
53
65
    std::shared_ptr<FileSystemHttpCache> cache;
54
65
    ConfigProto config = normalizeConfig(non_normalized_config);
55
65
    auto key = config.cache_path();
56
65
    absl::MutexLock lock(mu_);
57
65
    auto it = caches_.find(key);
58
65
    if (it != caches_.end()) {
59
2
      cache = it->second.lock();
60
2
    }
61
65
    if (!cache) {
62
63
      std::shared_ptr<Common::AsyncFiles::AsyncFileManager> async_file_manager =
63
63
          async_file_manager_factory_->getAsyncFileManager(config.manager_config());
64
63
      cache = std::make_shared<FileSystemHttpCache>(singleton, cache_eviction_thread_,
65
63
                                                    std::move(config),
66
63
                                                    std::move(async_file_manager), stats_scope);
67
63
      caches_[key] = cache;
68
63
    } else if (!Protobuf::util::MessageDifferencer::Equals(cache->config(), config)) {
69
1
      throw EnvoyException(
70
1
          fmt::format("mismatched FileSystemHttpCacheConfig with same path\n{}\nvs.\n{}",
71
1
                      cache->config().DebugString(), config.DebugString()));
72
1
    }
73
64
    return cache;
74
65
  }
75

            
76
private:
77
  std::shared_ptr<Common::AsyncFiles::AsyncFileManagerFactory> async_file_manager_factory_;
78
  CacheEvictionThread cache_eviction_thread_;
79
  absl::Mutex mu_;
80
  // We keep weak_ptr here so the caches can be destroyed if the config is updated to stop using
81
  // that config of cache. The caches each keep shared_ptrs to this singleton, which keeps the
82
  // singleton from being destroyed unless it's no longer keeping track of any caches.
83
  // (The singleton shared_ptr is *only* held by cache instances.)
84
  absl::flat_hash_map<std::string, std::weak_ptr<FileSystemHttpCache>> caches_ ABSL_GUARDED_BY(mu_);
85
};
86

            
87
SINGLETON_MANAGER_REGISTRATION(file_system_http_cache_singleton);
88

            
89
class FileSystemHttpCacheFactory : public HttpCacheFactory {
90
public:
91
  // From UntypedFactory
92
5
  std::string name() const override { return std::string{FileSystemHttpCache::name()}; }
93
  // From TypedFactory
94
2
  ProtobufTypes::MessagePtr createEmptyConfigProto() override {
95
2
    return std::make_unique<ConfigProto>();
96
2
  }
97
  // From HttpCacheFactory
98
  std::shared_ptr<HttpCache>
99
  getCache(const envoy::extensions::filters::http::cache::v3::CacheConfig& filter_config,
100
65
           Server::Configuration::FactoryContext& context) override {
101
65
    ConfigProto config;
102
65
    THROW_IF_NOT_OK(MessageUtil::unpackTo(filter_config.typed_config(), config));
103
65
    std::shared_ptr<CacheSingleton> caches =
104
65
        context.serverFactoryContext().singletonManager().getTyped<CacheSingleton>(
105
65
            SINGLETON_MANAGER_REGISTERED_NAME(file_system_http_cache_singleton), [&context] {
106
62
              return std::make_shared<CacheSingleton>(
107
62
                  Common::AsyncFiles::AsyncFileManagerFactory::singleton(
108
62
                      &context.serverFactoryContext().singletonManager()),
109
62
                  context.serverFactoryContext().api().threadFactory());
110
62
            });
111
65
    return caches->get(caches, config, context.scope());
112
65
  }
113
};
114

            
115
static Registry::RegisterFactory<FileSystemHttpCacheFactory, HttpCacheFactory> register_;
116

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