Line data Source code
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 0 : ConfigProto normalizeConfig(const ConfigProto& original) { 27 0 : ConfigProto config = original; 28 0 : if (!absl::EndsWith(config.cache_path(), "/") && !absl::EndsWith(config.cache_path(), "\\")) { 29 0 : config.set_cache_path(absl::StrCat(config.cache_path(), "/")); 30 0 : } 31 0 : return config; 32 0 : } 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 : : async_file_manager_factory_(async_file_manager_factory), 48 0 : cache_eviction_thread_(thread_factory) {} 49 : 50 : std::shared_ptr<FileSystemHttpCache> get(std::shared_ptr<CacheSingleton> singleton, 51 : const ConfigProto& non_normalized_config, 52 0 : Stats::Scope& stats_scope) { 53 0 : std::shared_ptr<FileSystemHttpCache> cache; 54 0 : ConfigProto config = normalizeConfig(non_normalized_config); 55 0 : auto key = config.cache_path(); 56 0 : absl::MutexLock lock(&mu_); 57 0 : auto it = caches_.find(key); 58 0 : if (it != caches_.end()) { 59 0 : cache = it->second.lock(); 60 0 : } 61 0 : if (!cache) { 62 0 : std::shared_ptr<Common::AsyncFiles::AsyncFileManager> async_file_manager = 63 0 : async_file_manager_factory_->getAsyncFileManager(config.manager_config()); 64 0 : cache = std::make_shared<FileSystemHttpCache>(singleton, cache_eviction_thread_, 65 0 : std::move(config), 66 0 : std::move(async_file_manager), stats_scope); 67 0 : caches_[key] = cache; 68 0 : } else if (!Protobuf::util::MessageDifferencer::Equals(cache->config(), config)) { 69 0 : throw EnvoyException( 70 0 : fmt::format("mismatched FileSystemHttpCacheConfig with same path\n{}\nvs.\n{}", 71 0 : cache->config().DebugString(), config.DebugString())); 72 0 : } 73 0 : return cache; 74 0 : } 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 38 : std::string name() const override { return std::string{FileSystemHttpCache::name()}; } 93 : // From TypedFactory 94 1 : ProtobufTypes::MessagePtr createEmptyConfigProto() override { 95 1 : return std::make_unique<ConfigProto>(); 96 1 : } 97 : // From HttpCacheFactory 98 : std::shared_ptr<HttpCache> 99 : getCache(const envoy::extensions::filters::http::cache::v3::CacheConfig& filter_config, 100 0 : Server::Configuration::FactoryContext& context) override { 101 0 : ConfigProto config; 102 0 : MessageUtil::unpackTo(filter_config.typed_config(), config); 103 0 : std::shared_ptr<CacheSingleton> caches = 104 0 : context.serverFactoryContext().singletonManager().getTyped<CacheSingleton>( 105 0 : SINGLETON_MANAGER_REGISTERED_NAME(file_system_http_cache_singleton), [&context] { 106 0 : return std::make_shared<CacheSingleton>( 107 0 : Common::AsyncFiles::AsyncFileManagerFactory::singleton( 108 0 : &context.serverFactoryContext().singletonManager()), 109 0 : context.serverFactoryContext().api().threadFactory()); 110 0 : }); 111 0 : return caches->get(caches, config, context.scope()); 112 0 : } 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