1
#include "source/extensions/upstreams/http/dynamic_modules/config.h"
2

            
3
#include "source/common/protobuf/utility.h"
4
#include "source/extensions/dynamic_modules/dynamic_modules.h"
5
#include "source/extensions/upstreams/http/dynamic_modules/upstream_request.h"
6

            
7
#include "absl/container/flat_hash_map.h"
8

            
9
namespace Envoy {
10
namespace Extensions {
11
namespace Upstreams {
12
namespace Http {
13
namespace DynamicModules {
14

            
15
namespace {
16

            
17
// Thread-local cache of BridgeConfig instances keyed by proto config hash.
18
// This ensures that on_bridge_config_new is called once per unique configuration per
19
// worker thread, matching the lifecycle semantics of other dynamic module extension points.
20
thread_local absl::flat_hash_map<uint64_t, BridgeConfigSharedPtr> bridge_config_cache;
21

            
22
absl::StatusOr<BridgeConfigSharedPtr> getOrCreateBridgeConfig(
23
10
    const envoy::extensions::upstreams::http::dynamic_modules::v3::Config& proto_config) {
24
10
  const uint64_t cache_key = MessageUtil::hash(proto_config);
25
10
  auto it = bridge_config_cache.find(cache_key);
26
10
  if (it != bridge_config_cache.end()) {
27
3
    return it->second;
28
3
  }
29

            
30
7
  const auto& module_config = proto_config.dynamic_module_config();
31
7
  auto dynamic_module = Envoy::Extensions::DynamicModules::newDynamicModuleByName(
32
7
      module_config.name(), module_config.do_not_close(), module_config.load_globally());
33
7
  if (!dynamic_module.ok()) {
34
1
    return absl::InvalidArgumentError("failed to load dynamic module: " +
35
1
                                      std::string(dynamic_module.status().message()));
36
1
  }
37

            
38
6
  std::string bridge_config_bytes;
39
6
  if (proto_config.has_bridge_config()) {
40
3
    auto config_or_error = MessageUtil::anyToBytes(proto_config.bridge_config());
41
3
    if (!config_or_error.ok()) {
42
1
      return absl::InvalidArgumentError("failed to parse bridge_config: " +
43
1
                                        std::string(config_or_error.status().message()));
44
1
    }
45
2
    bridge_config_bytes = std::move(config_or_error.value());
46
2
  }
47

            
48
5
  auto bridge_config = BridgeConfig::create(proto_config.bridge_name(), bridge_config_bytes,
49
5
                                            std::move(dynamic_module.value()));
50
5
  if (!bridge_config.ok()) {
51
1
    return bridge_config.status();
52
1
  }
53

            
54
4
  bridge_config_cache[cache_key] = bridge_config.value();
55
4
  return bridge_config.value();
56
5
}
57

            
58
} // namespace
59

            
60
Router::GenericConnPoolPtr DynamicModuleGenericConnPoolFactory::createGenericConnPool(
61
    Upstream::HostConstSharedPtr host, Upstream::ThreadLocalCluster& thread_local_cluster,
62
    Router::GenericConnPoolFactory::UpstreamProtocol, Upstream::ResourcePriority priority,
63
    absl::optional<Envoy::Http::Protocol>, Upstream::LoadBalancerContext* ctx,
64
10
    const Protobuf::Message& config) const {
65
10
  const auto& typed_config =
66
10
      dynamic_cast<const envoy::extensions::upstreams::http::dynamic_modules::v3::Config&>(config);
67

            
68
10
  auto bridge_config = getOrCreateBridgeConfig(typed_config);
69
10
  if (!bridge_config.ok()) {
70
3
    ENVOY_LOG_MISC(error, "failed to create bridge config: {}", bridge_config.status().message());
71
3
    return nullptr;
72
3
  }
73

            
74
7
  auto ret = std::make_unique<TcpConnPool>(host, thread_local_cluster, priority, ctx,
75
7
                                           bridge_config.value());
76
7
  return (ret->valid() ? std::move(ret) : nullptr);
77
10
}
78

            
79
REGISTER_FACTORY(DynamicModuleGenericConnPoolFactory, Router::GenericConnPoolFactory);
80

            
81
} // namespace DynamicModules
82
} // namespace Http
83
} // namespace Upstreams
84
} // namespace Extensions
85
} // namespace Envoy