#pragma once #include "envoy/http/conn_pool.h" #include "envoy/http/metadata_interface.h" #include "envoy/http/protocol.h" #include "envoy/router/router.h" #include "envoy/upstream/cluster_manager.h" #include "envoy/upstream/host_description.h" #include "common/http/header_map_impl.h" #include "common/router/router.h" #include "common/router/upstream_request.h" #include "extensions/upstreams/http/http/upstream_request.h" namespace Envoy { namespace { /** * A helper to add header into header map from metadata. The added value is `metadata[key1][key2]`. * * @param header_map The mutable header map. * @param header_name The target header entry in the `header_map`. * @param metadata The source of header value. * @param key1 The key to the value in metadata. * @param key2 The second key to the value after `key1` is selected. */ void addHeader(Envoy::Http::RequestHeaderMap& header_map, absl::string_view header_name, const envoy::config::core::v3::Metadata& metadata, absl::string_view key1, absl::string_view key2) { if (auto filter_metadata = metadata.filter_metadata().find(key1); filter_metadata != metadata.filter_metadata().end()) { const ProtobufWkt::Struct& data_struct = filter_metadata->second; const auto& fields = data_struct.fields(); if (auto iter = fields.find(key2); iter != fields.end()) { if (iter->second.kind_case() == ProtobufWkt::Value::kStringValue) { header_map.setCopy(Envoy::Http::LowerCaseString(std::string(header_name)), iter->second.string_value()); } } } } } // namespace // The http upstream to remember the host and encode the host metadata and cluster metadata into // upstream http request. class PerHostHttpUpstream : public Extensions::Upstreams::Http::Http::HttpUpstream { public: PerHostHttpUpstream(Router::UpstreamToDownstream& upstream_request, Envoy::Http::RequestEncoder* encoder, Upstream::HostDescriptionConstSharedPtr host) : HttpUpstream(upstream_request, encoder), host_(host) {} Http::Status encodeHeaders(const Envoy::Http::RequestHeaderMap& headers, bool end_stream) override { auto dup = Envoy::Http::RequestHeaderMapImpl::create(); Envoy::Http::HeaderMapImpl::copyFrom(*dup, headers); dup->setCopy(Envoy::Http::LowerCaseString("X-foo"), "foo-common"); addHeader(*dup, "X-cluster-foo", host_->cluster().metadata(), "foo", "bar"); if (host_->metadata() != nullptr) { addHeader(*dup, "X-host-foo", *host_->metadata(), "foo", "bar"); } return HttpUpstream::encodeHeaders(*dup, end_stream); } private: Upstream::HostDescriptionConstSharedPtr host_; }; class PerHostHttpConnPool : public Extensions::Upstreams::Http::Http::HttpConnPool { public: PerHostHttpConnPool(Upstream::ClusterManager& cm, bool is_connect, const Router::RouteEntry& route_entry, absl::optional downstream_protocol, Upstream::LoadBalancerContext* ctx) : HttpConnPool(cm, is_connect, route_entry, downstream_protocol, ctx) {} void onPoolReady(Envoy::Http::RequestEncoder& callbacks_encoder, Upstream::HostDescriptionConstSharedPtr host, const StreamInfo::StreamInfo& info, absl::optional protocol) override { conn_pool_stream_handle_ = nullptr; auto upstream = std::make_unique(callbacks_->upstreamToDownstream(), &callbacks_encoder, host); callbacks_->onPoolReady(std::move(upstream), host, callbacks_encoder.getStream().connectionLocalAddress(), info, protocol); } }; /** * Config registration for the HttpConnPool. @see Router::GenericConnPoolFactory */ class PerHostGenericConnPoolFactory : public Router::GenericConnPoolFactory { public: std::string name() const override { return "envoy.filters.connection_pools.http.per_host"; } std::string category() const override { return "envoy.upstreams"; } Router::GenericConnPoolPtr createGenericConnPool(Upstream::ClusterManager& cm, bool is_connect, const Router::RouteEntry& route_entry, absl::optional downstream_protocol, Upstream::LoadBalancerContext* ctx) const override { if (is_connect) { // This example factory doesn't support terminating CONNECT stream. return nullptr; } auto upstream_http_conn_pool = std::make_unique( cm, is_connect, route_entry, downstream_protocol, ctx); return (upstream_http_conn_pool->valid() ? std::move(upstream_http_conn_pool) : nullptr); } ProtobufTypes::MessagePtr createEmptyConfigProto() override { return std::make_unique(); } }; } // namespace Envoy