From cbe2855866fc2c9cfdf9e6ed685aabe92ffb509e Mon Sep 17 00:00:00 2001 From: "Mark D. Roth" Date: Fri, 3 Dec 2021 08:19:54 -0800 Subject: [PATCH] XdsClient: move resource type parsing out of XdsApi (#28151) * introduce XdsResourceType API and change Listener parsing to use it * converted RouteConfig parsing * convert cluster and endpoint parsing * cleanup * clang-format * attempt to work around compiler problems * move XdsResourceType to its own file, and move endpoint code out of XdsApi * move cluster parsing to its own file * move route config parsing to its own file * move listener parsing to its own file * clang-format * minor cleanup * remove comment * add missing virtual dtor --- BUILD | 13 + CMakeLists.txt | 5 + Makefile | 10 + build_autogenerated.yaml | 12 + config.m4 | 5 + config.w32 | 5 + gRPC-C++.podspec | 14 + gRPC-Core.podspec | 19 + grpc.gemspec | 12 + grpc.gyp | 5 + package.xml | 12 + .../client_channel/lb_policy/xds/cds.cc | 19 +- .../lb_policy/xds/xds_cluster_impl.cc | 14 +- .../lb_policy/xds/xds_cluster_resolver.cc | 30 +- .../resolver/xds/xds_resolver.cc | 51 +- src/core/ext/xds/upb_utils.h | 62 + src/core/ext/xds/xds_api.cc | 3102 +---------------- src/core/ext/xds/xds_api.h | 539 +-- src/core/ext/xds/xds_client.cc | 8 +- src/core/ext/xds/xds_client.h | 16 +- src/core/ext/xds/xds_cluster.cc | 442 +++ src/core/ext/xds/xds_cluster.h | 101 + src/core/ext/xds/xds_common_types.cc | 388 +++ src/core/ext/xds/xds_common_types.h | 110 + src/core/ext/xds/xds_endpoint.cc | 352 ++ src/core/ext/xds/xds_endpoint.h | 132 + src/core/ext/xds/xds_listener.cc | 1012 ++++++ src/core/ext/xds/xds_listener.h | 210 ++ src/core/ext/xds/xds_resource_type.h | 77 + src/core/ext/xds/xds_route_config.cc | 982 ++++++ src/core/ext/xds/xds_route_config.h | 211 ++ src/core/ext/xds/xds_routing.cc | 16 +- src/core/ext/xds/xds_routing.h | 10 +- src/core/ext/xds/xds_server_config_fetcher.cc | 117 +- src/python/grpcio/grpc_core_dependencies.py | 5 + tools/doxygen/Doxyfile.c++.internal | 12 + tools/doxygen/Doxyfile.core.internal | 12 + 37 files changed, 4442 insertions(+), 3700 deletions(-) create mode 100644 src/core/ext/xds/upb_utils.h create mode 100644 src/core/ext/xds/xds_cluster.cc create mode 100644 src/core/ext/xds/xds_cluster.h create mode 100644 src/core/ext/xds/xds_common_types.cc create mode 100644 src/core/ext/xds/xds_common_types.h create mode 100644 src/core/ext/xds/xds_endpoint.cc create mode 100644 src/core/ext/xds/xds_endpoint.h create mode 100644 src/core/ext/xds/xds_listener.cc create mode 100644 src/core/ext/xds/xds_listener.h create mode 100644 src/core/ext/xds/xds_resource_type.h create mode 100644 src/core/ext/xds/xds_route_config.cc create mode 100644 src/core/ext/xds/xds_route_config.h diff --git a/BUILD b/BUILD index 5367b5ec9b3..7c2375695de 100644 --- a/BUILD +++ b/BUILD @@ -2599,8 +2599,13 @@ grpc_cc_library( "src/core/ext/xds/xds_certificate_provider.cc", "src/core/ext/xds/xds_client.cc", "src/core/ext/xds/xds_client_stats.cc", + "src/core/ext/xds/xds_cluster.cc", + "src/core/ext/xds/xds_common_types.cc", + "src/core/ext/xds/xds_endpoint.cc", "src/core/ext/xds/xds_http_fault_filter.cc", "src/core/ext/xds/xds_http_filters.cc", + "src/core/ext/xds/xds_listener.cc", + "src/core/ext/xds/xds_route_config.cc", "src/core/ext/xds/xds_routing.cc", "src/core/lib/security/credentials/xds/xds_credentials.cc", ], @@ -2609,19 +2614,27 @@ grpc_cc_library( "src/core/ext/xds/certificate_provider_registry.h", "src/core/ext/xds/certificate_provider_store.h", "src/core/ext/xds/file_watcher_certificate_provider_factory.h", + "src/core/ext/xds/upb_utils.h", "src/core/ext/xds/xds_api.h", "src/core/ext/xds/xds_bootstrap.h", "src/core/ext/xds/xds_certificate_provider.h", "src/core/ext/xds/xds_channel_args.h", "src/core/ext/xds/xds_client.h", "src/core/ext/xds/xds_client_stats.h", + "src/core/ext/xds/xds_cluster.h", + "src/core/ext/xds/xds_common_types.h", + "src/core/ext/xds/xds_endpoint.h", "src/core/ext/xds/xds_http_fault_filter.h", "src/core/ext/xds/xds_http_filters.h", + "src/core/ext/xds/xds_listener.h", + "src/core/ext/xds/xds_resource_type.h", + "src/core/ext/xds/xds_route_config.h", "src/core/ext/xds/xds_routing.h", "src/core/lib/security/credentials/xds/xds_credentials.h", ], external_deps = [ "absl/functional:bind_front", + "absl/memory", "absl/status:statusor", "absl/strings", "absl/strings:str_format", diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e9174cc44b..d70f964ea34 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1850,8 +1850,13 @@ add_library(grpc src/core/ext/xds/xds_channel_stack_modifier.cc src/core/ext/xds/xds_client.cc src/core/ext/xds/xds_client_stats.cc + src/core/ext/xds/xds_cluster.cc + src/core/ext/xds/xds_common_types.cc + src/core/ext/xds/xds_endpoint.cc src/core/ext/xds/xds_http_fault_filter.cc src/core/ext/xds/xds_http_filters.cc + src/core/ext/xds/xds_listener.cc + src/core/ext/xds/xds_route_config.cc src/core/ext/xds/xds_routing.cc src/core/ext/xds/xds_server_config_fetcher.cc src/core/lib/address_utils/parse_address.cc diff --git a/Makefile b/Makefile index 152c700c87f..4ff3093fe99 100644 --- a/Makefile +++ b/Makefile @@ -1348,8 +1348,13 @@ LIBGRPC_SRC = \ src/core/ext/xds/xds_channel_stack_modifier.cc \ src/core/ext/xds/xds_client.cc \ src/core/ext/xds/xds_client_stats.cc \ + src/core/ext/xds/xds_cluster.cc \ + src/core/ext/xds/xds_common_types.cc \ + src/core/ext/xds/xds_endpoint.cc \ src/core/ext/xds/xds_http_fault_filter.cc \ src/core/ext/xds/xds_http_filters.cc \ + src/core/ext/xds/xds_listener.cc \ + src/core/ext/xds/xds_route_config.cc \ src/core/ext/xds/xds_routing.cc \ src/core/ext/xds/xds_server_config_fetcher.cc \ src/core/lib/address_utils/parse_address.cc \ @@ -2951,8 +2956,13 @@ src/core/ext/xds/xds_certificate_provider.cc: $(OPENSSL_DEP) src/core/ext/xds/xds_channel_stack_modifier.cc: $(OPENSSL_DEP) src/core/ext/xds/xds_client.cc: $(OPENSSL_DEP) src/core/ext/xds/xds_client_stats.cc: $(OPENSSL_DEP) +src/core/ext/xds/xds_cluster.cc: $(OPENSSL_DEP) +src/core/ext/xds/xds_common_types.cc: $(OPENSSL_DEP) +src/core/ext/xds/xds_endpoint.cc: $(OPENSSL_DEP) src/core/ext/xds/xds_http_fault_filter.cc: $(OPENSSL_DEP) src/core/ext/xds/xds_http_filters.cc: $(OPENSSL_DEP) +src/core/ext/xds/xds_listener.cc: $(OPENSSL_DEP) +src/core/ext/xds/xds_route_config.cc: $(OPENSSL_DEP) src/core/ext/xds/xds_routing.cc: $(OPENSSL_DEP) src/core/ext/xds/xds_server_config_fetcher.cc: $(OPENSSL_DEP) src/core/lib/http/httpcli_security_connector.cc: $(OPENSSL_DEP) diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml index 66f5c81cd7a..d781c02ec52 100644 --- a/build_autogenerated.yaml +++ b/build_autogenerated.yaml @@ -715,6 +715,7 @@ libs: - src/core/ext/xds/certificate_provider_registry.h - src/core/ext/xds/certificate_provider_store.h - src/core/ext/xds/file_watcher_certificate_provider_factory.h + - src/core/ext/xds/upb_utils.h - src/core/ext/xds/xds_api.h - src/core/ext/xds/xds_bootstrap.h - src/core/ext/xds/xds_certificate_provider.h @@ -722,8 +723,14 @@ libs: - src/core/ext/xds/xds_channel_stack_modifier.h - src/core/ext/xds/xds_client.h - src/core/ext/xds/xds_client_stats.h + - src/core/ext/xds/xds_cluster.h + - src/core/ext/xds/xds_common_types.h + - src/core/ext/xds/xds_endpoint.h - src/core/ext/xds/xds_http_fault_filter.h - src/core/ext/xds/xds_http_filters.h + - src/core/ext/xds/xds_listener.h + - src/core/ext/xds/xds_resource_type.h + - src/core/ext/xds/xds_route_config.h - src/core/ext/xds/xds_routing.h - src/core/lib/address_utils/parse_address.h - src/core/lib/address_utils/sockaddr_utils.h @@ -1306,8 +1313,13 @@ libs: - src/core/ext/xds/xds_channel_stack_modifier.cc - src/core/ext/xds/xds_client.cc - src/core/ext/xds/xds_client_stats.cc + - src/core/ext/xds/xds_cluster.cc + - src/core/ext/xds/xds_common_types.cc + - src/core/ext/xds/xds_endpoint.cc - src/core/ext/xds/xds_http_fault_filter.cc - src/core/ext/xds/xds_http_filters.cc + - src/core/ext/xds/xds_listener.cc + - src/core/ext/xds/xds_route_config.cc - src/core/ext/xds/xds_routing.cc - src/core/ext/xds/xds_server_config_fetcher.cc - src/core/lib/address_utils/parse_address.cc diff --git a/config.m4 b/config.m4 index a8e3e2f4f59..35f792af291 100644 --- a/config.m4 +++ b/config.m4 @@ -365,8 +365,13 @@ if test "$PHP_GRPC" != "no"; then src/core/ext/xds/xds_channel_stack_modifier.cc \ src/core/ext/xds/xds_client.cc \ src/core/ext/xds/xds_client_stats.cc \ + src/core/ext/xds/xds_cluster.cc \ + src/core/ext/xds/xds_common_types.cc \ + src/core/ext/xds/xds_endpoint.cc \ src/core/ext/xds/xds_http_fault_filter.cc \ src/core/ext/xds/xds_http_filters.cc \ + src/core/ext/xds/xds_listener.cc \ + src/core/ext/xds/xds_route_config.cc \ src/core/ext/xds/xds_routing.cc \ src/core/ext/xds/xds_server_config_fetcher.cc \ src/core/lib/address_utils/parse_address.cc \ diff --git a/config.w32 b/config.w32 index aaa819b5696..6cee84abbc9 100644 --- a/config.w32 +++ b/config.w32 @@ -331,8 +331,13 @@ if (PHP_GRPC != "no") { "src\\core\\ext\\xds\\xds_channel_stack_modifier.cc " + "src\\core\\ext\\xds\\xds_client.cc " + "src\\core\\ext\\xds\\xds_client_stats.cc " + + "src\\core\\ext\\xds\\xds_cluster.cc " + + "src\\core\\ext\\xds\\xds_common_types.cc " + + "src\\core\\ext\\xds\\xds_endpoint.cc " + "src\\core\\ext\\xds\\xds_http_fault_filter.cc " + "src\\core\\ext\\xds\\xds_http_filters.cc " + + "src\\core\\ext\\xds\\xds_listener.cc " + + "src\\core\\ext\\xds\\xds_route_config.cc " + "src\\core\\ext\\xds\\xds_routing.cc " + "src\\core\\ext\\xds\\xds_server_config_fetcher.cc " + "src\\core\\lib\\address_utils\\parse_address.cc " + diff --git a/gRPC-C++.podspec b/gRPC-C++.podspec index 7f63fcfeaef..f484dc34f25 100644 --- a/gRPC-C++.podspec +++ b/gRPC-C++.podspec @@ -550,6 +550,7 @@ Pod::Spec.new do |s| 'src/core/ext/xds/certificate_provider_registry.h', 'src/core/ext/xds/certificate_provider_store.h', 'src/core/ext/xds/file_watcher_certificate_provider_factory.h', + 'src/core/ext/xds/upb_utils.h', 'src/core/ext/xds/xds_api.h', 'src/core/ext/xds/xds_bootstrap.h', 'src/core/ext/xds/xds_certificate_provider.h', @@ -557,8 +558,14 @@ Pod::Spec.new do |s| 'src/core/ext/xds/xds_channel_stack_modifier.h', 'src/core/ext/xds/xds_client.h', 'src/core/ext/xds/xds_client_stats.h', + 'src/core/ext/xds/xds_cluster.h', + 'src/core/ext/xds/xds_common_types.h', + 'src/core/ext/xds/xds_endpoint.h', 'src/core/ext/xds/xds_http_fault_filter.h', 'src/core/ext/xds/xds_http_filters.h', + 'src/core/ext/xds/xds_listener.h', + 'src/core/ext/xds/xds_resource_type.h', + 'src/core/ext/xds/xds_route_config.h', 'src/core/ext/xds/xds_routing.h', 'src/core/lib/address_utils/parse_address.h', 'src/core/lib/address_utils/sockaddr_utils.h', @@ -1266,6 +1273,7 @@ Pod::Spec.new do |s| 'src/core/ext/xds/certificate_provider_registry.h', 'src/core/ext/xds/certificate_provider_store.h', 'src/core/ext/xds/file_watcher_certificate_provider_factory.h', + 'src/core/ext/xds/upb_utils.h', 'src/core/ext/xds/xds_api.h', 'src/core/ext/xds/xds_bootstrap.h', 'src/core/ext/xds/xds_certificate_provider.h', @@ -1273,8 +1281,14 @@ Pod::Spec.new do |s| 'src/core/ext/xds/xds_channel_stack_modifier.h', 'src/core/ext/xds/xds_client.h', 'src/core/ext/xds/xds_client_stats.h', + 'src/core/ext/xds/xds_cluster.h', + 'src/core/ext/xds/xds_common_types.h', + 'src/core/ext/xds/xds_endpoint.h', 'src/core/ext/xds/xds_http_fault_filter.h', 'src/core/ext/xds/xds_http_filters.h', + 'src/core/ext/xds/xds_listener.h', + 'src/core/ext/xds/xds_resource_type.h', + 'src/core/ext/xds/xds_route_config.h', 'src/core/ext/xds/xds_routing.h', 'src/core/lib/address_utils/parse_address.h', 'src/core/lib/address_utils/sockaddr_utils.h', diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec index eed2c8bee39..e8e1732ab39 100644 --- a/gRPC-Core.podspec +++ b/gRPC-Core.podspec @@ -808,6 +808,7 @@ Pod::Spec.new do |s| 'src/core/ext/xds/certificate_provider_store.h', 'src/core/ext/xds/file_watcher_certificate_provider_factory.cc', 'src/core/ext/xds/file_watcher_certificate_provider_factory.h', + 'src/core/ext/xds/upb_utils.h', 'src/core/ext/xds/xds_api.cc', 'src/core/ext/xds/xds_api.h', 'src/core/ext/xds/xds_bootstrap.cc', @@ -821,10 +822,21 @@ Pod::Spec.new do |s| 'src/core/ext/xds/xds_client.h', 'src/core/ext/xds/xds_client_stats.cc', 'src/core/ext/xds/xds_client_stats.h', + 'src/core/ext/xds/xds_cluster.cc', + 'src/core/ext/xds/xds_cluster.h', + 'src/core/ext/xds/xds_common_types.cc', + 'src/core/ext/xds/xds_common_types.h', + 'src/core/ext/xds/xds_endpoint.cc', + 'src/core/ext/xds/xds_endpoint.h', 'src/core/ext/xds/xds_http_fault_filter.cc', 'src/core/ext/xds/xds_http_fault_filter.h', 'src/core/ext/xds/xds_http_filters.cc', 'src/core/ext/xds/xds_http_filters.h', + 'src/core/ext/xds/xds_listener.cc', + 'src/core/ext/xds/xds_listener.h', + 'src/core/ext/xds/xds_resource_type.h', + 'src/core/ext/xds/xds_route_config.cc', + 'src/core/ext/xds/xds_route_config.h', 'src/core/ext/xds/xds_routing.cc', 'src/core/ext/xds/xds_routing.h', 'src/core/ext/xds/xds_server_config_fetcher.cc', @@ -1797,6 +1809,7 @@ Pod::Spec.new do |s| 'src/core/ext/xds/certificate_provider_registry.h', 'src/core/ext/xds/certificate_provider_store.h', 'src/core/ext/xds/file_watcher_certificate_provider_factory.h', + 'src/core/ext/xds/upb_utils.h', 'src/core/ext/xds/xds_api.h', 'src/core/ext/xds/xds_bootstrap.h', 'src/core/ext/xds/xds_certificate_provider.h', @@ -1804,8 +1817,14 @@ Pod::Spec.new do |s| 'src/core/ext/xds/xds_channel_stack_modifier.h', 'src/core/ext/xds/xds_client.h', 'src/core/ext/xds/xds_client_stats.h', + 'src/core/ext/xds/xds_cluster.h', + 'src/core/ext/xds/xds_common_types.h', + 'src/core/ext/xds/xds_endpoint.h', 'src/core/ext/xds/xds_http_fault_filter.h', 'src/core/ext/xds/xds_http_filters.h', + 'src/core/ext/xds/xds_listener.h', + 'src/core/ext/xds/xds_resource_type.h', + 'src/core/ext/xds/xds_route_config.h', 'src/core/ext/xds/xds_routing.h', 'src/core/lib/address_utils/parse_address.h', 'src/core/lib/address_utils/sockaddr_utils.h', diff --git a/grpc.gemspec b/grpc.gemspec index f476f4638fb..17e05778a90 100644 --- a/grpc.gemspec +++ b/grpc.gemspec @@ -727,6 +727,7 @@ Gem::Specification.new do |s| s.files += %w( src/core/ext/xds/certificate_provider_store.h ) s.files += %w( src/core/ext/xds/file_watcher_certificate_provider_factory.cc ) s.files += %w( src/core/ext/xds/file_watcher_certificate_provider_factory.h ) + s.files += %w( src/core/ext/xds/upb_utils.h ) s.files += %w( src/core/ext/xds/xds_api.cc ) s.files += %w( src/core/ext/xds/xds_api.h ) s.files += %w( src/core/ext/xds/xds_bootstrap.cc ) @@ -740,10 +741,21 @@ Gem::Specification.new do |s| s.files += %w( src/core/ext/xds/xds_client.h ) s.files += %w( src/core/ext/xds/xds_client_stats.cc ) s.files += %w( src/core/ext/xds/xds_client_stats.h ) + s.files += %w( src/core/ext/xds/xds_cluster.cc ) + s.files += %w( src/core/ext/xds/xds_cluster.h ) + s.files += %w( src/core/ext/xds/xds_common_types.cc ) + s.files += %w( src/core/ext/xds/xds_common_types.h ) + s.files += %w( src/core/ext/xds/xds_endpoint.cc ) + s.files += %w( src/core/ext/xds/xds_endpoint.h ) s.files += %w( src/core/ext/xds/xds_http_fault_filter.cc ) s.files += %w( src/core/ext/xds/xds_http_fault_filter.h ) s.files += %w( src/core/ext/xds/xds_http_filters.cc ) s.files += %w( src/core/ext/xds/xds_http_filters.h ) + s.files += %w( src/core/ext/xds/xds_listener.cc ) + s.files += %w( src/core/ext/xds/xds_listener.h ) + s.files += %w( src/core/ext/xds/xds_resource_type.h ) + s.files += %w( src/core/ext/xds/xds_route_config.cc ) + s.files += %w( src/core/ext/xds/xds_route_config.h ) s.files += %w( src/core/ext/xds/xds_routing.cc ) s.files += %w( src/core/ext/xds/xds_routing.h ) s.files += %w( src/core/ext/xds/xds_server_config_fetcher.cc ) diff --git a/grpc.gyp b/grpc.gyp index 2fcfff9352f..e9ddcda8666 100644 --- a/grpc.gyp +++ b/grpc.gyp @@ -803,8 +803,13 @@ 'src/core/ext/xds/xds_channel_stack_modifier.cc', 'src/core/ext/xds/xds_client.cc', 'src/core/ext/xds/xds_client_stats.cc', + 'src/core/ext/xds/xds_cluster.cc', + 'src/core/ext/xds/xds_common_types.cc', + 'src/core/ext/xds/xds_endpoint.cc', 'src/core/ext/xds/xds_http_fault_filter.cc', 'src/core/ext/xds/xds_http_filters.cc', + 'src/core/ext/xds/xds_listener.cc', + 'src/core/ext/xds/xds_route_config.cc', 'src/core/ext/xds/xds_routing.cc', 'src/core/ext/xds/xds_server_config_fetcher.cc', 'src/core/lib/address_utils/parse_address.cc', diff --git a/package.xml b/package.xml index 478ed55d0d7..3b3c031d6d3 100644 --- a/package.xml +++ b/package.xml @@ -707,6 +707,7 @@ + @@ -720,10 +721,21 @@ + + + + + + + + + + + diff --git a/src/core/ext/filters/client_channel/lb_policy/xds/cds.cc b/src/core/ext/filters/client_channel/lb_policy/xds/cds.cc index 7b97d9be07b..bb7742fe786 100644 --- a/src/core/ext/filters/client_channel/lb_policy/xds/cds.cc +++ b/src/core/ext/filters/client_channel/lb_policy/xds/cds.cc @@ -71,7 +71,7 @@ class CdsLb : public LoadBalancingPolicy { ClusterWatcher(RefCountedPtr parent, std::string name) : parent_(std::move(parent)), name_(std::move(name)) {} - void OnClusterChanged(XdsApi::CdsUpdate cluster_data) override { + void OnClusterChanged(XdsClusterResource cluster_data) override { Ref().release(); // Ref held by lambda parent_->work_serializer()->Run( // TODO(roth): When we move to C++14, capture cluster_data with @@ -111,7 +111,7 @@ class CdsLb : public LoadBalancingPolicy { // Not owned, so do not dereference. ClusterWatcher* watcher = nullptr; // Most recent update obtained from this watcher. - absl::optional update; + absl::optional update; }; // Delegating helper to be passed to child policy. @@ -139,12 +139,12 @@ class CdsLb : public LoadBalancingPolicy { const std::string& name, Json::Array* discovery_mechanisms, std::set* clusters_needed); void OnClusterChanged(const std::string& name, - XdsApi::CdsUpdate cluster_data); + XdsClusterResource cluster_data); void OnError(const std::string& name, grpc_error_handle error); void OnResourceDoesNotExist(const std::string& name); grpc_error_handle UpdateXdsCertificateProvider( - const std::string& cluster_name, const XdsApi::CdsUpdate& cluster_data); + const std::string& cluster_name, const XdsClusterResource& cluster_data); void CancelClusterDataWatch(absl::string_view cluster_name, XdsClient::ClusterWatcherInterface* watcher, @@ -332,7 +332,8 @@ bool CdsLb::GenerateDiscoveryMechanismForCluster( // Don't have the update we need yet. if (!state.update.has_value()) return false; // For AGGREGATE clusters, recursively expand to child clusters. - if (state.update->cluster_type == XdsApi::CdsUpdate::ClusterType::AGGREGATE) { + if (state.update->cluster_type == + XdsClusterResource::ClusterType::AGGREGATE) { bool missing_cluster = false; for (const std::string& child_name : state.update->prioritized_cluster_names) { @@ -348,13 +349,13 @@ bool CdsLb::GenerateDiscoveryMechanismForCluster( {"max_concurrent_requests", state.update->max_concurrent_requests}, }; switch (state.update->cluster_type) { - case XdsApi::CdsUpdate::ClusterType::EDS: + case XdsClusterResource::ClusterType::EDS: mechanism["type"] = "EDS"; if (!state.update->eds_service_name.empty()) { mechanism["edsServiceName"] = state.update->eds_service_name; } break; - case XdsApi::CdsUpdate::ClusterType::LOGICAL_DNS: + case XdsClusterResource::ClusterType::LOGICAL_DNS: mechanism["type"] = "LOGICAL_DNS"; mechanism["dnsHostname"] = state.update->dns_hostname; break; @@ -371,7 +372,7 @@ bool CdsLb::GenerateDiscoveryMechanismForCluster( } void CdsLb::OnClusterChanged(const std::string& name, - XdsApi::CdsUpdate cluster_data) { + XdsClusterResource cluster_data) { if (GRPC_TRACE_FLAG_ENABLED(grpc_cds_lb_trace)) { gpr_log( GPR_INFO, @@ -509,7 +510,7 @@ void CdsLb::OnResourceDoesNotExist(const std::string& name) { } grpc_error_handle CdsLb::UpdateXdsCertificateProvider( - const std::string& cluster_name, const XdsApi::CdsUpdate& cluster_data) { + const std::string& cluster_name, const XdsClusterResource& cluster_data) { // Early out if channel is not configured to use xds security. grpc_channel_credentials* channel_credentials = grpc_channel_credentials_find_in_args(args_); diff --git a/src/core/ext/filters/client_channel/lb_policy/xds/xds_cluster_impl.cc b/src/core/ext/filters/client_channel/lb_policy/xds/xds_cluster_impl.cc index 755d187185a..49f45b4054b 100644 --- a/src/core/ext/filters/client_channel/lb_policy/xds/xds_cluster_impl.cc +++ b/src/core/ext/filters/client_channel/lb_policy/xds/xds_cluster_impl.cc @@ -120,7 +120,7 @@ class XdsClusterImplLbConfig : public LoadBalancingPolicy::Config { std::string cluster_name, std::string eds_service_name, absl::optional lrs_load_reporting_server_name, uint32_t max_concurrent_requests, - RefCountedPtr drop_config) + RefCountedPtr drop_config) : child_policy_(std::move(child_policy)), cluster_name_(std::move(cluster_name)), eds_service_name_(std::move(eds_service_name)), @@ -140,7 +140,7 @@ class XdsClusterImplLbConfig : public LoadBalancingPolicy::Config { return lrs_load_reporting_server_name_; }; uint32_t max_concurrent_requests() const { return max_concurrent_requests_; } - RefCountedPtr drop_config() const { + RefCountedPtr drop_config() const { return drop_config_; } @@ -150,7 +150,7 @@ class XdsClusterImplLbConfig : public LoadBalancingPolicy::Config { std::string eds_service_name_; absl::optional lrs_load_reporting_server_name_; uint32_t max_concurrent_requests_; - RefCountedPtr drop_config_; + RefCountedPtr drop_config_; }; // xDS Cluster Impl LB policy. @@ -205,7 +205,7 @@ class XdsClusterImplLb : public LoadBalancingPolicy { RefCountedPtr call_counter_; uint32_t max_concurrent_requests_; - RefCountedPtr drop_config_; + RefCountedPtr drop_config_; RefCountedPtr drop_stats_; RefCountedPtr picker_; }; @@ -736,7 +736,7 @@ class XdsClusterImplLbFactory : public LoadBalancingPolicyFactory { } } // Drop config. - auto drop_config = MakeRefCounted(); + auto drop_config = MakeRefCounted(); it = json.object_value().find("dropCategories"); if (it == json.object_value().end()) { error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( @@ -762,7 +762,7 @@ class XdsClusterImplLbFactory : public LoadBalancingPolicyFactory { private: static std::vector ParseDropCategories( - const Json& json, XdsApi::EdsUpdate::DropConfig* drop_config) { + const Json& json, XdsEndpointResource::DropConfig* drop_config) { std::vector error_list; if (json.type() != Json::Type::ARRAY) { error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( @@ -786,7 +786,7 @@ class XdsClusterImplLbFactory : public LoadBalancingPolicyFactory { } static std::vector ParseDropCategory( - const Json& json, XdsApi::EdsUpdate::DropConfig* drop_config) { + const Json& json, XdsEndpointResource::DropConfig* drop_config) { std::vector error_list; if (json.type() != Json::Type::OBJECT) { error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( diff --git a/src/core/ext/filters/client_channel/lb_policy/xds/xds_cluster_resolver.cc b/src/core/ext/filters/client_channel/lb_policy/xds/xds_cluster_resolver.cc index 432d52b4877..2fe88321320 100644 --- a/src/core/ext/filters/client_channel/lb_policy/xds/xds_cluster_resolver.cc +++ b/src/core/ext/filters/client_channel/lb_policy/xds/xds_cluster_resolver.cc @@ -175,7 +175,7 @@ class XdsClusterResolverLb : public LoadBalancingPolicy { ~EndpointWatcher() override { discovery_mechanism_.reset(DEBUG_LOCATION, "EndpointWatcher"); } - void OnEndpointChanged(XdsApi::EdsUpdate update) override { + void OnEndpointChanged(XdsEndpointResource update) override { Ref().release(); // ref held by callback discovery_mechanism_->parent()->work_serializer()->Run( // TODO(yashykt): When we move to C++14, capture update with @@ -209,7 +209,7 @@ class XdsClusterResolverLb : public LoadBalancingPolicy { // Code accessing protected methods of `DiscoveryMechanism` need to be // in methods of this class rather than in lambdas to work around an MSVC // bug. - void OnEndpointChangedHelper(XdsApi::EdsUpdate update) { + void OnEndpointChangedHelper(XdsEndpointResource update) { discovery_mechanism_->parent()->OnEndpointChanged( discovery_mechanism_->index(), std::move(update)); } @@ -292,10 +292,10 @@ class XdsClusterResolverLb : public LoadBalancingPolicy { // (The sum of this across all discovery mechanisms should always equal // the number of priorities in priority_list_.) uint32_t num_priorities = 0; - RefCountedPtr drop_config; + RefCountedPtr drop_config; // Populated only when an update has been delivered by the mechanism // but has not yet been applied to the LB policy's combined priority_list_. - absl::optional pending_priority_list; + absl::optional pending_priority_list; }; class Helper : public ChannelControlHelper { @@ -328,13 +328,13 @@ class XdsClusterResolverLb : public LoadBalancingPolicy { void ShutdownLocked() override; - void OnEndpointChanged(size_t index, XdsApi::EdsUpdate update); + void OnEndpointChanged(size_t index, XdsEndpointResource update); void OnError(size_t index, grpc_error_handle error); void OnResourceDoesNotExist(size_t index); void MaybeDestroyChildPolicyLocked(); - void UpdatePriorityList(XdsApi::EdsUpdate::PriorityList priority_list); + void UpdatePriorityList(XdsEndpointResource::PriorityList priority_list); void UpdateChildPolicyLocked(); OrphanablePtr CreateChildPolicyLocked( const grpc_channel_args* args); @@ -361,7 +361,7 @@ class XdsClusterResolverLb : public LoadBalancingPolicy { std::vector discovery_mechanisms_; // The latest data from the endpoint watcher. - XdsApi::EdsUpdate::PriorityList priority_list_; + XdsEndpointResource::PriorityList priority_list_; // State used to retain child policy names for priority policy. std::vector priority_child_numbers_; @@ -498,12 +498,12 @@ void XdsClusterResolverLb::LogicalDNSDiscoveryMechanism::Orphan() { void XdsClusterResolverLb::LogicalDNSDiscoveryMechanism::ResolverResultHandler:: ReturnResult(Resolver::Result result) { // convert result to eds update - XdsApi::EdsUpdate update; - XdsApi::EdsUpdate::Priority::Locality locality; + XdsEndpointResource update; + XdsEndpointResource::Priority::Locality locality; locality.name = MakeRefCounted("", "", ""); locality.lb_weight = 1; locality.endpoints = std::move(result.addresses); - XdsApi::EdsUpdate::Priority priority; + XdsEndpointResource::Priority priority; priority.localities.emplace(locality.name.get(), std::move(locality)); update.priorities.emplace_back(std::move(priority)); discovery_mechanism_->parent()->OnEndpointChanged( @@ -630,7 +630,7 @@ void XdsClusterResolverLb::ExitIdleLocked() { } void XdsClusterResolverLb::OnEndpointChanged(size_t index, - XdsApi::EdsUpdate update) { + XdsEndpointResource update) { if (shutting_down_) return; if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_xds_cluster_resolver_trace)) { gpr_log(GPR_INFO, @@ -660,7 +660,7 @@ void XdsClusterResolverLb::OnEndpointChanged(size_t index, if (!mechanism.first_update_received) return; } // Construct new priority list. - XdsApi::EdsUpdate::PriorityList priority_list; + XdsEndpointResource::PriorityList priority_list; size_t priority_index = 0; for (DiscoveryMechanismEntry& mechanism : discovery_mechanisms_) { // If the mechanism has a pending update, use that. @@ -694,7 +694,7 @@ void XdsClusterResolverLb::OnError(size_t index, grpc_error_handle error) { if (!discovery_mechanisms_[index].first_update_received) { // Call OnEndpointChanged with an empty update just like // OnResourceDoesNotExist. - OnEndpointChanged(index, XdsApi::EdsUpdate()); + OnEndpointChanged(index, XdsEndpointResource()); } } @@ -705,7 +705,7 @@ void XdsClusterResolverLb::OnResourceDoesNotExist(size_t index) { this, index); if (shutting_down_) return; // Call OnEndpointChanged with an empty update. - OnEndpointChanged(index, XdsApi::EdsUpdate()); + OnEndpointChanged(index, XdsEndpointResource()); } // @@ -713,7 +713,7 @@ void XdsClusterResolverLb::OnResourceDoesNotExist(size_t index) { // void XdsClusterResolverLb::UpdatePriorityList( - XdsApi::EdsUpdate::PriorityList priority_list) { + XdsEndpointResource::PriorityList priority_list) { // Build some maps from locality to child number and the reverse from // the old data in priority_list_ and priority_child_numbers_. std::map diff --git a/src/core/ext/filters/client_channel/resolver/xds/xds_resolver.cc b/src/core/ext/filters/client_channel/resolver/xds/xds_resolver.cc index cde2475fb99..fd8a9d304bc 100644 --- a/src/core/ext/filters/client_channel/resolver/xds/xds_resolver.cc +++ b/src/core/ext/filters/client_channel/resolver/xds/xds_resolver.cc @@ -83,7 +83,7 @@ class XdsResolver : public Resolver { public: explicit ListenerWatcher(RefCountedPtr resolver) : resolver_(std::move(resolver)) {} - void OnListenerChanged(XdsApi::LdsUpdate listener) override { + void OnListenerChanged(XdsListenerResource listener) override { Ref().release(); // ref held by lambda resolver_->work_serializer_->Run( // TODO(yashykt): When we move to C++14, capture listener with @@ -121,7 +121,7 @@ class XdsResolver : public Resolver { public: explicit RouteConfigWatcher(RefCountedPtr resolver) : resolver_(std::move(resolver)) {} - void OnRouteConfigChanged(XdsApi::RdsUpdate route_config) override { + void OnRouteConfigChanged(XdsRouteConfigResource route_config) override { Ref().release(); // ref held by lambda resolver_->work_serializer_->Run( // TODO(yashykt): When we move to C++14, capture route_config with @@ -250,7 +250,7 @@ class XdsResolver : public Resolver { bool operator==(const ClusterWeightState& other) const; }; - XdsApi::Route route; + XdsRouteConfigResource::Route route; RefCountedPtr method_config; absl::InlinedVector weighted_cluster_state; @@ -262,8 +262,9 @@ class XdsResolver : public Resolver { void MaybeAddCluster(const std::string& name); grpc_error_handle CreateMethodConfig( - const XdsApi::Route& route, - const XdsApi::Route::RouteAction::ClusterWeight* cluster_weight, + const XdsRouteConfigResource::Route& route, + const XdsRouteConfigResource::Route::RouteAction::ClusterWeight* + cluster_weight, RefCountedPtr* method_config); RefCountedPtr resolver_; @@ -272,8 +273,8 @@ class XdsResolver : public Resolver { std::vector filters_; }; - void OnListenerUpdate(XdsApi::LdsUpdate listener); - void OnRouteConfigUpdate(XdsApi::RdsUpdate rds_update); + void OnListenerUpdate(XdsListenerResource listener); + void OnRouteConfigUpdate(XdsRouteConfigResource rds_update); void OnError(grpc_error_handle error); void OnResourceDoesNotExist(); @@ -294,11 +295,11 @@ class XdsResolver : public Resolver { // This will not contain the RouteConfiguration, even if it comes with the // LDS response; instead, the relevant VirtualHost from the // RouteConfiguration will be saved in current_virtual_host_. - XdsApi::LdsUpdate current_listener_; + XdsListenerResource current_listener_; std::string route_config_name_; XdsClient::RouteConfigWatcherInterface* route_config_watcher_ = nullptr; - XdsApi::RdsUpdate::VirtualHost current_virtual_host_; + XdsRouteConfigResource::VirtualHost current_virtual_host_; ClusterState::ClusterStateMap cluster_state_map_; }; @@ -336,7 +337,7 @@ class XdsResolver::XdsConfigSelector::RouteListIterator size_t Size() const override { return route_table_->size(); } - const XdsApi::Route::Matchers& GetMatchersForRoute( + const XdsRouteConfigResource::Route::Matchers& GetMatchersForRoute( size_t index) const override { return (*route_table_)[index].route.matchers; } @@ -375,7 +376,8 @@ XdsResolver::XdsConfigSelector::XdsConfigSelector( auto& route_entry = route_table_.back(); route_entry.route = route; auto* route_action = - absl::get_if(&route_entry.route.action); + absl::get_if( + &route_entry.route.action); if (route_action != nullptr) { // If the route doesn't specify a timeout, set its timeout to the global // one. @@ -431,12 +433,13 @@ XdsResolver::XdsConfigSelector::~XdsConfigSelector() { } grpc_error_handle XdsResolver::XdsConfigSelector::CreateMethodConfig( - const XdsApi::Route& route, - const XdsApi::Route::RouteAction::ClusterWeight* cluster_weight, + const XdsRouteConfigResource::Route& route, + const XdsRouteConfigResource::Route::RouteAction::ClusterWeight* + cluster_weight, RefCountedPtr* method_config) { std::vector fields; const auto& route_action = - absl::get(route.action); + absl::get(route.action); // Set retry policy if any. if (route_action.retry_policy.has_value() && !route_action.retry_policy->retry_on.Empty()) { @@ -536,9 +539,10 @@ void XdsResolver::XdsConfigSelector::MaybeAddCluster(const std::string& name) { } absl::optional HeaderHashHelper( - const XdsApi::Route::RouteAction::HashPolicy& policy, + const XdsRouteConfigResource::Route::RouteAction::HashPolicy& policy, grpc_metadata_batch* initial_metadata) { - GPR_ASSERT(policy.type == XdsApi::Route::RouteAction::HashPolicy::HEADER); + GPR_ASSERT(policy.type == + XdsRouteConfigResource::Route::RouteAction::HashPolicy::HEADER); std::string value_buffer; absl::optional header_value = XdsRouting::GetHeaderValue( initial_metadata, policy.header_name, &value_buffer); @@ -568,7 +572,8 @@ ConfigSelector::CallConfig XdsResolver::XdsConfigSelector::GetCallConfig( auto& entry = route_table_[*route_index]; // Found a route match const auto* route_action = - absl::get_if(&entry.route.action); + absl::get_if( + &entry.route.action); if (route_action == nullptr) { CallConfig call_config; call_config.error = @@ -615,10 +620,10 @@ ConfigSelector::CallConfig XdsResolver::XdsConfigSelector::GetCallConfig( for (const auto& hash_policy : route_action->hash_policies) { absl::optional new_hash; switch (hash_policy.type) { - case XdsApi::Route::RouteAction::HashPolicy::HEADER: + case XdsRouteConfigResource::Route::RouteAction::HashPolicy::HEADER: new_hash = HeaderHashHelper(hash_policy, args.initial_metadata); break; - case XdsApi::Route::RouteAction::HashPolicy::CHANNEL_ID: + case XdsRouteConfigResource::Route::RouteAction::HashPolicy::CHANNEL_ID: new_hash = static_cast(reinterpret_cast(resolver_.get())); break; @@ -706,7 +711,7 @@ void XdsResolver::ShutdownLocked() { } } -void XdsResolver::OnListenerUpdate(XdsApi::LdsUpdate listener) { +void XdsResolver::OnListenerUpdate(XdsListenerResource listener) { if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_resolver_trace)) { gpr_log(GPR_INFO, "[xds_resolver %p] received updated listener data", this); } @@ -748,7 +753,7 @@ namespace { class VirtualHostListIterator : public XdsRouting::VirtualHostListIterator { public: explicit VirtualHostListIterator( - const std::vector* virtual_hosts) + const std::vector* virtual_hosts) : virtual_hosts_(virtual_hosts) {} size_t Size() const override { return virtual_hosts_->size(); } @@ -759,11 +764,11 @@ class VirtualHostListIterator : public XdsRouting::VirtualHostListIterator { } private: - const std::vector* virtual_hosts_; + const std::vector* virtual_hosts_; }; } // namespace -void XdsResolver::OnRouteConfigUpdate(XdsApi::RdsUpdate rds_update) { +void XdsResolver::OnRouteConfigUpdate(XdsRouteConfigResource rds_update) { if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_resolver_trace)) { gpr_log(GPR_INFO, "[xds_resolver %p] received updated route config", this); } diff --git a/src/core/ext/xds/upb_utils.h b/src/core/ext/xds/upb_utils.h new file mode 100644 index 00000000000..d867eca5044 --- /dev/null +++ b/src/core/ext/xds/upb_utils.h @@ -0,0 +1,62 @@ +// +// Copyright 2018 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef GRPC_CORE_EXT_XDS_UPB_UTILS_H +#define GRPC_CORE_EXT_XDS_UPB_UTILS_H + +#include + +#include + +#include "absl/strings/string_view.h" +#include "upb/text_encode.h" +#include "upb/upb.h" +#include "upb/upb.hpp" + +#include "src/core/ext/xds/certificate_provider_store.h" +#include "src/core/lib/debug/trace.h" + +namespace grpc_core { + +class XdsClient; + +struct XdsEncodingContext { + XdsClient* client; // Used only for logging. Unsafe for dereferencing. + TraceFlag* tracer; + upb_symtab* symtab; + upb_arena* arena; + bool use_v3; + const CertificateProviderStore::PluginDefinitionMap* + certificate_provider_definition_map; +}; + +// Works for both std::string and absl::string_view. +template +inline upb_strview StdStringToUpbString(const T& str) { + return upb_strview_make(str.data(), str.size()); +} + +inline absl::string_view UpbStringToAbsl(const upb_strview& str) { + return absl::string_view(str.data, str.size); +} + +inline std::string UpbStringToStdString(const upb_strview& str) { + return std::string(str.data, str.size); +} + +} // namespace grpc_core + +#endif // GRPC_CORE_EXT_XDS_UPB_UTILS_H diff --git a/src/core/ext/xds/xds_api.cc b/src/core/ext/xds/xds_api.cc index 679da843086..0497cd3f4a6 100644 --- a/src/core/ext/xds/xds_api.cc +++ b/src/core/ext/xds/xds_api.cc @@ -89,6 +89,11 @@ #include #include +#include "src/core/ext/xds/upb_utils.h" +#include "src/core/ext/xds/xds_cluster.h" +#include "src/core/ext/xds/xds_common_types.h" +#include "src/core/ext/xds/xds_endpoint.h" +#include "src/core/ext/xds/xds_resource_type.h" #include "src/core/ext/xds/xds_routing.h" #include "src/core/lib/address_utils/sockaddr_utils.h" #include "src/core/lib/gpr/env.h" @@ -102,622 +107,6 @@ namespace grpc_core { -// TODO(donnadionne): Check to see if cluster types aggregate_cluster and -// logical_dns are enabled, this will be -// removed once the cluster types are fully integration-tested and enabled by -// default. -bool XdsAggregateAndLogicalDnsClusterEnabled() { - char* value = gpr_getenv( - "GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER"); - bool parsed_value; - bool parse_succeeded = gpr_parse_bool_value(value, &parsed_value); - gpr_free(value); - return parse_succeeded && parsed_value; -} - -// TODO(yashykt): Remove once RBAC is no longer experimental -bool XdsRbacEnabled() { - char* value = gpr_getenv("GRPC_XDS_EXPERIMENTAL_RBAC"); - bool parsed_value; - bool parse_succeeded = gpr_parse_bool_value(value, &parsed_value); - gpr_free(value); - return parse_succeeded && parsed_value; -} - -// -// XdsApi::RetryPolicy -// - -std::string XdsApi::RetryPolicy::RetryBackOff::ToString() const { - std::vector contents; - contents.push_back( - absl::StrCat("RetryBackOff Base: ", base_interval.ToString())); - contents.push_back( - absl::StrCat("RetryBackOff max: ", max_interval.ToString())); - return absl::StrJoin(contents, ","); -} - -std::string XdsApi::RetryPolicy::ToString() const { - std::vector contents; - contents.push_back(absl::StrFormat("num_retries=%d", num_retries)); - contents.push_back(retry_back_off.ToString()); - return absl::StrCat("{", absl::StrJoin(contents, ","), "}"); -} - -// -// XdsApi::Route::Matchers -// - -std::string XdsApi::Route::Matchers::ToString() const { - std::vector contents; - contents.push_back( - absl::StrFormat("PathMatcher{%s}", path_matcher.ToString())); - for (const HeaderMatcher& header_matcher : header_matchers) { - contents.push_back(header_matcher.ToString()); - } - if (fraction_per_million.has_value()) { - contents.push_back(absl::StrFormat("Fraction Per Million %d", - fraction_per_million.value())); - } - return absl::StrJoin(contents, "\n"); -} - -// -// XdsApi::Route::RouteAction::HashPolicy -// - -XdsApi::Route::RouteAction::HashPolicy::HashPolicy(const HashPolicy& other) - : type(other.type), - header_name(other.header_name), - regex_substitution(other.regex_substitution) { - if (other.regex != nullptr) { - regex = - absl::make_unique(other.regex->pattern(), other.regex->options()); - } -} - -XdsApi::Route::RouteAction::HashPolicy& -XdsApi::Route::RouteAction::HashPolicy::operator=(const HashPolicy& other) { - type = other.type; - header_name = other.header_name; - if (other.regex != nullptr) { - regex = - absl::make_unique(other.regex->pattern(), other.regex->options()); - } - regex_substitution = other.regex_substitution; - return *this; -} - -XdsApi::Route::RouteAction::HashPolicy::HashPolicy(HashPolicy&& other) noexcept - : type(other.type), - header_name(std::move(other.header_name)), - regex(std::move(other.regex)), - regex_substitution(std::move(other.regex_substitution)) {} - -XdsApi::Route::RouteAction::HashPolicy& -XdsApi::Route::RouteAction::HashPolicy::operator=(HashPolicy&& other) noexcept { - type = other.type; - header_name = std::move(other.header_name); - regex = std::move(other.regex); - regex_substitution = std::move(other.regex_substitution); - return *this; -} - -bool XdsApi::Route::RouteAction::HashPolicy::HashPolicy::operator==( - const HashPolicy& other) const { - if (type != other.type) return false; - if (type == Type::HEADER) { - if (regex == nullptr) { - if (other.regex != nullptr) return false; - } else { - if (other.regex == nullptr) return false; - return header_name == other.header_name && - regex->pattern() == other.regex->pattern() && - regex_substitution == other.regex_substitution; - } - } - return true; -} - -std::string XdsApi::Route::RouteAction::HashPolicy::ToString() const { - std::vector contents; - switch (type) { - case Type::HEADER: - contents.push_back("type=HEADER"); - break; - case Type::CHANNEL_ID: - contents.push_back("type=CHANNEL_ID"); - break; - } - contents.push_back( - absl::StrFormat("terminal=%s", terminal ? "true" : "false")); - if (type == Type::HEADER) { - contents.push_back(absl::StrFormat( - "Header %s:/%s/%s", header_name, - (regex == nullptr) ? "" : regex->pattern(), regex_substitution)); - } - return absl::StrCat("{", absl::StrJoin(contents, ", "), "}"); -} - -// -// XdsApi::Route::RouteAction::ClusterWeight -// - -std::string XdsApi::Route::RouteAction::ClusterWeight::ToString() const { - std::vector contents; - contents.push_back(absl::StrCat("cluster=", name)); - contents.push_back(absl::StrCat("weight=", weight)); - if (!typed_per_filter_config.empty()) { - std::vector parts; - for (const auto& p : typed_per_filter_config) { - const std::string& key = p.first; - const auto& config = p.second; - parts.push_back(absl::StrCat(key, "=", config.ToString())); - } - contents.push_back(absl::StrCat("typed_per_filter_config={", - absl::StrJoin(parts, ", "), "}")); - } - return absl::StrCat("{", absl::StrJoin(contents, ", "), "}"); -} - -// -// XdsApi::Route::RouteAction -// - -std::string XdsApi::Route::RouteAction::ToString() const { - std::vector contents; - for (const HashPolicy& hash_policy : hash_policies) { - contents.push_back(absl::StrCat("hash_policy=", hash_policy.ToString())); - } - if (retry_policy.has_value()) { - contents.push_back(absl::StrCat("retry_policy=", retry_policy->ToString())); - } - if (!cluster_name.empty()) { - contents.push_back(absl::StrFormat("Cluster name: %s", cluster_name)); - } - for (const ClusterWeight& cluster_weight : weighted_clusters) { - contents.push_back(cluster_weight.ToString()); - } - if (max_stream_duration.has_value()) { - contents.push_back(max_stream_duration->ToString()); - } - return absl::StrCat("{", absl::StrJoin(contents, ", "), "}"); -} - -// -// XdsApi::Route -// - -std::string XdsApi::Route::ToString() const { - std::vector contents; - contents.push_back(matchers.ToString()); - auto* route_action = absl::get_if(&action); - if (route_action != nullptr) { - contents.push_back(absl::StrCat("route=", route_action->ToString())); - } else if (absl::holds_alternative( - action)) { - contents.push_back("non_forwarding_action={}"); - } else { - contents.push_back("unknown_action={}"); - } - if (!typed_per_filter_config.empty()) { - contents.push_back("typed_per_filter_config={"); - for (const auto& p : typed_per_filter_config) { - const std::string& name = p.first; - const auto& config = p.second; - contents.push_back(absl::StrCat(" ", name, "=", config.ToString())); - } - contents.push_back("}"); - } - return absl::StrJoin(contents, "\n"); -} - -// -// XdsApi::RdsUpdate -// - -std::string XdsApi::RdsUpdate::ToString() const { - std::vector vhosts; - for (const VirtualHost& vhost : virtual_hosts) { - vhosts.push_back( - absl::StrCat("vhost={\n" - " domains=[", - absl::StrJoin(vhost.domains, ", "), - "]\n" - " routes=[\n")); - for (const XdsApi::Route& route : vhost.routes) { - vhosts.push_back(" {\n"); - vhosts.push_back(route.ToString()); - vhosts.push_back("\n }\n"); - } - vhosts.push_back(" ]\n"); - vhosts.push_back(" typed_per_filter_config={\n"); - for (const auto& p : vhost.typed_per_filter_config) { - const std::string& name = p.first; - const auto& config = p.second; - vhosts.push_back( - absl::StrCat(" ", name, "=", config.ToString(), "\n")); - } - vhosts.push_back(" }\n"); - vhosts.push_back("]\n"); - } - return absl::StrJoin(vhosts, ""); -} - -// -// XdsApi::CommonTlsContext::CertificateValidationContext -// - -std::string XdsApi::CommonTlsContext::CertificateValidationContext::ToString() - const { - std::vector contents; - for (const auto& match : match_subject_alt_names) { - contents.push_back(match.ToString()); - } - return absl::StrFormat("{match_subject_alt_names=[%s]}", - absl::StrJoin(contents, ", ")); -} - -bool XdsApi::CommonTlsContext::CertificateValidationContext::Empty() const { - return match_subject_alt_names.empty(); -} - -// -// XdsApi::CommonTlsContext::CertificateProviderPluginInstance -// - -std::string -XdsApi::CommonTlsContext::CertificateProviderPluginInstance::ToString() const { - absl::InlinedVector contents; - if (!instance_name.empty()) { - contents.push_back(absl::StrFormat("instance_name=%s", instance_name)); - } - if (!certificate_name.empty()) { - contents.push_back( - absl::StrFormat("certificate_name=%s", certificate_name)); - } - return absl::StrCat("{", absl::StrJoin(contents, ", "), "}"); -} - -bool XdsApi::CommonTlsContext::CertificateProviderPluginInstance::Empty() - const { - return instance_name.empty() && certificate_name.empty(); -} - -// -// XdsApi::CommonTlsContext -// - -std::string XdsApi::CommonTlsContext::ToString() const { - absl::InlinedVector contents; - if (!tls_certificate_provider_instance.Empty()) { - contents.push_back( - absl::StrFormat("tls_certificate_provider_instance=%s", - tls_certificate_provider_instance.ToString())); - } - if (!certificate_validation_context.Empty()) { - contents.push_back( - absl::StrFormat("certificate_validation_context=%s", - certificate_validation_context.ToString())); - } - return absl::StrCat("{", absl::StrJoin(contents, ", "), "}"); -} - -bool XdsApi::CommonTlsContext::Empty() const { - return tls_certificate_provider_instance.Empty() && - certificate_validation_context.Empty(); -} - -// -// XdsApi::DownstreamTlsContext -// - -std::string XdsApi::DownstreamTlsContext::ToString() const { - return absl::StrFormat("common_tls_context=%s, require_client_certificate=%s", - common_tls_context.ToString(), - require_client_certificate ? "true" : "false"); -} - -bool XdsApi::DownstreamTlsContext::Empty() const { - return common_tls_context.Empty(); -} - -// -// XdsApi::LdsUpdate::HttpConnectionManager -// - -std::string XdsApi::LdsUpdate::HttpConnectionManager::ToString() const { - absl::InlinedVector contents; - contents.push_back(absl::StrFormat( - "route_config_name=%s", - !route_config_name.empty() ? route_config_name.c_str() : "")); - contents.push_back(absl::StrFormat("http_max_stream_duration=%s", - http_max_stream_duration.ToString())); - if (rds_update.has_value()) { - contents.push_back( - absl::StrFormat("rds_update=%s", rds_update->ToString())); - } - if (!http_filters.empty()) { - std::vector filter_strings; - for (const auto& http_filter : http_filters) { - filter_strings.push_back(http_filter.ToString()); - } - contents.push_back(absl::StrCat("http_filters=[", - absl::StrJoin(filter_strings, ", "), "]")); - } - return absl::StrCat("{", absl::StrJoin(contents, ", "), "}"); -} - -// -// XdsApi::LdsUpdate::HttpFilter -// - -std::string XdsApi::LdsUpdate::HttpConnectionManager::HttpFilter::ToString() - const { - return absl::StrCat("{name=", name, ", config=", config.ToString(), "}"); -} - -// -// XdsApi::LdsUpdate::FilterChainData -// - -std::string XdsApi::LdsUpdate::FilterChainData::ToString() const { - return absl::StrCat( - "{downstream_tls_context=", downstream_tls_context.ToString(), - " http_connection_manager=", http_connection_manager.ToString(), "}"); -} - -// -// XdsApi::LdsUpdate::FilterChainMap::CidrRange -// - -std::string XdsApi::LdsUpdate::FilterChainMap::CidrRange::ToString() const { - return absl::StrCat( - "{address_prefix=", grpc_sockaddr_to_string(&address, false), - ", prefix_len=", prefix_len, "}"); -} - -// -// FilterChain -// - -struct FilterChain { - struct FilterChainMatch { - uint32_t destination_port = 0; - std::vector prefix_ranges; - XdsApi::LdsUpdate::FilterChainMap::ConnectionSourceType source_type = - XdsApi::LdsUpdate::FilterChainMap::ConnectionSourceType::kAny; - std::vector - source_prefix_ranges; - std::vector source_ports; - std::vector server_names; - std::string transport_protocol; - std::vector application_protocols; - - std::string ToString() const; - } filter_chain_match; - - std::shared_ptr filter_chain_data; -}; - -std::string FilterChain::FilterChainMatch::ToString() const { - absl::InlinedVector contents; - if (destination_port != 0) { - contents.push_back(absl::StrCat("destination_port=", destination_port)); - } - if (!prefix_ranges.empty()) { - std::vector prefix_ranges_content; - for (const auto& range : prefix_ranges) { - prefix_ranges_content.push_back(range.ToString()); - } - contents.push_back(absl::StrCat( - "prefix_ranges={", absl::StrJoin(prefix_ranges_content, ", "), "}")); - } - if (source_type == XdsApi::LdsUpdate::FilterChainMap::ConnectionSourceType:: - kSameIpOrLoopback) { - contents.push_back("source_type=SAME_IP_OR_LOOPBACK"); - } else if (source_type == XdsApi::LdsUpdate::FilterChainMap:: - ConnectionSourceType::kExternal) { - contents.push_back("source_type=EXTERNAL"); - } - if (!source_prefix_ranges.empty()) { - std::vector source_prefix_ranges_content; - for (const auto& range : source_prefix_ranges) { - source_prefix_ranges_content.push_back(range.ToString()); - } - contents.push_back( - absl::StrCat("source_prefix_ranges={", - absl::StrJoin(source_prefix_ranges_content, ", "), "}")); - } - if (!source_ports.empty()) { - contents.push_back( - absl::StrCat("source_ports={", absl::StrJoin(source_ports, ", "), "}")); - } - if (!server_names.empty()) { - contents.push_back( - absl::StrCat("server_names={", absl::StrJoin(server_names, ", "), "}")); - } - if (!transport_protocol.empty()) { - contents.push_back(absl::StrCat("transport_protocol=", transport_protocol)); - } - if (!application_protocols.empty()) { - contents.push_back(absl::StrCat("application_protocols={", - absl::StrJoin(application_protocols, ", "), - "}")); - } - return absl::StrCat("{", absl::StrJoin(contents, ", "), "}"); -} - -// -// XdsApi::LdsUpdate::FilterChainMap -// - -std::string XdsApi::LdsUpdate::FilterChainMap::ToString() const { - std::vector contents; - for (const auto& destination_ip : destination_ip_vector) { - for (int source_type = 0; source_type < 3; ++source_type) { - for (const auto& source_ip : - destination_ip.source_types_array[source_type]) { - for (const auto& source_port_pair : source_ip.ports_map) { - FilterChain::FilterChainMatch filter_chain_match; - if (destination_ip.prefix_range.has_value()) { - filter_chain_match.prefix_ranges.push_back( - *destination_ip.prefix_range); - } - filter_chain_match.source_type = static_cast< - XdsApi::LdsUpdate::FilterChainMap::ConnectionSourceType>( - source_type); - if (source_ip.prefix_range.has_value()) { - filter_chain_match.source_prefix_ranges.push_back( - *source_ip.prefix_range); - } - if (source_port_pair.first != 0) { - filter_chain_match.source_ports.push_back(source_port_pair.first); - } - contents.push_back(absl::StrCat( - "{filter_chain_match=", filter_chain_match.ToString(), - ", filter_chain=", source_port_pair.second.data->ToString(), - "}")); - } - } - } - } - return absl::StrCat("{", absl::StrJoin(contents, ", "), "}"); -} - -// -// XdsApi::LdsUpdate -// - -std::string XdsApi::LdsUpdate::ToString() const { - absl::InlinedVector contents; - if (type == ListenerType::kTcpListener) { - contents.push_back(absl::StrCat("address=", address)); - contents.push_back( - absl::StrCat("filter_chain_map=", filter_chain_map.ToString())); - if (default_filter_chain.has_value()) { - contents.push_back(absl::StrCat("default_filter_chain=", - default_filter_chain->ToString())); - } - } else if (type == ListenerType::kHttpApiListener) { - contents.push_back(absl::StrFormat("http_connection_manager=%s", - http_connection_manager.ToString())); - } - return absl::StrCat("{", absl::StrJoin(contents, ", "), "}"); -} - -// -// XdsApi::CdsUpdate -// - -std::string XdsApi::CdsUpdate::ToString() const { - absl::InlinedVector contents; - switch (cluster_type) { - case EDS: - contents.push_back("cluster_type=EDS"); - if (!eds_service_name.empty()) { - contents.push_back( - absl::StrFormat("eds_service_name=%s", eds_service_name)); - } - break; - case LOGICAL_DNS: - contents.push_back("cluster_type=LOGICAL_DNS"); - contents.push_back(absl::StrFormat("dns_hostname=%s", dns_hostname)); - break; - case AGGREGATE: - contents.push_back("cluster_type=AGGREGATE"); - contents.push_back( - absl::StrFormat("prioritized_cluster_names=[%s]", - absl::StrJoin(prioritized_cluster_names, ", "))); - } - if (!common_tls_context.Empty()) { - contents.push_back(absl::StrFormat("common_tls_context=%s", - common_tls_context.ToString())); - } - if (lrs_load_reporting_server_name.has_value()) { - contents.push_back(absl::StrFormat("lrs_load_reporting_server_name=%s", - lrs_load_reporting_server_name.value())); - } - contents.push_back(absl::StrCat("lb_policy=", lb_policy)); - if (lb_policy == "RING_HASH") { - contents.push_back(absl::StrCat("min_ring_size=", min_ring_size)); - contents.push_back(absl::StrCat("max_ring_size=", max_ring_size)); - } - contents.push_back( - absl::StrFormat("max_concurrent_requests=%d", max_concurrent_requests)); - return absl::StrCat("{", absl::StrJoin(contents, ", "), "}"); -} - -// -// XdsApi::EdsUpdate -// - -std::string XdsApi::EdsUpdate::Priority::Locality::ToString() const { - std::vector endpoint_strings; - for (const ServerAddress& endpoint : endpoints) { - endpoint_strings.emplace_back(endpoint.ToString()); - } - return absl::StrCat("{name=", name->AsHumanReadableString(), - ", lb_weight=", lb_weight, ", endpoints=[", - absl::StrJoin(endpoint_strings, ", "), "]}"); -} - -bool XdsApi::EdsUpdate::Priority::operator==(const Priority& other) const { - if (localities.size() != other.localities.size()) return false; - auto it1 = localities.begin(); - auto it2 = other.localities.begin(); - while (it1 != localities.end()) { - if (*it1->first != *it2->first) return false; - if (it1->second != it2->second) return false; - ++it1; - ++it2; - } - return true; -} - -std::string XdsApi::EdsUpdate::Priority::ToString() const { - std::vector locality_strings; - for (const auto& p : localities) { - locality_strings.emplace_back(p.second.ToString()); - } - return absl::StrCat("[", absl::StrJoin(locality_strings, ", "), "]"); -} - -bool XdsApi::EdsUpdate::DropConfig::ShouldDrop( - const std::string** category_name) const { - for (size_t i = 0; i < drop_category_list_.size(); ++i) { - const auto& drop_category = drop_category_list_[i]; - // Generate a random number in [0, 1000000). - const uint32_t random = static_cast(rand()) % 1000000; - if (random < drop_category.parts_per_million) { - *category_name = &drop_category.name; - return true; - } - } - return false; -} - -std::string XdsApi::EdsUpdate::DropConfig::ToString() const { - std::vector category_strings; - for (const DropCategory& category : drop_category_list_) { - category_strings.emplace_back( - absl::StrCat(category.name, "=", category.parts_per_million)); - } - return absl::StrCat("{[", absl::StrJoin(category_strings, ", "), - "], drop_all=", drop_all_, "}"); -} - -std::string XdsApi::EdsUpdate::ToString() const { - std::vector priority_strings; - for (size_t i = 0; i < priorities.size(); ++i) { - const Priority& priority = priorities[i]; - priority_strings.emplace_back( - absl::StrCat("priority ", i, ": ", priority.ToString())); - } - return absl::StrCat("priorities=[", absl::StrJoin(priority_strings, ", "), - "], drop_config=", drop_config->ToString()); -} - // // XdsApi // @@ -790,48 +179,6 @@ std::string TypeUrlInternalToExternal(absl::string_view type_url) { return std::string(type_url); } -struct EncodingContext { - XdsClient* client; // Used only for logging. Unsafe for dereferencing. - TraceFlag* tracer; - upb_symtab* symtab; - upb_arena* arena; - bool use_v3; - const CertificateProviderStore::PluginDefinitionMap* - certificate_provider_definition_map; -}; - -class XdsResourceType { - public: - // A base type for resource data. - struct ResourceData { - virtual ~ResourceData() = default; - }; - - struct DecodeResult { - std::string name; - absl::StatusOr> resource; - }; - - virtual ~XdsResourceType() = default; - - virtual absl::string_view type_url() const = 0; - - virtual absl::string_view v2_type_url() const = 0; - - virtual absl::StatusOr Decode( - const EncodingContext& context, absl::string_view serialized_resource, - bool is_v2) const = 0; - - bool IsType(absl::string_view resource_type, bool* is_v2) const { - if (resource_type == type_url()) return true; - if (resource_type == v2_type_url()) { - if (is_v2 != nullptr) *is_v2 = true; - return true; - } - return false; - } -}; - absl::StatusOr ParseResourceNameInternal( absl::string_view name, std::function is_expected_type) { @@ -952,16 +299,10 @@ std::string XdsApi::ConstructFullResourceName(absl::string_view authority, namespace { -// Works for both std::string and absl::string_view. -template -inline upb_strview StdStringToUpbString(const T& str) { - return upb_strview_make(str.data(), str.size()); -} - -void PopulateMetadataValue(const EncodingContext& context, +void PopulateMetadataValue(const XdsEncodingContext& context, google_protobuf_Value* value_pb, const Json& value); -void PopulateListValue(const EncodingContext& context, +void PopulateListValue(const XdsEncodingContext& context, google_protobuf_ListValue* list_value, const Json::Array& values) { for (const auto& value : values) { @@ -971,7 +312,7 @@ void PopulateListValue(const EncodingContext& context, } } -void PopulateMetadata(const EncodingContext& context, +void PopulateMetadata(const XdsEncodingContext& context, google_protobuf_Struct* metadata_pb, const Json::Object& metadata) { for (const auto& p : metadata) { @@ -982,7 +323,7 @@ void PopulateMetadata(const EncodingContext& context, } } -void PopulateMetadataValue(const EncodingContext& context, +void PopulateMetadataValue(const XdsEncodingContext& context, google_protobuf_Value* value_pb, const Json& value) { switch (value.type()) { case Json::Type::JSON_NULL: @@ -1038,7 +379,7 @@ std::string EncodeStringField(uint32_t field_number, const std::string& str) { EncodeVarint(str.size()) + str; } -void PopulateBuildVersion(const EncodingContext& context, +void PopulateBuildVersion(const XdsEncodingContext& context, envoy_config_core_v3_Node* node_msg, const std::string& build_version) { std::string encoded_build_version = EncodeStringField(5, build_version); @@ -1050,7 +391,7 @@ void PopulateBuildVersion(const EncodingContext& context, encoded_build_version.size(), context.arena); } -void PopulateNode(const EncodingContext& context, +void PopulateNode(const XdsEncodingContext& context, const XdsBootstrap::Node* node, const std::string& build_version, const std::string& user_agent_name, @@ -1100,16 +441,8 @@ void PopulateNode(const EncodingContext& context, context.arena); } -inline absl::string_view UpbStringToAbsl(const upb_strview& str) { - return absl::string_view(str.data, str.size); -} - -inline std::string UpbStringToStdString(const upb_strview& str) { - return std::string(str.data, str.size); -} - void MaybeLogDiscoveryRequest( - const EncodingContext& context, + const XdsEncodingContext& context, const envoy_service_discovery_v3_DiscoveryRequest* request) { if (GRPC_TRACE_FLAG_ENABLED(*context.tracer) && gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) { @@ -1123,7 +456,7 @@ void MaybeLogDiscoveryRequest( } grpc_slice SerializeDiscoveryRequest( - const EncodingContext& context, + const XdsEncodingContext& context, envoy_service_discovery_v3_DiscoveryRequest* request) { size_t output_length; char* output = envoy_service_discovery_v3_DiscoveryRequest_serialize( @@ -1140,12 +473,12 @@ grpc_slice XdsApi::CreateAdsRequest( const std::string& version, const std::string& nonce, grpc_error_handle error, bool populate_node) { upb::Arena arena; - const EncodingContext context = {client_, - tracer_, - symtab_.ptr(), - arena.ptr(), - server.ShouldUseV3(), - certificate_provider_definition_map_}; + const XdsEncodingContext context = {client_, + tracer_, + symtab_.ptr(), + arena.ptr(), + server.ShouldUseV3(), + certificate_provider_definition_map_}; // Create a request. envoy_service_discovery_v3_DiscoveryRequest* request = envoy_service_discovery_v3_DiscoveryRequest_new(arena.ptr()); @@ -1220,7 +553,7 @@ grpc_slice XdsApi::CreateAdsRequest( namespace { void MaybeLogDiscoveryResponse( - const EncodingContext& context, + const XdsEncodingContext& context, const envoy_service_discovery_v3_DiscoveryResponse* response) { if (GRPC_TRACE_FLAG_ENABLED(*context.tracer) && gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) { @@ -1233,2345 +566,8 @@ void MaybeLogDiscoveryResponse( } } -void MaybeLogListener(const EncodingContext& context, - const envoy_config_listener_v3_Listener* listener) { - if (GRPC_TRACE_FLAG_ENABLED(*context.tracer) && - gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) { - const upb_msgdef* msg_type = - envoy_config_listener_v3_Listener_getmsgdef(context.symtab); - char buf[10240]; - upb_text_encode(listener, msg_type, nullptr, 0, buf, sizeof(buf)); - gpr_log(GPR_DEBUG, "[xds_client %p] Listener: %s", context.client, buf); - } -} - -void MaybeLogHttpConnectionManager( - const EncodingContext& context, - const envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager* - http_connection_manager_config) { - if (GRPC_TRACE_FLAG_ENABLED(*context.tracer) && - gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) { - const upb_msgdef* msg_type = - envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_getmsgdef( - context.symtab); - char buf[10240]; - upb_text_encode(http_connection_manager_config, msg_type, nullptr, 0, buf, - sizeof(buf)); - gpr_log(GPR_DEBUG, "[xds_client %p] HttpConnectionManager: %s", - context.client, buf); - } -} - -void MaybeLogRouteConfiguration( - const EncodingContext& context, - const envoy_config_route_v3_RouteConfiguration* route_config) { - if (GRPC_TRACE_FLAG_ENABLED(*context.tracer) && - gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) { - const upb_msgdef* msg_type = - envoy_config_route_v3_RouteConfiguration_getmsgdef(context.symtab); - char buf[10240]; - upb_text_encode(route_config, msg_type, nullptr, 0, buf, sizeof(buf)); - gpr_log(GPR_DEBUG, "[xds_client %p] RouteConfiguration: %s", context.client, - buf); - } -} - -void MaybeLogCluster(const EncodingContext& context, - const envoy_config_cluster_v3_Cluster* cluster) { - if (GRPC_TRACE_FLAG_ENABLED(*context.tracer) && - gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) { - const upb_msgdef* msg_type = - envoy_config_cluster_v3_Cluster_getmsgdef(context.symtab); - char buf[10240]; - upb_text_encode(cluster, msg_type, nullptr, 0, buf, sizeof(buf)); - gpr_log(GPR_DEBUG, "[xds_client %p] Cluster: %s", context.client, buf); - } -} - -void MaybeLogClusterLoadAssignment( - const EncodingContext& context, - const envoy_config_endpoint_v3_ClusterLoadAssignment* cla) { - if (GRPC_TRACE_FLAG_ENABLED(*context.tracer) && - gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) { - const upb_msgdef* msg_type = - envoy_config_endpoint_v3_ClusterLoadAssignment_getmsgdef( - context.symtab); - char buf[10240]; - upb_text_encode(cla, msg_type, nullptr, 0, buf, sizeof(buf)); - gpr_log(GPR_DEBUG, "[xds_client %p] ClusterLoadAssignment: %s", - context.client, buf); - } -} - -grpc_error_handle RoutePathMatchParse( - const envoy_config_route_v3_RouteMatch* match, XdsApi::Route* route, - bool* ignore_route) { - auto* case_sensitive_ptr = - envoy_config_route_v3_RouteMatch_case_sensitive(match); - bool case_sensitive = true; - if (case_sensitive_ptr != nullptr) { - case_sensitive = google_protobuf_BoolValue_value(case_sensitive_ptr); - } - StringMatcher::Type type; - std::string match_string; - if (envoy_config_route_v3_RouteMatch_has_prefix(match)) { - absl::string_view prefix = - UpbStringToAbsl(envoy_config_route_v3_RouteMatch_prefix(match)); - // Empty prefix "" is accepted. - if (!prefix.empty()) { - // Prefix "/" is accepted. - if (prefix[0] != '/') { - // Prefix which does not start with a / will never match anything, so - // ignore this route. - *ignore_route = true; - return GRPC_ERROR_NONE; - } - std::vector prefix_elements = - absl::StrSplit(prefix.substr(1), absl::MaxSplits('/', 2)); - if (prefix_elements.size() > 2) { - // Prefix cannot have more than 2 slashes. - *ignore_route = true; - return GRPC_ERROR_NONE; - } else if (prefix_elements.size() == 2 && prefix_elements[0].empty()) { - // Prefix contains empty string between the 2 slashes - *ignore_route = true; - return GRPC_ERROR_NONE; - } - } - type = StringMatcher::Type::kPrefix; - match_string = std::string(prefix); - } else if (envoy_config_route_v3_RouteMatch_has_path(match)) { - absl::string_view path = - UpbStringToAbsl(envoy_config_route_v3_RouteMatch_path(match)); - if (path.empty()) { - // Path that is empty will never match anything, so ignore this route. - *ignore_route = true; - return GRPC_ERROR_NONE; - } - if (path[0] != '/') { - // Path which does not start with a / will never match anything, so - // ignore this route. - *ignore_route = true; - return GRPC_ERROR_NONE; - } - std::vector path_elements = - absl::StrSplit(path.substr(1), absl::MaxSplits('/', 2)); - if (path_elements.size() != 2) { - // Path not in the required format of /service/method will never match - // anything, so ignore this route. - *ignore_route = true; - return GRPC_ERROR_NONE; - } else if (path_elements[0].empty()) { - // Path contains empty service name will never match anything, so ignore - // this route. - *ignore_route = true; - return GRPC_ERROR_NONE; - } else if (path_elements[1].empty()) { - // Path contains empty method name will never match anything, so ignore - // this route. - *ignore_route = true; - return GRPC_ERROR_NONE; - } - type = StringMatcher::Type::kExact; - match_string = std::string(path); - } else if (envoy_config_route_v3_RouteMatch_has_safe_regex(match)) { - const envoy_type_matcher_v3_RegexMatcher* regex_matcher = - envoy_config_route_v3_RouteMatch_safe_regex(match); - GPR_ASSERT(regex_matcher != nullptr); - type = StringMatcher::Type::kSafeRegex; - match_string = UpbStringToStdString( - envoy_type_matcher_v3_RegexMatcher_regex(regex_matcher)); - } else { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Invalid route path specifier specified."); - } - absl::StatusOr string_matcher = - StringMatcher::Create(type, match_string, case_sensitive); - if (!string_matcher.ok()) { - return GRPC_ERROR_CREATE_FROM_CPP_STRING( - absl::StrCat("path matcher: ", string_matcher.status().message())); - } - route->matchers.path_matcher = std::move(string_matcher.value()); - return GRPC_ERROR_NONE; -} - -grpc_error_handle RouteHeaderMatchersParse( - const envoy_config_route_v3_RouteMatch* match, XdsApi::Route* route) { - size_t size; - const envoy_config_route_v3_HeaderMatcher* const* headers = - envoy_config_route_v3_RouteMatch_headers(match, &size); - for (size_t i = 0; i < size; ++i) { - const envoy_config_route_v3_HeaderMatcher* header = headers[i]; - const std::string name = - UpbStringToStdString(envoy_config_route_v3_HeaderMatcher_name(header)); - HeaderMatcher::Type type; - std::string match_string; - int64_t range_start = 0; - int64_t range_end = 0; - bool present_match = false; - if (envoy_config_route_v3_HeaderMatcher_has_exact_match(header)) { - type = HeaderMatcher::Type::kExact; - match_string = UpbStringToStdString( - envoy_config_route_v3_HeaderMatcher_exact_match(header)); - } else if (envoy_config_route_v3_HeaderMatcher_has_safe_regex_match( - header)) { - const envoy_type_matcher_v3_RegexMatcher* regex_matcher = - envoy_config_route_v3_HeaderMatcher_safe_regex_match(header); - GPR_ASSERT(regex_matcher != nullptr); - type = HeaderMatcher::Type::kSafeRegex; - match_string = UpbStringToStdString( - envoy_type_matcher_v3_RegexMatcher_regex(regex_matcher)); - } else if (envoy_config_route_v3_HeaderMatcher_has_range_match(header)) { - type = HeaderMatcher::Type::kRange; - const envoy_type_v3_Int64Range* range_matcher = - envoy_config_route_v3_HeaderMatcher_range_match(header); - range_start = envoy_type_v3_Int64Range_start(range_matcher); - range_end = envoy_type_v3_Int64Range_end(range_matcher); - } else if (envoy_config_route_v3_HeaderMatcher_has_present_match(header)) { - type = HeaderMatcher::Type::kPresent; - present_match = envoy_config_route_v3_HeaderMatcher_present_match(header); - } else if (envoy_config_route_v3_HeaderMatcher_has_prefix_match(header)) { - type = HeaderMatcher::Type::kPrefix; - match_string = UpbStringToStdString( - envoy_config_route_v3_HeaderMatcher_prefix_match(header)); - } else if (envoy_config_route_v3_HeaderMatcher_has_suffix_match(header)) { - type = HeaderMatcher::Type::kSuffix; - match_string = UpbStringToStdString( - envoy_config_route_v3_HeaderMatcher_suffix_match(header)); - } else if (envoy_config_route_v3_HeaderMatcher_has_contains_match(header)) { - type = HeaderMatcher::Type::kContains; - match_string = UpbStringToStdString( - envoy_config_route_v3_HeaderMatcher_contains_match(header)); - } else { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Invalid route header matcher specified."); - } - bool invert_match = - envoy_config_route_v3_HeaderMatcher_invert_match(header); - absl::StatusOr header_matcher = - HeaderMatcher::Create(name, type, match_string, range_start, range_end, - present_match, invert_match); - if (!header_matcher.ok()) { - return GRPC_ERROR_CREATE_FROM_CPP_STRING( - absl::StrCat("header matcher: ", header_matcher.status().message())); - } - route->matchers.header_matchers.emplace_back( - std::move(header_matcher.value())); - } - return GRPC_ERROR_NONE; -} - -grpc_error_handle RouteRuntimeFractionParse( - const envoy_config_route_v3_RouteMatch* match, XdsApi::Route* route) { - const envoy_config_core_v3_RuntimeFractionalPercent* runtime_fraction = - envoy_config_route_v3_RouteMatch_runtime_fraction(match); - if (runtime_fraction != nullptr) { - const envoy_type_v3_FractionalPercent* fraction = - envoy_config_core_v3_RuntimeFractionalPercent_default_value( - runtime_fraction); - if (fraction != nullptr) { - uint32_t numerator = envoy_type_v3_FractionalPercent_numerator(fraction); - const auto denominator = - static_cast( - envoy_type_v3_FractionalPercent_denominator(fraction)); - // Normalize to million. - switch (denominator) { - case envoy_type_v3_FractionalPercent_HUNDRED: - numerator *= 10000; - break; - case envoy_type_v3_FractionalPercent_TEN_THOUSAND: - numerator *= 100; - break; - case envoy_type_v3_FractionalPercent_MILLION: - break; - default: - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Unknown denominator type"); - } - route->matchers.fraction_per_million = numerator; - } - } - return GRPC_ERROR_NONE; -} - -grpc_error_handle ExtractHttpFilterTypeName(const EncodingContext& context, - const google_protobuf_Any* any, - absl::string_view* filter_type) { - *filter_type = UpbStringToAbsl(google_protobuf_Any_type_url(any)); - if (*filter_type == "type.googleapis.com/xds.type.v3.TypedStruct" || - *filter_type == "type.googleapis.com/udpa.type.v1.TypedStruct") { - upb_strview any_value = google_protobuf_Any_value(any); - const auto* typed_struct = xds_type_v3_TypedStruct_parse( - any_value.data, any_value.size, context.arena); - if (typed_struct == nullptr) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "could not parse TypedStruct from filter config"); - } - *filter_type = - UpbStringToAbsl(xds_type_v3_TypedStruct_type_url(typed_struct)); - } - *filter_type = absl::StripPrefix(*filter_type, "type.googleapis.com/"); - return GRPC_ERROR_NONE; -} - -template -grpc_error_handle ParseTypedPerFilterConfig( - const EncodingContext& context, const ParentType* parent, - const EntryType* (*entry_func)(const ParentType*, size_t*), - upb_strview (*key_func)(const EntryType*), - const google_protobuf_Any* (*value_func)(const EntryType*), - XdsApi::TypedPerFilterConfig* typed_per_filter_config) { - size_t filter_it = UPB_MAP_BEGIN; - while (true) { - const auto* filter_entry = entry_func(parent, &filter_it); - if (filter_entry == nullptr) break; - absl::string_view key = UpbStringToAbsl(key_func(filter_entry)); - if (key.empty()) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("empty filter name in map"); - } - const google_protobuf_Any* any = value_func(filter_entry); - GPR_ASSERT(any != nullptr); - absl::string_view filter_type = - UpbStringToAbsl(google_protobuf_Any_type_url(any)); - if (filter_type.empty()) { - return GRPC_ERROR_CREATE_FROM_CPP_STRING( - absl::StrCat("no filter config specified for filter name ", key)); - } - bool is_optional = false; - if (filter_type == - "type.googleapis.com/envoy.config.route.v3.FilterConfig") { - upb_strview any_value = google_protobuf_Any_value(any); - const auto* filter_config = envoy_config_route_v3_FilterConfig_parse( - any_value.data, any_value.size, context.arena); - if (filter_config == nullptr) { - return GRPC_ERROR_CREATE_FROM_CPP_STRING( - absl::StrCat("could not parse FilterConfig wrapper for ", key)); - } - is_optional = - envoy_config_route_v3_FilterConfig_is_optional(filter_config); - any = envoy_config_route_v3_FilterConfig_config(filter_config); - if (any == nullptr) { - if (is_optional) continue; - return GRPC_ERROR_CREATE_FROM_CPP_STRING( - absl::StrCat("no filter config specified for filter name ", key)); - } - } - grpc_error_handle error = - ExtractHttpFilterTypeName(context, any, &filter_type); - if (error != GRPC_ERROR_NONE) return error; - const XdsHttpFilterImpl* filter_impl = - XdsHttpFilterRegistry::GetFilterForType(filter_type); - if (filter_impl == nullptr) { - if (is_optional) continue; - return GRPC_ERROR_CREATE_FROM_CPP_STRING( - absl::StrCat("no filter registered for config type ", filter_type)); - } - absl::StatusOr filter_config = - filter_impl->GenerateFilterConfigOverride( - google_protobuf_Any_value(any), context.arena); - if (!filter_config.ok()) { - return GRPC_ERROR_CREATE_FROM_CPP_STRING(absl::StrCat( - "filter config for type ", filter_type, - " failed to parse: ", filter_config.status().ToString())); - } - (*typed_per_filter_config)[std::string(key)] = std::move(*filter_config); - } - return GRPC_ERROR_NONE; -} - -XdsApi::Duration DurationParse(const google_protobuf_Duration* proto_duration) { - XdsApi::Duration duration; - duration.seconds = google_protobuf_Duration_seconds(proto_duration); - duration.nanos = google_protobuf_Duration_nanos(proto_duration); - return duration; -} - -grpc_error_handle RetryPolicyParse( - const EncodingContext& context, - const envoy_config_route_v3_RetryPolicy* retry_policy, - absl::optional* retry) { - std::vector errors; - XdsApi::RetryPolicy retry_to_return; - auto retry_on = UpbStringToStdString( - envoy_config_route_v3_RetryPolicy_retry_on(retry_policy)); - std::vector codes = absl::StrSplit(retry_on, ','); - for (const auto& code : codes) { - if (code == "cancelled") { - retry_to_return.retry_on.Add(GRPC_STATUS_CANCELLED); - } else if (code == "deadline-exceeded") { - retry_to_return.retry_on.Add(GRPC_STATUS_DEADLINE_EXCEEDED); - } else if (code == "internal") { - retry_to_return.retry_on.Add(GRPC_STATUS_INTERNAL); - } else if (code == "resource-exhausted") { - retry_to_return.retry_on.Add(GRPC_STATUS_RESOURCE_EXHAUSTED); - } else if (code == "unavailable") { - retry_to_return.retry_on.Add(GRPC_STATUS_UNAVAILABLE); - } else { - if (GRPC_TRACE_FLAG_ENABLED(*context.tracer)) { - gpr_log(GPR_INFO, "Unsupported retry_on policy %s.", - std::string(code).c_str()); - } - } - } - const google_protobuf_UInt32Value* num_retries = - envoy_config_route_v3_RetryPolicy_num_retries(retry_policy); - if (num_retries != nullptr) { - uint32_t num_retries_value = google_protobuf_UInt32Value_value(num_retries); - if (num_retries_value == 0) { - errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "RouteAction RetryPolicy num_retries set to invalid value 0.")); - } else { - retry_to_return.num_retries = num_retries_value; - } - } else { - retry_to_return.num_retries = 1; - } - const envoy_config_route_v3_RetryPolicy_RetryBackOff* backoff = - envoy_config_route_v3_RetryPolicy_retry_back_off(retry_policy); - if (backoff != nullptr) { - const google_protobuf_Duration* base_interval = - envoy_config_route_v3_RetryPolicy_RetryBackOff_base_interval(backoff); - if (base_interval == nullptr) { - errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "RouteAction RetryPolicy RetryBackoff missing base interval.")); - } else { - retry_to_return.retry_back_off.base_interval = - DurationParse(base_interval); - } - const google_protobuf_Duration* max_interval = - envoy_config_route_v3_RetryPolicy_RetryBackOff_max_interval(backoff); - XdsApi::Duration max; - if (max_interval != nullptr) { - max = DurationParse(max_interval); - } else { - // if max interval is not set, it is 10x the base, if the value in nanos - // can yield another second, adjust the value in seconds accordingly. - max.seconds = retry_to_return.retry_back_off.base_interval.seconds * 10; - max.nanos = retry_to_return.retry_back_off.base_interval.nanos * 10; - if (max.nanos > 1000000000) { - max.seconds += max.nanos / 1000000000; - max.nanos = max.nanos % 1000000000; - } - } - retry_to_return.retry_back_off.max_interval = max; - } else { - retry_to_return.retry_back_off.base_interval.seconds = 0; - retry_to_return.retry_back_off.base_interval.nanos = 25000000; - retry_to_return.retry_back_off.max_interval.seconds = 0; - retry_to_return.retry_back_off.max_interval.nanos = 250000000; - } - if (errors.empty()) { - *retry = retry_to_return; - return GRPC_ERROR_NONE; - } else { - return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing retry policy", - &errors); - } -} - -grpc_error_handle RouteActionParse(const EncodingContext& context, - const envoy_config_route_v3_Route* route_msg, - XdsApi::Route::RouteAction* route, - bool* ignore_route) { - const envoy_config_route_v3_RouteAction* route_action = - envoy_config_route_v3_Route_route(route_msg); - // Get the cluster or weighted_clusters in the RouteAction. - if (envoy_config_route_v3_RouteAction_has_cluster(route_action)) { - route->cluster_name = UpbStringToStdString( - envoy_config_route_v3_RouteAction_cluster(route_action)); - if (route->cluster_name.empty()) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "RouteAction cluster contains empty cluster name."); - } - } else if (envoy_config_route_v3_RouteAction_has_weighted_clusters( - route_action)) { - const envoy_config_route_v3_WeightedCluster* weighted_cluster = - envoy_config_route_v3_RouteAction_weighted_clusters(route_action); - uint32_t total_weight = 100; - const google_protobuf_UInt32Value* weight = - envoy_config_route_v3_WeightedCluster_total_weight(weighted_cluster); - if (weight != nullptr) { - total_weight = google_protobuf_UInt32Value_value(weight); - } - size_t clusters_size; - const envoy_config_route_v3_WeightedCluster_ClusterWeight* const* clusters = - envoy_config_route_v3_WeightedCluster_clusters(weighted_cluster, - &clusters_size); - uint32_t sum_of_weights = 0; - for (size_t j = 0; j < clusters_size; ++j) { - const envoy_config_route_v3_WeightedCluster_ClusterWeight* - cluster_weight = clusters[j]; - XdsApi::Route::RouteAction::ClusterWeight cluster; - cluster.name = UpbStringToStdString( - envoy_config_route_v3_WeightedCluster_ClusterWeight_name( - cluster_weight)); - if (cluster.name.empty()) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "RouteAction weighted_cluster cluster contains empty cluster " - "name."); - } - const google_protobuf_UInt32Value* weight = - envoy_config_route_v3_WeightedCluster_ClusterWeight_weight( - cluster_weight); - if (weight == nullptr) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "RouteAction weighted_cluster cluster missing weight"); - } - cluster.weight = google_protobuf_UInt32Value_value(weight); - if (cluster.weight == 0) continue; - sum_of_weights += cluster.weight; - if (context.use_v3) { - grpc_error_handle error = ParseTypedPerFilterConfig< - envoy_config_route_v3_WeightedCluster_ClusterWeight, - envoy_config_route_v3_WeightedCluster_ClusterWeight_TypedPerFilterConfigEntry>( - context, cluster_weight, - envoy_config_route_v3_WeightedCluster_ClusterWeight_typed_per_filter_config_next, - envoy_config_route_v3_WeightedCluster_ClusterWeight_TypedPerFilterConfigEntry_key, - envoy_config_route_v3_WeightedCluster_ClusterWeight_TypedPerFilterConfigEntry_value, - &cluster.typed_per_filter_config); - if (error != GRPC_ERROR_NONE) return error; - } - route->weighted_clusters.emplace_back(std::move(cluster)); - } - if (total_weight != sum_of_weights) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "RouteAction weighted_cluster has incorrect total weight"); - } - if (route->weighted_clusters.empty()) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "RouteAction weighted_cluster has no valid clusters specified."); - } - } else { - // No cluster or weighted_clusters found in RouteAction, ignore this route. - *ignore_route = true; - } - if (!*ignore_route) { - const envoy_config_route_v3_RouteAction_MaxStreamDuration* - max_stream_duration = - envoy_config_route_v3_RouteAction_max_stream_duration(route_action); - if (max_stream_duration != nullptr) { - const google_protobuf_Duration* duration = - envoy_config_route_v3_RouteAction_MaxStreamDuration_grpc_timeout_header_max( - max_stream_duration); - if (duration == nullptr) { - duration = - envoy_config_route_v3_RouteAction_MaxStreamDuration_max_stream_duration( - max_stream_duration); - } - if (duration != nullptr) { - route->max_stream_duration = DurationParse(duration); - } - } - } - // Get HashPolicy from RouteAction - size_t size = 0; - const envoy_config_route_v3_RouteAction_HashPolicy* const* hash_policies = - envoy_config_route_v3_RouteAction_hash_policy(route_action, &size); - for (size_t i = 0; i < size; ++i) { - const envoy_config_route_v3_RouteAction_HashPolicy* hash_policy = - hash_policies[i]; - XdsApi::Route::RouteAction::HashPolicy policy; - policy.terminal = - envoy_config_route_v3_RouteAction_HashPolicy_terminal(hash_policy); - const envoy_config_route_v3_RouteAction_HashPolicy_Header* header; - const envoy_config_route_v3_RouteAction_HashPolicy_FilterState* - filter_state; - if ((header = envoy_config_route_v3_RouteAction_HashPolicy_header( - hash_policy)) != nullptr) { - policy.type = XdsApi::Route::RouteAction::HashPolicy::Type::HEADER; - policy.header_name = UpbStringToStdString( - envoy_config_route_v3_RouteAction_HashPolicy_Header_header_name( - header)); - const struct envoy_type_matcher_v3_RegexMatchAndSubstitute* - regex_rewrite = - envoy_config_route_v3_RouteAction_HashPolicy_Header_regex_rewrite( - header); - if (regex_rewrite != nullptr) { - const envoy_type_matcher_v3_RegexMatcher* regex_matcher = - envoy_type_matcher_v3_RegexMatchAndSubstitute_pattern( - regex_rewrite); - if (regex_matcher == nullptr) { - gpr_log( - GPR_DEBUG, - "RouteAction HashPolicy contains policy specifier Header with " - "RegexMatchAndSubstitution but RegexMatcher pattern is " - "missing"); - continue; - } - RE2::Options options; - policy.regex = absl::make_unique( - UpbStringToStdString( - envoy_type_matcher_v3_RegexMatcher_regex(regex_matcher)), - options); - if (!policy.regex->ok()) { - gpr_log( - GPR_DEBUG, - "RouteAction HashPolicy contains policy specifier Header with " - "RegexMatchAndSubstitution but RegexMatcher pattern does not " - "compile"); - continue; - } - policy.regex_substitution = UpbStringToStdString( - envoy_type_matcher_v3_RegexMatchAndSubstitute_substitution( - regex_rewrite)); - } - } else if ((filter_state = - envoy_config_route_v3_RouteAction_HashPolicy_filter_state( - hash_policy)) != nullptr) { - std::string key = UpbStringToStdString( - envoy_config_route_v3_RouteAction_HashPolicy_FilterState_key( - filter_state)); - if (key == "io.grpc.channel_id") { - policy.type = XdsApi::Route::RouteAction::HashPolicy::Type::CHANNEL_ID; - } else { - gpr_log(GPR_DEBUG, - "RouteAction HashPolicy contains policy specifier " - "FilterState but " - "key is not io.grpc.channel_id."); - continue; - } - } else { - gpr_log(GPR_DEBUG, - "RouteAction HashPolicy contains unsupported policy specifier."); - continue; - } - route->hash_policies.emplace_back(std::move(policy)); - } - // Get retry policy - const envoy_config_route_v3_RetryPolicy* retry_policy = - envoy_config_route_v3_RouteAction_retry_policy(route_action); - if (retry_policy != nullptr) { - absl::optional retry; - grpc_error_handle error = RetryPolicyParse(context, retry_policy, &retry); - if (error != GRPC_ERROR_NONE) return error; - route->retry_policy = retry; - } - return GRPC_ERROR_NONE; -} - -grpc_error_handle RouteConfigParse( - const EncodingContext& context, - const envoy_config_route_v3_RouteConfiguration* route_config, - bool /*is_v2*/, XdsApi::RdsUpdate* rds_update) { - // Get the virtual hosts. - size_t num_virtual_hosts; - const envoy_config_route_v3_VirtualHost* const* virtual_hosts = - envoy_config_route_v3_RouteConfiguration_virtual_hosts( - route_config, &num_virtual_hosts); - for (size_t i = 0; i < num_virtual_hosts; ++i) { - rds_update->virtual_hosts.emplace_back(); - XdsApi::RdsUpdate::VirtualHost& vhost = rds_update->virtual_hosts.back(); - // Parse domains. - size_t domain_size; - upb_strview const* domains = envoy_config_route_v3_VirtualHost_domains( - virtual_hosts[i], &domain_size); - for (size_t j = 0; j < domain_size; ++j) { - std::string domain_pattern = UpbStringToStdString(domains[j]); - if (!XdsRouting::IsValidDomainPattern(domain_pattern)) { - return GRPC_ERROR_CREATE_FROM_CPP_STRING( - absl::StrCat("Invalid domain pattern \"", domain_pattern, "\".")); - } - vhost.domains.emplace_back(std::move(domain_pattern)); - } - if (vhost.domains.empty()) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("VirtualHost has no domains"); - } - // Parse typed_per_filter_config. - if (context.use_v3) { - grpc_error_handle error = ParseTypedPerFilterConfig< - envoy_config_route_v3_VirtualHost, - envoy_config_route_v3_VirtualHost_TypedPerFilterConfigEntry>( - context, virtual_hosts[i], - envoy_config_route_v3_VirtualHost_typed_per_filter_config_next, - envoy_config_route_v3_VirtualHost_TypedPerFilterConfigEntry_key, - envoy_config_route_v3_VirtualHost_TypedPerFilterConfigEntry_value, - &vhost.typed_per_filter_config); - if (error != GRPC_ERROR_NONE) return error; - } - // Parse retry policy. - absl::optional virtual_host_retry_policy; - const envoy_config_route_v3_RetryPolicy* retry_policy = - envoy_config_route_v3_VirtualHost_retry_policy(virtual_hosts[i]); - if (retry_policy != nullptr) { - grpc_error_handle error = - RetryPolicyParse(context, retry_policy, &virtual_host_retry_policy); - if (error != GRPC_ERROR_NONE) return error; - } - // Parse routes. - size_t num_routes; - const envoy_config_route_v3_Route* const* routes = - envoy_config_route_v3_VirtualHost_routes(virtual_hosts[i], &num_routes); - if (num_routes < 1) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "No route found in the virtual host."); - } - // Loop over the whole list of routes - for (size_t j = 0; j < num_routes; ++j) { - const envoy_config_route_v3_RouteMatch* match = - envoy_config_route_v3_Route_match(routes[j]); - if (match == nullptr) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Match can't be null."); - } - size_t query_parameters_size; - static_cast(envoy_config_route_v3_RouteMatch_query_parameters( - match, &query_parameters_size)); - if (query_parameters_size > 0) { - continue; - } - XdsApi::Route route; - bool ignore_route = false; - grpc_error_handle error = - RoutePathMatchParse(match, &route, &ignore_route); - if (error != GRPC_ERROR_NONE) return error; - if (ignore_route) continue; - error = RouteHeaderMatchersParse(match, &route); - if (error != GRPC_ERROR_NONE) return error; - error = RouteRuntimeFractionParse(match, &route); - if (error != GRPC_ERROR_NONE) return error; - if (envoy_config_route_v3_Route_has_route(routes[j])) { - route.action.emplace(); - auto& route_action = - absl::get(route.action); - error = - RouteActionParse(context, routes[j], &route_action, &ignore_route); - if (error != GRPC_ERROR_NONE) return error; - if (ignore_route) continue; - if (route_action.retry_policy == absl::nullopt && - retry_policy != nullptr) { - route_action.retry_policy = virtual_host_retry_policy; - } - } else if (envoy_config_route_v3_Route_has_non_forwarding_action( - routes[j])) { - route.action.emplace(); - } - if (context.use_v3) { - grpc_error_handle error = ParseTypedPerFilterConfig< - envoy_config_route_v3_Route, - envoy_config_route_v3_Route_TypedPerFilterConfigEntry>( - context, routes[j], - envoy_config_route_v3_Route_typed_per_filter_config_next, - envoy_config_route_v3_Route_TypedPerFilterConfigEntry_key, - envoy_config_route_v3_Route_TypedPerFilterConfigEntry_value, - &route.typed_per_filter_config); - if (error != GRPC_ERROR_NONE) return error; - } - vhost.routes.emplace_back(std::move(route)); - } - if (vhost.routes.empty()) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("No valid routes specified."); - } - } - return GRPC_ERROR_NONE; -} - -// CertificateProviderInstance is deprecated but we are still supporting it for -// backward compatibility reasons. Note that we still parse the data into the -// same CertificateProviderPluginInstance struct since the fields are the same. -// TODO(yashykt): Remove this once we stop supporting the old way of fetching -// certificate provider instances. -grpc_error_handle CertificateProviderInstanceParse( - const EncodingContext& context, - const envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CertificateProviderInstance* - certificate_provider_instance_proto, - XdsApi::CommonTlsContext::CertificateProviderPluginInstance* - certificate_provider_plugin_instance) { - *certificate_provider_plugin_instance = { - UpbStringToStdString( - envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CertificateProviderInstance_instance_name( - certificate_provider_instance_proto)), - UpbStringToStdString( - envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CertificateProviderInstance_certificate_name( - certificate_provider_instance_proto))}; - if (context.certificate_provider_definition_map->find( - certificate_provider_plugin_instance->instance_name) == - context.certificate_provider_definition_map->end()) { - return GRPC_ERROR_CREATE_FROM_CPP_STRING( - absl::StrCat("Unrecognized certificate provider instance name: ", - certificate_provider_plugin_instance->instance_name)); - } - return GRPC_ERROR_NONE; -} - -grpc_error_handle CertificateProviderPluginInstanceParse( - const EncodingContext& context, - const envoy_extensions_transport_sockets_tls_v3_CertificateProviderPluginInstance* - certificate_provider_plugin_instance_proto, - XdsApi::CommonTlsContext::CertificateProviderPluginInstance* - certificate_provider_plugin_instance) { - *certificate_provider_plugin_instance = { - UpbStringToStdString( - envoy_extensions_transport_sockets_tls_v3_CertificateProviderPluginInstance_instance_name( - certificate_provider_plugin_instance_proto)), - UpbStringToStdString( - envoy_extensions_transport_sockets_tls_v3_CertificateProviderPluginInstance_certificate_name( - certificate_provider_plugin_instance_proto))}; - if (context.certificate_provider_definition_map->find( - certificate_provider_plugin_instance->instance_name) == - context.certificate_provider_definition_map->end()) { - return GRPC_ERROR_CREATE_FROM_CPP_STRING( - absl::StrCat("Unrecognized certificate provider instance name: ", - certificate_provider_plugin_instance->instance_name)); - } - return GRPC_ERROR_NONE; -} - -grpc_error_handle CertificateValidationContextParse( - const EncodingContext& context, - const envoy_extensions_transport_sockets_tls_v3_CertificateValidationContext* - certificate_validation_context_proto, - XdsApi::CommonTlsContext::CertificateValidationContext* - certificate_validation_context) { - std::vector errors; - size_t len = 0; - auto* subject_alt_names_matchers = - envoy_extensions_transport_sockets_tls_v3_CertificateValidationContext_match_subject_alt_names( - certificate_validation_context_proto, &len); - for (size_t i = 0; i < len; ++i) { - StringMatcher::Type type; - std::string matcher; - if (envoy_type_matcher_v3_StringMatcher_has_exact( - subject_alt_names_matchers[i])) { - type = StringMatcher::Type::kExact; - matcher = UpbStringToStdString(envoy_type_matcher_v3_StringMatcher_exact( - subject_alt_names_matchers[i])); - } else if (envoy_type_matcher_v3_StringMatcher_has_prefix( - subject_alt_names_matchers[i])) { - type = StringMatcher::Type::kPrefix; - matcher = UpbStringToStdString(envoy_type_matcher_v3_StringMatcher_prefix( - subject_alt_names_matchers[i])); - } else if (envoy_type_matcher_v3_StringMatcher_has_suffix( - subject_alt_names_matchers[i])) { - type = StringMatcher::Type::kSuffix; - matcher = UpbStringToStdString(envoy_type_matcher_v3_StringMatcher_suffix( - subject_alt_names_matchers[i])); - } else if (envoy_type_matcher_v3_StringMatcher_has_contains( - subject_alt_names_matchers[i])) { - type = StringMatcher::Type::kContains; - matcher = - UpbStringToStdString(envoy_type_matcher_v3_StringMatcher_contains( - subject_alt_names_matchers[i])); - } else if (envoy_type_matcher_v3_StringMatcher_has_safe_regex( - subject_alt_names_matchers[i])) { - type = StringMatcher::Type::kSafeRegex; - auto* regex_matcher = envoy_type_matcher_v3_StringMatcher_safe_regex( - subject_alt_names_matchers[i]); - matcher = UpbStringToStdString( - envoy_type_matcher_v3_RegexMatcher_regex(regex_matcher)); - } else { - errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Invalid StringMatcher specified")); - continue; - } - bool ignore_case = envoy_type_matcher_v3_StringMatcher_ignore_case( - subject_alt_names_matchers[i]); - absl::StatusOr string_matcher = - StringMatcher::Create(type, matcher, - /*case_sensitive=*/!ignore_case); - if (!string_matcher.ok()) { - errors.push_back(GRPC_ERROR_CREATE_FROM_CPP_STRING( - absl::StrCat("string matcher: ", string_matcher.status().message()))); - continue; - } - if (type == StringMatcher::Type::kSafeRegex && ignore_case) { - errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "StringMatcher: ignore_case has no effect for SAFE_REGEX.")); - continue; - } - certificate_validation_context->match_subject_alt_names.push_back( - std::move(string_matcher.value())); - } - auto* ca_certificate_provider_instance = - envoy_extensions_transport_sockets_tls_v3_CertificateValidationContext_ca_certificate_provider_instance( - certificate_validation_context_proto); - if (ca_certificate_provider_instance != nullptr) { - grpc_error_handle error = CertificateProviderPluginInstanceParse( - context, ca_certificate_provider_instance, - &certificate_validation_context->ca_certificate_provider_instance); - if (error != GRPC_ERROR_NONE) errors.push_back(error); - } - if (envoy_extensions_transport_sockets_tls_v3_CertificateValidationContext_verify_certificate_spki( - certificate_validation_context_proto, nullptr) != nullptr) { - errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "CertificateValidationContext: verify_certificate_spki " - "unsupported")); - } - if (envoy_extensions_transport_sockets_tls_v3_CertificateValidationContext_verify_certificate_hash( - certificate_validation_context_proto, nullptr) != nullptr) { - errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "CertificateValidationContext: verify_certificate_hash " - "unsupported")); - } - auto* require_signed_certificate_timestamp = - envoy_extensions_transport_sockets_tls_v3_CertificateValidationContext_require_signed_certificate_timestamp( - certificate_validation_context_proto); - if (require_signed_certificate_timestamp != nullptr && - google_protobuf_BoolValue_value(require_signed_certificate_timestamp)) { - errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "CertificateValidationContext: " - "require_signed_certificate_timestamp unsupported")); - } - if (envoy_extensions_transport_sockets_tls_v3_CertificateValidationContext_has_crl( - certificate_validation_context_proto)) { - errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "CertificateValidationContext: crl unsupported")); - } - if (envoy_extensions_transport_sockets_tls_v3_CertificateValidationContext_has_custom_validator_config( - certificate_validation_context_proto)) { - errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "CertificateValidationContext: custom_validator_config " - "unsupported")); - } - return GRPC_ERROR_CREATE_FROM_VECTOR( - "Error parsing CertificateValidationContext", &errors); -} - -grpc_error_handle CommonTlsContextParse( - const EncodingContext& context, - const envoy_extensions_transport_sockets_tls_v3_CommonTlsContext* - common_tls_context_proto, - XdsApi::CommonTlsContext* common_tls_context) { - std::vector errors; - // The validation context is derived from the oneof in - // 'validation_context_type'. 'validation_context_sds_secret_config' is not - // supported. - auto* combined_validation_context = - envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_combined_validation_context( - common_tls_context_proto); - if (combined_validation_context != nullptr) { - auto* default_validation_context = - envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CombinedCertificateValidationContext_default_validation_context( - combined_validation_context); - if (default_validation_context != nullptr) { - grpc_error_handle error = CertificateValidationContextParse( - context, default_validation_context, - &common_tls_context->certificate_validation_context); - if (error != GRPC_ERROR_NONE) errors.push_back(error); - } - // If after parsing default_validation_context, - // common_tls_context->certificate_validation_context.ca_certificate_provider_instance - // is empty, fall back onto - // 'validation_context_certificate_provider_instance' inside - // 'combined_validation_context'. Note that this way of fetching root - // certificates is deprecated and will be removed in the future. - // TODO(yashykt): Remove this once it's no longer needed. - auto* validation_context_certificate_provider_instance = - envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CombinedCertificateValidationContext_validation_context_certificate_provider_instance( - combined_validation_context); - if (common_tls_context->certificate_validation_context - .ca_certificate_provider_instance.Empty() && - validation_context_certificate_provider_instance != nullptr) { - grpc_error_handle error = CertificateProviderInstanceParse( - context, validation_context_certificate_provider_instance, - &common_tls_context->certificate_validation_context - .ca_certificate_provider_instance); - if (error != GRPC_ERROR_NONE) errors.push_back(error); - } - } else { - auto* validation_context = - envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_validation_context( - common_tls_context_proto); - if (validation_context != nullptr) { - grpc_error_handle error = CertificateValidationContextParse( - context, validation_context, - &common_tls_context->certificate_validation_context); - if (error != GRPC_ERROR_NONE) errors.push_back(error); - } else if ( - envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_has_validation_context_sds_secret_config( - common_tls_context_proto)) { - errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "validation_context_sds_secret_config unsupported")); - } - } - auto* tls_certificate_provider_instance = - envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_tls_certificate_provider_instance( - common_tls_context_proto); - if (tls_certificate_provider_instance != nullptr) { - grpc_error_handle error = CertificateProviderPluginInstanceParse( - context, tls_certificate_provider_instance, - &common_tls_context->tls_certificate_provider_instance); - if (error != GRPC_ERROR_NONE) errors.push_back(error); - } else { - // Fall back onto 'tls_certificate_certificate_provider_instance'. Note that - // this way of fetching identity certificates is deprecated and will be - // removed in the future. - // TODO(yashykt): Remove this once it's no longer needed. - auto* tls_certificate_certificate_provider_instance = - envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_tls_certificate_certificate_provider_instance( - common_tls_context_proto); - if (tls_certificate_certificate_provider_instance != nullptr) { - grpc_error_handle error = CertificateProviderInstanceParse( - context, tls_certificate_certificate_provider_instance, - &common_tls_context->tls_certificate_provider_instance); - if (error != GRPC_ERROR_NONE) errors.push_back(error); - } else { - if (envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_has_tls_certificates( - common_tls_context_proto)) { - errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "tls_certificates unsupported")); - } - if (envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_has_tls_certificate_sds_secret_configs( - common_tls_context_proto)) { - errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "tls_certificate_sds_secret_configs unsupported")); - } - } - } - if (envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_has_tls_params( - common_tls_context_proto)) { - errors.push_back( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("tls_params unsupported")); - } - if (envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_has_custom_handshaker( - common_tls_context_proto)) { - errors.push_back( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("custom_handshaker unsupported")); - } - return GRPC_ERROR_CREATE_FROM_VECTOR("Error parsing CommonTlsContext", - &errors); -} - -grpc_error_handle HttpConnectionManagerParse( - bool is_client, const EncodingContext& context, - const envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager* - http_connection_manager_proto, - bool is_v2, - XdsApi::LdsUpdate::HttpConnectionManager* http_connection_manager) { - MaybeLogHttpConnectionManager(context, http_connection_manager_proto); - // Obtain max_stream_duration from Http Protocol Options. - const envoy_config_core_v3_HttpProtocolOptions* options = - envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_common_http_protocol_options( - http_connection_manager_proto); - if (options != nullptr) { - const google_protobuf_Duration* duration = - envoy_config_core_v3_HttpProtocolOptions_max_stream_duration(options); - if (duration != nullptr) { - http_connection_manager->http_max_stream_duration = - DurationParse(duration); - } - } - // Parse filters. - if (!is_v2) { - size_t num_filters = 0; - const auto* http_filters = - envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_http_filters( - http_connection_manager_proto, &num_filters); - std::set names_seen; - for (size_t i = 0; i < num_filters; ++i) { - const auto* http_filter = http_filters[i]; - absl::string_view name = UpbStringToAbsl( - envoy_extensions_filters_network_http_connection_manager_v3_HttpFilter_name( - http_filter)); - if (name.empty()) { - return GRPC_ERROR_CREATE_FROM_CPP_STRING( - absl::StrCat("empty filter name at index ", i)); - } - if (names_seen.find(name) != names_seen.end()) { - return GRPC_ERROR_CREATE_FROM_CPP_STRING( - absl::StrCat("duplicate HTTP filter name: ", name)); - } - names_seen.insert(name); - const bool is_optional = - envoy_extensions_filters_network_http_connection_manager_v3_HttpFilter_is_optional( - http_filter); - const google_protobuf_Any* any = - envoy_extensions_filters_network_http_connection_manager_v3_HttpFilter_typed_config( - http_filter); - if (any == nullptr) { - if (is_optional) continue; - return GRPC_ERROR_CREATE_FROM_CPP_STRING( - absl::StrCat("no filter config specified for filter name ", name)); - } - absl::string_view filter_type; - grpc_error_handle error = - ExtractHttpFilterTypeName(context, any, &filter_type); - if (error != GRPC_ERROR_NONE) return error; - const XdsHttpFilterImpl* filter_impl = - XdsHttpFilterRegistry::GetFilterForType(filter_type); - if (filter_impl == nullptr) { - if (is_optional) continue; - return GRPC_ERROR_CREATE_FROM_CPP_STRING( - absl::StrCat("no filter registered for config type ", filter_type)); - } - if ((is_client && !filter_impl->IsSupportedOnClients()) || - (!is_client && !filter_impl->IsSupportedOnServers())) { - if (is_optional) continue; - return GRPC_ERROR_CREATE_FROM_CPP_STRING( - absl::StrFormat("Filter %s is not supported on %s", filter_type, - is_client ? "clients" : "servers")); - } - absl::StatusOr filter_config = - filter_impl->GenerateFilterConfig(google_protobuf_Any_value(any), - context.arena); - if (!filter_config.ok()) { - return GRPC_ERROR_CREATE_FROM_CPP_STRING(absl::StrCat( - "filter config for type ", filter_type, - " failed to parse: ", filter_config.status().ToString())); - } - http_connection_manager->http_filters.emplace_back( - XdsApi::LdsUpdate::HttpConnectionManager::HttpFilter{ - std::string(name), std::move(*filter_config)}); - } - if (http_connection_manager->http_filters.empty()) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Expected at least one HTTP filter"); - } - // Make sure that the last filter is terminal and non-last filters are - // non-terminal. Note that this check is being performed in a separate loop - // to take care of the case where there are two terminal filters in the list - // out of which only one gets added in the final list. - for (const auto& http_filter : http_connection_manager->http_filters) { - const XdsHttpFilterImpl* filter_impl = - XdsHttpFilterRegistry::GetFilterForType( - http_filter.config.config_proto_type_name); - if (&http_filter != &http_connection_manager->http_filters.back()) { - // Filters before the last filter must not be terminal. - if (filter_impl->IsTerminalFilter()) { - return GRPC_ERROR_CREATE_FROM_CPP_STRING( - absl::StrCat("terminal filter for config type ", - http_filter.config.config_proto_type_name, - " must be the last filter in the chain")); - } - } else { - // The last filter must be terminal. - if (!filter_impl->IsTerminalFilter()) { - return GRPC_ERROR_CREATE_FROM_CPP_STRING( - absl::StrCat("non-terminal filter for config type ", - http_filter.config.config_proto_type_name, - " is the last filter in the chain")); - } - } - } - } else { - // If using a v2 config, we just hard-code a list containing only the - // router filter without actually looking at the config. This ensures - // that the right thing happens in the xds resolver without having - // to expose whether the resource we received was v2 or v3. - http_connection_manager->http_filters.emplace_back( - XdsApi::LdsUpdate::HttpConnectionManager::HttpFilter{ - "router", {kXdsHttpRouterFilterConfigName, Json()}}); - } - // Guarding parsing of RouteConfig on the server side with the environmental - // variable since that's the first feature on the server side that will be - // using this. - if (is_client || XdsRbacEnabled()) { - // Found inlined route_config. Parse it to find the cluster_name. - if (envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_has_route_config( - http_connection_manager_proto)) { - const envoy_config_route_v3_RouteConfiguration* route_config = - envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_route_config( - http_connection_manager_proto); - XdsApi::RdsUpdate rds_update; - grpc_error_handle error = - RouteConfigParse(context, route_config, is_v2, &rds_update); - if (error != GRPC_ERROR_NONE) return error; - http_connection_manager->rds_update = std::move(rds_update); - return GRPC_ERROR_NONE; - } - // Validate that RDS must be used to get the route_config dynamically. - const envoy_extensions_filters_network_http_connection_manager_v3_Rds* rds = - envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_rds( - http_connection_manager_proto); - if (rds == nullptr) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "HttpConnectionManager neither has inlined route_config nor RDS."); - } - // Check that the ConfigSource specifies ADS. - const envoy_config_core_v3_ConfigSource* config_source = - envoy_extensions_filters_network_http_connection_manager_v3_Rds_config_source( - rds); - if (config_source == nullptr) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "HttpConnectionManager missing config_source for RDS."); - } - if (!envoy_config_core_v3_ConfigSource_has_ads(config_source)) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "HttpConnectionManager ConfigSource for RDS does not specify ADS."); - } - // Get the route_config_name. - http_connection_manager->route_config_name = UpbStringToStdString( - envoy_extensions_filters_network_http_connection_manager_v3_Rds_route_config_name( - rds)); - } - return GRPC_ERROR_NONE; -} - -grpc_error_handle LdsResourceParseClient( - const EncodingContext& context, - const envoy_config_listener_v3_ApiListener* api_listener, bool is_v2, - XdsApi::LdsUpdate* lds_update) { - lds_update->type = XdsApi::LdsUpdate::ListenerType::kHttpApiListener; - const upb_strview encoded_api_listener = google_protobuf_Any_value( - envoy_config_listener_v3_ApiListener_api_listener(api_listener)); - const auto* http_connection_manager = - envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_parse( - encoded_api_listener.data, encoded_api_listener.size, context.arena); - if (http_connection_manager == nullptr) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Could not parse HttpConnectionManager config from ApiListener"); - } - return HttpConnectionManagerParse(true /* is_client */, context, - http_connection_manager, is_v2, - &lds_update->http_connection_manager); -} - -grpc_error_handle DownstreamTlsContextParse( - const EncodingContext& context, - const envoy_config_core_v3_TransportSocket* transport_socket, - XdsApi::DownstreamTlsContext* downstream_tls_context) { - absl::string_view name = UpbStringToAbsl( - envoy_config_core_v3_TransportSocket_name(transport_socket)); - if (name != "envoy.transport_sockets.tls") { - return GRPC_ERROR_CREATE_FROM_CPP_STRING( - absl::StrCat("Unrecognized transport socket: ", name)); - } - auto* typed_config = - envoy_config_core_v3_TransportSocket_typed_config(transport_socket); - std::vector errors; - if (typed_config != nullptr) { - const upb_strview encoded_downstream_tls_context = - google_protobuf_Any_value(typed_config); - auto* downstream_tls_context_proto = - envoy_extensions_transport_sockets_tls_v3_DownstreamTlsContext_parse( - encoded_downstream_tls_context.data, - encoded_downstream_tls_context.size, context.arena); - if (downstream_tls_context_proto == nullptr) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Can't decode downstream tls context."); - } - auto* common_tls_context = - envoy_extensions_transport_sockets_tls_v3_DownstreamTlsContext_common_tls_context( - downstream_tls_context_proto); - if (common_tls_context != nullptr) { - grpc_error_handle error = - CommonTlsContextParse(context, common_tls_context, - &downstream_tls_context->common_tls_context); - if (error != GRPC_ERROR_NONE) errors.push_back(error); - } - auto* require_client_certificate = - envoy_extensions_transport_sockets_tls_v3_DownstreamTlsContext_require_client_certificate( - downstream_tls_context_proto); - if (require_client_certificate != nullptr) { - downstream_tls_context->require_client_certificate = - google_protobuf_BoolValue_value(require_client_certificate); - } - auto* require_sni = - envoy_extensions_transport_sockets_tls_v3_DownstreamTlsContext_require_sni( - downstream_tls_context_proto); - if (require_sni != nullptr && - google_protobuf_BoolValue_value(require_sni)) { - errors.push_back( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("require_sni: unsupported")); - } - if (envoy_extensions_transport_sockets_tls_v3_DownstreamTlsContext_ocsp_staple_policy( - downstream_tls_context_proto) != - envoy_extensions_transport_sockets_tls_v3_DownstreamTlsContext_LENIENT_STAPLING) { - errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "ocsp_staple_policy: Only LENIENT_STAPLING supported")); - } - } - if (downstream_tls_context->common_tls_context - .tls_certificate_provider_instance.instance_name.empty()) { - errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "TLS configuration provided but no " - "tls_certificate_provider_instance found.")); - } - if (downstream_tls_context->require_client_certificate && - downstream_tls_context->common_tls_context.certificate_validation_context - .ca_certificate_provider_instance.instance_name.empty()) { - errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "TLS configuration requires client certificates but no certificate " - "provider instance specified for validation.")); - } - if (!downstream_tls_context->common_tls_context.certificate_validation_context - .match_subject_alt_names.empty()) { - errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "match_subject_alt_names not supported on servers")); - } - return GRPC_ERROR_CREATE_FROM_VECTOR("Error parsing DownstreamTlsContext", - &errors); -} - -grpc_error_handle CidrRangeParse( - const envoy_config_core_v3_CidrRange* cidr_range_proto, - XdsApi::LdsUpdate::FilterChainMap::CidrRange* cidr_range) { - std::string address_prefix = UpbStringToStdString( - envoy_config_core_v3_CidrRange_address_prefix(cidr_range_proto)); - grpc_error_handle error = - grpc_string_to_sockaddr(&cidr_range->address, address_prefix.c_str(), 0); - if (error != GRPC_ERROR_NONE) return error; - cidr_range->prefix_len = 0; - auto* prefix_len_proto = - envoy_config_core_v3_CidrRange_prefix_len(cidr_range_proto); - if (prefix_len_proto != nullptr) { - cidr_range->prefix_len = std::min( - google_protobuf_UInt32Value_value(prefix_len_proto), - (reinterpret_cast(cidr_range->address.addr)) - ->sa_family == GRPC_AF_INET - ? uint32_t(32) - : uint32_t(128)); - } - // Normalize the network address by masking it with prefix_len - grpc_sockaddr_mask_bits(&cidr_range->address, cidr_range->prefix_len); - return GRPC_ERROR_NONE; -} - -grpc_error_handle FilterChainMatchParse( - const envoy_config_listener_v3_FilterChainMatch* filter_chain_match_proto, - FilterChain::FilterChainMatch* filter_chain_match) { - auto* destination_port = - envoy_config_listener_v3_FilterChainMatch_destination_port( - filter_chain_match_proto); - if (destination_port != nullptr) { - filter_chain_match->destination_port = - google_protobuf_UInt32Value_value(destination_port); - } - size_t size = 0; - auto* prefix_ranges = envoy_config_listener_v3_FilterChainMatch_prefix_ranges( - filter_chain_match_proto, &size); - filter_chain_match->prefix_ranges.reserve(size); - for (size_t i = 0; i < size; i++) { - XdsApi::LdsUpdate::FilterChainMap::CidrRange cidr_range; - grpc_error_handle error = CidrRangeParse(prefix_ranges[i], &cidr_range); - if (error != GRPC_ERROR_NONE) return error; - filter_chain_match->prefix_ranges.push_back(cidr_range); - } - filter_chain_match->source_type = - static_cast( - envoy_config_listener_v3_FilterChainMatch_source_type( - filter_chain_match_proto)); - auto* source_prefix_ranges = - envoy_config_listener_v3_FilterChainMatch_source_prefix_ranges( - filter_chain_match_proto, &size); - filter_chain_match->source_prefix_ranges.reserve(size); - for (size_t i = 0; i < size; i++) { - XdsApi::LdsUpdate::FilterChainMap::CidrRange cidr_range; - grpc_error_handle error = - CidrRangeParse(source_prefix_ranges[i], &cidr_range); - if (error != GRPC_ERROR_NONE) return error; - filter_chain_match->source_prefix_ranges.push_back(cidr_range); - } - auto* source_ports = envoy_config_listener_v3_FilterChainMatch_source_ports( - filter_chain_match_proto, &size); - filter_chain_match->source_ports.reserve(size); - for (size_t i = 0; i < size; i++) { - filter_chain_match->source_ports.push_back(source_ports[i]); - } - auto* server_names = envoy_config_listener_v3_FilterChainMatch_server_names( - filter_chain_match_proto, &size); - for (size_t i = 0; i < size; i++) { - filter_chain_match->server_names.push_back( - UpbStringToStdString(server_names[i])); - } - filter_chain_match->transport_protocol = UpbStringToStdString( - envoy_config_listener_v3_FilterChainMatch_transport_protocol( - filter_chain_match_proto)); - auto* application_protocols = - envoy_config_listener_v3_FilterChainMatch_application_protocols( - filter_chain_match_proto, &size); - for (size_t i = 0; i < size; i++) { - filter_chain_match->application_protocols.push_back( - UpbStringToStdString(application_protocols[i])); - } - return GRPC_ERROR_NONE; -} - -grpc_error_handle FilterChainParse( - const EncodingContext& context, - const envoy_config_listener_v3_FilterChain* filter_chain_proto, bool is_v2, - FilterChain* filter_chain) { - std::vector errors; - auto* filter_chain_match = - envoy_config_listener_v3_FilterChain_filter_chain_match( - filter_chain_proto); - if (filter_chain_match != nullptr) { - grpc_error_handle error = FilterChainMatchParse( - filter_chain_match, &filter_chain->filter_chain_match); - if (error != GRPC_ERROR_NONE) errors.push_back(error); - } - filter_chain->filter_chain_data = - std::make_shared(); - // Parse the filters list. Currently we only support HttpConnectionManager. - size_t size = 0; - auto* filters = - envoy_config_listener_v3_FilterChain_filters(filter_chain_proto, &size); - if (size != 1) { - errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "FilterChain should have exactly one filter: HttpConnectionManager; no " - "other filter is supported at the moment")); - } else { - auto* typed_config = - envoy_config_listener_v3_Filter_typed_config(filters[0]); - if (typed_config == nullptr) { - errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "No typed_config found in filter.")); - } else { - absl::string_view type_url = - UpbStringToAbsl(google_protobuf_Any_type_url(typed_config)); - if (type_url != - "type.googleapis.com/" - "envoy.extensions.filters.network.http_connection_manager.v3." - "HttpConnectionManager") { - errors.push_back(GRPC_ERROR_CREATE_FROM_CPP_STRING( - absl::StrCat("Unsupported filter type ", type_url))); - } else { - const upb_strview encoded_http_connection_manager = - google_protobuf_Any_value(typed_config); - const auto* http_connection_manager = - envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_parse( - encoded_http_connection_manager.data, - encoded_http_connection_manager.size, context.arena); - if (http_connection_manager == nullptr) { - errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Could not parse HttpConnectionManager config from filter " - "typed_config")); - } else { - grpc_error_handle error = HttpConnectionManagerParse( - false /* is_client */, context, http_connection_manager, is_v2, - &filter_chain->filter_chain_data->http_connection_manager); - if (error != GRPC_ERROR_NONE) errors.push_back(error); - } - } - } - } - auto* transport_socket = - envoy_config_listener_v3_FilterChain_transport_socket(filter_chain_proto); - if (transport_socket != nullptr) { - grpc_error_handle error = DownstreamTlsContextParse( - context, transport_socket, - &filter_chain->filter_chain_data->downstream_tls_context); - if (error != GRPC_ERROR_NONE) errors.push_back(error); - } - return GRPC_ERROR_CREATE_FROM_VECTOR("Error parsing FilterChain", &errors); -} - -grpc_error_handle AddressParse( - const envoy_config_core_v3_Address* address_proto, std::string* address) { - const auto* socket_address = - envoy_config_core_v3_Address_socket_address(address_proto); - if (socket_address == nullptr) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Address does not have socket_address"); - } - if (envoy_config_core_v3_SocketAddress_protocol(socket_address) != - envoy_config_core_v3_SocketAddress_TCP) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "SocketAddress protocol is not TCP"); - } - uint32_t port = envoy_config_core_v3_SocketAddress_port_value(socket_address); - if (port > 65535) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Invalid port"); - } - *address = JoinHostPort( - UpbStringToAbsl( - envoy_config_core_v3_SocketAddress_address(socket_address)), - port); - return GRPC_ERROR_NONE; -} - -// An intermediate map for filter chains that we create to validate the list of -// filter chains received from the control plane and to finally create -// XdsApi::LdsUpdate::FilterChainMap -struct InternalFilterChainMap { - using SourceIpMap = - std::map; - using ConnectionSourceTypesArray = std::array; - struct DestinationIp { - absl::optional prefix_range; - bool transport_protocol_raw_buffer_provided = false; - ConnectionSourceTypesArray source_types_array; - }; - using DestinationIpMap = std::map; - DestinationIpMap destination_ip_map; -}; - -grpc_error_handle AddFilterChainDataForSourcePort( - const FilterChain& filter_chain, - XdsApi::LdsUpdate::FilterChainMap::SourcePortsMap* ports_map, - uint32_t port) { - auto insert_result = ports_map->emplace( - port, XdsApi::LdsUpdate::FilterChainMap::FilterChainDataSharedPtr{ - filter_chain.filter_chain_data}); - if (!insert_result.second) { - return GRPC_ERROR_CREATE_FROM_CPP_STRING(absl::StrCat( - "Duplicate matching rules detected when adding filter chain: ", - filter_chain.filter_chain_match.ToString())); - } - return GRPC_ERROR_NONE; -} - -grpc_error_handle AddFilterChainDataForSourcePorts( - const FilterChain& filter_chain, - XdsApi::LdsUpdate::FilterChainMap::SourcePortsMap* ports_map) { - if (filter_chain.filter_chain_match.source_ports.empty()) { - return AddFilterChainDataForSourcePort(filter_chain, ports_map, 0); - } else { - for (uint32_t port : filter_chain.filter_chain_match.source_ports) { - grpc_error_handle error = - AddFilterChainDataForSourcePort(filter_chain, ports_map, port); - if (error != GRPC_ERROR_NONE) return error; - } - } - return GRPC_ERROR_NONE; -} - -grpc_error_handle AddFilterChainDataForSourceIpRange( - const FilterChain& filter_chain, - InternalFilterChainMap::SourceIpMap* source_ip_map) { - if (filter_chain.filter_chain_match.source_prefix_ranges.empty()) { - auto insert_result = source_ip_map->emplace( - "", XdsApi::LdsUpdate::FilterChainMap::SourceIp()); - return AddFilterChainDataForSourcePorts( - filter_chain, &insert_result.first->second.ports_map); - } else { - for (const auto& prefix_range : - filter_chain.filter_chain_match.source_prefix_ranges) { - auto insert_result = source_ip_map->emplace( - absl::StrCat(grpc_sockaddr_to_string(&prefix_range.address, false), - "/", prefix_range.prefix_len), - XdsApi::LdsUpdate::FilterChainMap::SourceIp()); - if (insert_result.second) { - insert_result.first->second.prefix_range.emplace(prefix_range); - } - grpc_error_handle error = AddFilterChainDataForSourcePorts( - filter_chain, &insert_result.first->second.ports_map); - if (error != GRPC_ERROR_NONE) return error; - } - } - return GRPC_ERROR_NONE; -} - -grpc_error_handle AddFilterChainDataForSourceType( - const FilterChain& filter_chain, - InternalFilterChainMap::DestinationIp* destination_ip) { - GPR_ASSERT(static_cast( - filter_chain.filter_chain_match.source_type) < 3); - return AddFilterChainDataForSourceIpRange( - filter_chain, &destination_ip->source_types_array[static_cast( - filter_chain.filter_chain_match.source_type)]); -} - -grpc_error_handle AddFilterChainDataForApplicationProtocols( - const FilterChain& filter_chain, - InternalFilterChainMap::DestinationIp* destination_ip) { - // Only allow filter chains that do not mention application protocols - if (!filter_chain.filter_chain_match.application_protocols.empty()) { - return GRPC_ERROR_NONE; - } - return AddFilterChainDataForSourceType(filter_chain, destination_ip); -} - -grpc_error_handle AddFilterChainDataForTransportProtocol( - const FilterChain& filter_chain, - InternalFilterChainMap::DestinationIp* destination_ip) { - const std::string& transport_protocol = - filter_chain.filter_chain_match.transport_protocol; - // Only allow filter chains with no transport protocol or "raw_buffer" - if (!transport_protocol.empty() && transport_protocol != "raw_buffer") { - return GRPC_ERROR_NONE; - } - // If for this configuration, we've already seen filter chains that mention - // the transport protocol as "raw_buffer", we will never match filter chains - // that do not mention it. - if (destination_ip->transport_protocol_raw_buffer_provided && - transport_protocol.empty()) { - return GRPC_ERROR_NONE; - } - if (!transport_protocol.empty() && - !destination_ip->transport_protocol_raw_buffer_provided) { - destination_ip->transport_protocol_raw_buffer_provided = true; - // Clear out the previous entries if any since those entries did not mention - // "raw_buffer" - destination_ip->source_types_array = - InternalFilterChainMap::ConnectionSourceTypesArray(); - } - return AddFilterChainDataForApplicationProtocols(filter_chain, - destination_ip); -} - -grpc_error_handle AddFilterChainDataForServerNames( - const FilterChain& filter_chain, - InternalFilterChainMap::DestinationIp* destination_ip) { - // Don't continue adding filter chains with server names mentioned - if (!filter_chain.filter_chain_match.server_names.empty()) { - return GRPC_ERROR_NONE; - } - return AddFilterChainDataForTransportProtocol(filter_chain, destination_ip); -} - -grpc_error_handle AddFilterChainDataForDestinationIpRange( - const FilterChain& filter_chain, - InternalFilterChainMap::DestinationIpMap* destination_ip_map) { - if (filter_chain.filter_chain_match.prefix_ranges.empty()) { - auto insert_result = destination_ip_map->emplace( - "", InternalFilterChainMap::DestinationIp()); - return AddFilterChainDataForServerNames(filter_chain, - &insert_result.first->second); - } else { - for (const auto& prefix_range : - filter_chain.filter_chain_match.prefix_ranges) { - auto insert_result = destination_ip_map->emplace( - absl::StrCat(grpc_sockaddr_to_string(&prefix_range.address, false), - "/", prefix_range.prefix_len), - InternalFilterChainMap::DestinationIp()); - if (insert_result.second) { - insert_result.first->second.prefix_range.emplace(prefix_range); - } - grpc_error_handle error = AddFilterChainDataForServerNames( - filter_chain, &insert_result.first->second); - if (error != GRPC_ERROR_NONE) return error; - } - } - return GRPC_ERROR_NONE; -} - -XdsApi::LdsUpdate::FilterChainMap BuildFromInternalFilterChainMap( - InternalFilterChainMap* internal_filter_chain_map) { - XdsApi::LdsUpdate::FilterChainMap filter_chain_map; - for (auto& destination_ip_pair : - internal_filter_chain_map->destination_ip_map) { - XdsApi::LdsUpdate::FilterChainMap::DestinationIp destination_ip; - destination_ip.prefix_range = destination_ip_pair.second.prefix_range; - for (int i = 0; i < 3; i++) { - auto& source_ip_map = destination_ip_pair.second.source_types_array[i]; - for (auto& source_ip_pair : source_ip_map) { - destination_ip.source_types_array[i].push_back( - std::move(source_ip_pair.second)); - } - } - filter_chain_map.destination_ip_vector.push_back(std::move(destination_ip)); - } - return filter_chain_map; -} - -grpc_error_handle BuildFilterChainMap( - const std::vector& filter_chains, - XdsApi::LdsUpdate::FilterChainMap* filter_chain_map) { - InternalFilterChainMap internal_filter_chain_map; - for (const auto& filter_chain : filter_chains) { - // Discard filter chain entries that specify destination port - if (filter_chain.filter_chain_match.destination_port != 0) continue; - grpc_error_handle error = AddFilterChainDataForDestinationIpRange( - filter_chain, &internal_filter_chain_map.destination_ip_map); - if (error != GRPC_ERROR_NONE) return error; - } - *filter_chain_map = - BuildFromInternalFilterChainMap(&internal_filter_chain_map); - return GRPC_ERROR_NONE; -} - -grpc_error_handle LdsResourceParseServer( - const EncodingContext& context, - const envoy_config_listener_v3_Listener* listener, bool is_v2, - XdsApi::LdsUpdate* lds_update) { - lds_update->type = XdsApi::LdsUpdate::ListenerType::kTcpListener; - grpc_error_handle error = - AddressParse(envoy_config_listener_v3_Listener_address(listener), - &lds_update->address); - if (error != GRPC_ERROR_NONE) return error; - const auto* use_original_dst = - envoy_config_listener_v3_Listener_use_original_dst(listener); - if (use_original_dst != nullptr) { - if (google_protobuf_BoolValue_value(use_original_dst)) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Field \'use_original_dst\' is not supported."); - } - } - size_t size = 0; - auto* filter_chains = - envoy_config_listener_v3_Listener_filter_chains(listener, &size); - std::vector parsed_filter_chains; - parsed_filter_chains.reserve(size); - for (size_t i = 0; i < size; i++) { - FilterChain filter_chain; - error = FilterChainParse(context, filter_chains[i], is_v2, &filter_chain); - if (error != GRPC_ERROR_NONE) return error; - parsed_filter_chains.push_back(std::move(filter_chain)); - } - error = - BuildFilterChainMap(parsed_filter_chains, &lds_update->filter_chain_map); - if (error != GRPC_ERROR_NONE) return error; - auto* default_filter_chain = - envoy_config_listener_v3_Listener_default_filter_chain(listener); - if (default_filter_chain != nullptr) { - FilterChain filter_chain; - error = - FilterChainParse(context, default_filter_chain, is_v2, &filter_chain); - if (error != GRPC_ERROR_NONE) return error; - if (filter_chain.filter_chain_data != nullptr) { - lds_update->default_filter_chain = - std::move(*filter_chain.filter_chain_data); - } - } - if (size == 0 && default_filter_chain == nullptr) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("No filter chain provided."); - } - return GRPC_ERROR_NONE; -} - -grpc_error_handle LdsResourceParse( - const EncodingContext& context, - const envoy_config_listener_v3_Listener* listener, bool is_v2, - XdsApi::LdsUpdate* lds_update) { - // Check whether it's a client or server listener. - const envoy_config_listener_v3_ApiListener* api_listener = - envoy_config_listener_v3_Listener_api_listener(listener); - const envoy_config_core_v3_Address* address = - envoy_config_listener_v3_Listener_address(listener); - if (api_listener != nullptr && address != nullptr) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Listener has both address and ApiListener"); - } - if (api_listener == nullptr && address == nullptr) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Listener has neither address nor ApiListener"); - } - // Validate Listener fields. - grpc_error_handle error = GRPC_ERROR_NONE; - if (api_listener != nullptr) { - error = LdsResourceParseClient(context, api_listener, is_v2, lds_update); - } else { - error = LdsResourceParseServer(context, listener, is_v2, lds_update); - } - return error; -} - -grpc_error_handle UpstreamTlsContextParse( - const EncodingContext& context, - const envoy_config_core_v3_TransportSocket* transport_socket, - XdsApi::CommonTlsContext* common_tls_context) { - // Record Upstream tls context - absl::string_view name = UpbStringToAbsl( - envoy_config_core_v3_TransportSocket_name(transport_socket)); - if (name != "envoy.transport_sockets.tls") { - return GRPC_ERROR_CREATE_FROM_CPP_STRING( - absl::StrCat("Unrecognized transport socket: ", name)); - } - auto* typed_config = - envoy_config_core_v3_TransportSocket_typed_config(transport_socket); - if (typed_config != nullptr) { - const upb_strview encoded_upstream_tls_context = - google_protobuf_Any_value(typed_config); - auto* upstream_tls_context = - envoy_extensions_transport_sockets_tls_v3_UpstreamTlsContext_parse( - encoded_upstream_tls_context.data, - encoded_upstream_tls_context.size, context.arena); - if (upstream_tls_context == nullptr) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Can't decode upstream tls context."); - } - auto* common_tls_context_proto = - envoy_extensions_transport_sockets_tls_v3_UpstreamTlsContext_common_tls_context( - upstream_tls_context); - if (common_tls_context_proto != nullptr) { - grpc_error_handle error = CommonTlsContextParse( - context, common_tls_context_proto, common_tls_context); - if (error != GRPC_ERROR_NONE) { - return grpc_error_add_child(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Error parsing UpstreamTlsContext"), - error); - } - } - } - if (common_tls_context->certificate_validation_context - .ca_certificate_provider_instance.instance_name.empty()) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "UpstreamTlsContext: TLS configuration provided but no " - "ca_certificate_provider_instance found."); - } - return GRPC_ERROR_NONE; -} - -grpc_error_handle CdsLogicalDnsParse( - const envoy_config_cluster_v3_Cluster* cluster, - XdsApi::CdsUpdate* cds_update) { - const auto* load_assignment = - envoy_config_cluster_v3_Cluster_load_assignment(cluster); - if (load_assignment == nullptr) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "load_assignment not present for LOGICAL_DNS cluster"); - } - size_t num_localities; - const auto* const* localities = - envoy_config_endpoint_v3_ClusterLoadAssignment_endpoints(load_assignment, - &num_localities); - if (num_localities != 1) { - return GRPC_ERROR_CREATE_FROM_CPP_STRING( - absl::StrCat("load_assignment for LOGICAL_DNS cluster must have " - "exactly one locality, found ", - num_localities)); - } - size_t num_endpoints; - const auto* const* endpoints = - envoy_config_endpoint_v3_LocalityLbEndpoints_lb_endpoints(localities[0], - &num_endpoints); - if (num_endpoints != 1) { - return GRPC_ERROR_CREATE_FROM_CPP_STRING( - absl::StrCat("locality for LOGICAL_DNS cluster must have " - "exactly one endpoint, found ", - num_endpoints)); - } - const auto* endpoint = - envoy_config_endpoint_v3_LbEndpoint_endpoint(endpoints[0]); - if (endpoint == nullptr) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "LbEndpoint endpoint field not set"); - } - const auto* address = envoy_config_endpoint_v3_Endpoint_address(endpoint); - if (address == nullptr) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Endpoint address field not set"); - } - const auto* socket_address = - envoy_config_core_v3_Address_socket_address(address); - if (socket_address == nullptr) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Address socket_address field not set"); - } - if (envoy_config_core_v3_SocketAddress_resolver_name(socket_address).size != - 0) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "LOGICAL_DNS clusters must NOT have a custom resolver name set"); - } - absl::string_view address_str = UpbStringToAbsl( - envoy_config_core_v3_SocketAddress_address(socket_address)); - if (address_str.empty()) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "SocketAddress address field not set"); - } - if (!envoy_config_core_v3_SocketAddress_has_port_value(socket_address)) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "SocketAddress port_value field not set"); - } - cds_update->dns_hostname = JoinHostPort( - address_str, - envoy_config_core_v3_SocketAddress_port_value(socket_address)); - return GRPC_ERROR_NONE; -} - -grpc_error_handle CdsResourceParse( - const EncodingContext& context, - const envoy_config_cluster_v3_Cluster* cluster, bool /*is_v2*/, - XdsApi::CdsUpdate* cds_update) { - std::vector errors; - // Check the cluster_discovery_type. - if (!envoy_config_cluster_v3_Cluster_has_type(cluster) && - !envoy_config_cluster_v3_Cluster_has_cluster_type(cluster)) { - errors.push_back( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("DiscoveryType not found.")); - } else if (envoy_config_cluster_v3_Cluster_type(cluster) == - envoy_config_cluster_v3_Cluster_EDS) { - cds_update->cluster_type = XdsApi::CdsUpdate::ClusterType::EDS; - // Check the EDS config source. - const envoy_config_cluster_v3_Cluster_EdsClusterConfig* eds_cluster_config = - envoy_config_cluster_v3_Cluster_eds_cluster_config(cluster); - const envoy_config_core_v3_ConfigSource* eds_config = - envoy_config_cluster_v3_Cluster_EdsClusterConfig_eds_config( - eds_cluster_config); - if (!envoy_config_core_v3_ConfigSource_has_ads(eds_config)) { - errors.push_back( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("EDS ConfigSource is not ADS.")); - } - // Record EDS service_name (if any). - upb_strview service_name = - envoy_config_cluster_v3_Cluster_EdsClusterConfig_service_name( - eds_cluster_config); - if (service_name.size != 0) { - cds_update->eds_service_name = UpbStringToStdString(service_name); - } - } else if (!XdsAggregateAndLogicalDnsClusterEnabled()) { - errors.push_back( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("DiscoveryType is not valid.")); - } else if (envoy_config_cluster_v3_Cluster_type(cluster) == - envoy_config_cluster_v3_Cluster_LOGICAL_DNS) { - cds_update->cluster_type = XdsApi::CdsUpdate::ClusterType::LOGICAL_DNS; - grpc_error_handle error = CdsLogicalDnsParse(cluster, cds_update); - if (error != GRPC_ERROR_NONE) errors.push_back(error); - } else { - if (!envoy_config_cluster_v3_Cluster_has_cluster_type(cluster)) { - errors.push_back( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("DiscoveryType is not valid.")); - } else { - const envoy_config_cluster_v3_Cluster_CustomClusterType* - custom_cluster_type = - envoy_config_cluster_v3_Cluster_cluster_type(cluster); - upb_strview type_name = - envoy_config_cluster_v3_Cluster_CustomClusterType_name( - custom_cluster_type); - if (UpbStringToAbsl(type_name) != "envoy.clusters.aggregate") { - errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "DiscoveryType is not valid.")); - } else { - cds_update->cluster_type = XdsApi::CdsUpdate::ClusterType::AGGREGATE; - // Retrieve aggregate clusters. - const google_protobuf_Any* typed_config = - envoy_config_cluster_v3_Cluster_CustomClusterType_typed_config( - custom_cluster_type); - const upb_strview aggregate_cluster_config_upb_strview = - google_protobuf_Any_value(typed_config); - const envoy_extensions_clusters_aggregate_v3_ClusterConfig* - aggregate_cluster_config = - envoy_extensions_clusters_aggregate_v3_ClusterConfig_parse( - aggregate_cluster_config_upb_strview.data, - aggregate_cluster_config_upb_strview.size, context.arena); - if (aggregate_cluster_config == nullptr) { - errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Can't parse aggregate cluster.")); - } else { - size_t size; - const upb_strview* clusters = - envoy_extensions_clusters_aggregate_v3_ClusterConfig_clusters( - aggregate_cluster_config, &size); - for (size_t i = 0; i < size; ++i) { - const upb_strview cluster = clusters[i]; - cds_update->prioritized_cluster_names.emplace_back( - UpbStringToStdString(cluster)); - } - } - } - } - } - // Check the LB policy. - if (envoy_config_cluster_v3_Cluster_lb_policy(cluster) == - envoy_config_cluster_v3_Cluster_ROUND_ROBIN) { - cds_update->lb_policy = "ROUND_ROBIN"; - } else if (envoy_config_cluster_v3_Cluster_lb_policy(cluster) == - envoy_config_cluster_v3_Cluster_RING_HASH) { - cds_update->lb_policy = "RING_HASH"; - // Record ring hash lb config - auto* ring_hash_config = - envoy_config_cluster_v3_Cluster_ring_hash_lb_config(cluster); - if (ring_hash_config != nullptr) { - const google_protobuf_UInt64Value* max_ring_size = - envoy_config_cluster_v3_Cluster_RingHashLbConfig_maximum_ring_size( - ring_hash_config); - if (max_ring_size != nullptr) { - cds_update->max_ring_size = - google_protobuf_UInt64Value_value(max_ring_size); - if (cds_update->max_ring_size > 8388608 || - cds_update->max_ring_size == 0) { - errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "max_ring_size is not in the range of 1 to 8388608.")); - } - } - const google_protobuf_UInt64Value* min_ring_size = - envoy_config_cluster_v3_Cluster_RingHashLbConfig_minimum_ring_size( - ring_hash_config); - if (min_ring_size != nullptr) { - cds_update->min_ring_size = - google_protobuf_UInt64Value_value(min_ring_size); - if (cds_update->min_ring_size > 8388608 || - cds_update->min_ring_size == 0) { - errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "min_ring_size is not in the range of 1 to 8388608.")); - } - if (cds_update->min_ring_size > cds_update->max_ring_size) { - errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "min_ring_size cannot be greater than max_ring_size.")); - } - } - if (envoy_config_cluster_v3_Cluster_RingHashLbConfig_hash_function( - ring_hash_config) != - envoy_config_cluster_v3_Cluster_RingHashLbConfig_XX_HASH) { - errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "ring hash lb config has invalid hash function.")); - } - } - } else { - errors.push_back( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("LB policy is not supported.")); - } - auto* transport_socket = - envoy_config_cluster_v3_Cluster_transport_socket(cluster); - if (transport_socket != nullptr) { - grpc_error_handle error = UpstreamTlsContextParse( - context, transport_socket, &cds_update->common_tls_context); - if (error != GRPC_ERROR_NONE) { - errors.push_back( - grpc_error_add_child(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Error parsing security configuration"), - error)); - } - } - // Record LRS server name (if any). - const envoy_config_core_v3_ConfigSource* lrs_server = - envoy_config_cluster_v3_Cluster_lrs_server(cluster); - if (lrs_server != nullptr) { - if (!envoy_config_core_v3_ConfigSource_has_self(lrs_server)) { - errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - ": LRS ConfigSource is not self.")); - } - cds_update->lrs_load_reporting_server_name.emplace(""); - } - // The Cluster resource encodes the circuit breaking parameters in a list of - // Thresholds messages, where each message specifies the parameters for a - // particular RoutingPriority. we will look only at the first entry in the - // list for priority DEFAULT and default to 1024 if not found. - if (envoy_config_cluster_v3_Cluster_has_circuit_breakers(cluster)) { - const envoy_config_cluster_v3_CircuitBreakers* circuit_breakers = - envoy_config_cluster_v3_Cluster_circuit_breakers(cluster); - size_t num_thresholds; - const envoy_config_cluster_v3_CircuitBreakers_Thresholds* const* - thresholds = envoy_config_cluster_v3_CircuitBreakers_thresholds( - circuit_breakers, &num_thresholds); - for (size_t i = 0; i < num_thresholds; ++i) { - const auto* threshold = thresholds[i]; - if (envoy_config_cluster_v3_CircuitBreakers_Thresholds_priority( - threshold) == envoy_config_core_v3_DEFAULT) { - const google_protobuf_UInt32Value* max_requests = - envoy_config_cluster_v3_CircuitBreakers_Thresholds_max_requests( - threshold); - if (max_requests != nullptr) { - cds_update->max_concurrent_requests = - google_protobuf_UInt32Value_value(max_requests); - } - break; - } - } - } - return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing CDS resource", &errors); -} - -grpc_error_handle ServerAddressParseAndAppend( - const envoy_config_endpoint_v3_LbEndpoint* lb_endpoint, - ServerAddressList* list) { - // If health_status is not HEALTHY or UNKNOWN, skip this endpoint. - const int32_t health_status = - envoy_config_endpoint_v3_LbEndpoint_health_status(lb_endpoint); - if (health_status != envoy_config_core_v3_UNKNOWN && - health_status != envoy_config_core_v3_HEALTHY) { - return GRPC_ERROR_NONE; - } - // Find the ip:port. - const envoy_config_endpoint_v3_Endpoint* endpoint = - envoy_config_endpoint_v3_LbEndpoint_endpoint(lb_endpoint); - const envoy_config_core_v3_Address* address = - envoy_config_endpoint_v3_Endpoint_address(endpoint); - const envoy_config_core_v3_SocketAddress* socket_address = - envoy_config_core_v3_Address_socket_address(address); - std::string address_str = UpbStringToStdString( - envoy_config_core_v3_SocketAddress_address(socket_address)); - uint32_t port = envoy_config_core_v3_SocketAddress_port_value(socket_address); - if (GPR_UNLIKELY(port >> 16) != 0) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Invalid port."); - } - // Find load_balancing_weight for the endpoint. - const google_protobuf_UInt32Value* load_balancing_weight = - envoy_config_endpoint_v3_LbEndpoint_load_balancing_weight(lb_endpoint); - const int32_t weight = - load_balancing_weight != nullptr - ? google_protobuf_UInt32Value_value(load_balancing_weight) - : 500; - if (weight == 0) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "Invalid endpoint weight of 0."); - } - // Populate grpc_resolved_address. - grpc_resolved_address addr; - grpc_error_handle error = - grpc_string_to_sockaddr(&addr, address_str.c_str(), port); - if (error != GRPC_ERROR_NONE) return error; - // Append the address to the list. - std::map> - attributes; - attributes[ServerAddressWeightAttribute::kServerAddressWeightAttributeKey] = - absl::make_unique(weight); - list->emplace_back(addr, nullptr, std::move(attributes)); - return GRPC_ERROR_NONE; -} - -grpc_error_handle LocalityParse( - const envoy_config_endpoint_v3_LocalityLbEndpoints* locality_lb_endpoints, - XdsApi::EdsUpdate::Priority::Locality* output_locality, size_t* priority) { - // Parse LB weight. - const google_protobuf_UInt32Value* lb_weight = - envoy_config_endpoint_v3_LocalityLbEndpoints_load_balancing_weight( - locality_lb_endpoints); - // If LB weight is not specified, it means this locality is assigned no load. - // TODO(juanlishen): When we support CDS to configure the inter-locality - // policy, we should change the LB weight handling. - output_locality->lb_weight = - lb_weight != nullptr ? google_protobuf_UInt32Value_value(lb_weight) : 0; - if (output_locality->lb_weight == 0) return GRPC_ERROR_NONE; - // Parse locality name. - const envoy_config_core_v3_Locality* locality = - envoy_config_endpoint_v3_LocalityLbEndpoints_locality( - locality_lb_endpoints); - if (locality == nullptr) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Empty locality."); - } - std::string region = - UpbStringToStdString(envoy_config_core_v3_Locality_region(locality)); - std::string zone = - UpbStringToStdString(envoy_config_core_v3_Locality_region(locality)); - std::string sub_zone = - UpbStringToStdString(envoy_config_core_v3_Locality_sub_zone(locality)); - output_locality->name = MakeRefCounted( - std::move(region), std::move(zone), std::move(sub_zone)); - // Parse the addresses. - size_t size; - const envoy_config_endpoint_v3_LbEndpoint* const* lb_endpoints = - envoy_config_endpoint_v3_LocalityLbEndpoints_lb_endpoints( - locality_lb_endpoints, &size); - for (size_t i = 0; i < size; ++i) { - grpc_error_handle error = ServerAddressParseAndAppend( - lb_endpoints[i], &output_locality->endpoints); - if (error != GRPC_ERROR_NONE) return error; - } - // Parse the priority. - *priority = envoy_config_endpoint_v3_LocalityLbEndpoints_priority( - locality_lb_endpoints); - return GRPC_ERROR_NONE; -} - -grpc_error_handle DropParseAndAppend( - const envoy_config_endpoint_v3_ClusterLoadAssignment_Policy_DropOverload* - drop_overload, - XdsApi::EdsUpdate::DropConfig* drop_config) { - // Get the category. - std::string category = UpbStringToStdString( - envoy_config_endpoint_v3_ClusterLoadAssignment_Policy_DropOverload_category( - drop_overload)); - if (category.empty()) { - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Empty drop category name"); - } - // Get the drop rate (per million). - const envoy_type_v3_FractionalPercent* drop_percentage = - envoy_config_endpoint_v3_ClusterLoadAssignment_Policy_DropOverload_drop_percentage( - drop_overload); - uint32_t numerator = - envoy_type_v3_FractionalPercent_numerator(drop_percentage); - const auto denominator = - static_cast( - envoy_type_v3_FractionalPercent_denominator(drop_percentage)); - // Normalize to million. - switch (denominator) { - case envoy_type_v3_FractionalPercent_HUNDRED: - numerator *= 10000; - break; - case envoy_type_v3_FractionalPercent_TEN_THOUSAND: - numerator *= 100; - break; - case envoy_type_v3_FractionalPercent_MILLION: - break; - default: - return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Unknown denominator type"); - } - // Cap numerator to 1000000. - numerator = std::min(numerator, 1000000u); - drop_config->AddCategory(std::move(category), numerator); - return GRPC_ERROR_NONE; -} - -grpc_error_handle EdsResourceParse( - const EncodingContext& /*context*/, - const envoy_config_endpoint_v3_ClusterLoadAssignment* - cluster_load_assignment, - bool /*is_v2*/, XdsApi::EdsUpdate* eds_update) { - std::vector errors; - // Get the endpoints. - size_t locality_size; - const envoy_config_endpoint_v3_LocalityLbEndpoints* const* endpoints = - envoy_config_endpoint_v3_ClusterLoadAssignment_endpoints( - cluster_load_assignment, &locality_size); - for (size_t j = 0; j < locality_size; ++j) { - size_t priority; - XdsApi::EdsUpdate::Priority::Locality locality; - grpc_error_handle error = LocalityParse(endpoints[j], &locality, &priority); - if (error != GRPC_ERROR_NONE) { - errors.push_back(error); - continue; - } - // Filter out locality with weight 0. - if (locality.lb_weight == 0) continue; - // Make sure prorities is big enough. Note that they might not - // arrive in priority order. - while (eds_update->priorities.size() < priority + 1) { - eds_update->priorities.emplace_back(); - } - eds_update->priorities[priority].localities.emplace(locality.name.get(), - std::move(locality)); - } - for (const auto& priority : eds_update->priorities) { - if (priority.localities.empty()) { - errors.push_back( - GRPC_ERROR_CREATE_FROM_STATIC_STRING("sparse priority list")); - } - } - // Get the drop config. - eds_update->drop_config = MakeRefCounted(); - const envoy_config_endpoint_v3_ClusterLoadAssignment_Policy* policy = - envoy_config_endpoint_v3_ClusterLoadAssignment_policy( - cluster_load_assignment); - if (policy != nullptr) { - size_t drop_size; - const envoy_config_endpoint_v3_ClusterLoadAssignment_Policy_DropOverload* const* - drop_overload = - envoy_config_endpoint_v3_ClusterLoadAssignment_Policy_drop_overloads( - policy, &drop_size); - for (size_t j = 0; j < drop_size; ++j) { - grpc_error_handle error = - DropParseAndAppend(drop_overload[j], eds_update->drop_config.get()); - if (error != GRPC_ERROR_NONE) { - errors.push_back( - grpc_error_add_child(GRPC_ERROR_CREATE_FROM_STATIC_STRING( - "drop config validation error"), - error)); - } - } - } - return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing EDS resource", &errors); -} - -class ListenerResourceType : public XdsResourceType { - public: - struct ListenerData : public ResourceData { - XdsApi::LdsUpdate resource; - }; - - absl::string_view type_url() const override { return XdsApi::kLdsTypeUrl; } - absl::string_view v2_type_url() const override { return kLdsV2TypeUrl; } - - absl::StatusOr Decode(const EncodingContext& context, - absl::string_view serialized_resource, - bool is_v2) const override { - // Parse serialized proto. - auto* resource = envoy_config_listener_v3_Listener_parse( - serialized_resource.data(), serialized_resource.size(), context.arena); - if (resource == nullptr) { - return absl::InvalidArgumentError("Can't parse Listener resource."); - } - MaybeLogListener(context, resource); - // Validate resource. - DecodeResult result; - result.name = - UpbStringToStdString(envoy_config_listener_v3_Listener_name(resource)); - auto listener_data = absl::make_unique(); - grpc_error_handle error = - LdsResourceParse(context, resource, is_v2, &listener_data->resource); - if (error != GRPC_ERROR_NONE) { - result.resource = - absl::InvalidArgumentError(grpc_error_std_string(error)); - GRPC_ERROR_UNREF(error); - } else { - result.resource = std::move(listener_data); - } - return std::move(result); - } -}; - -class RouteConfigResourceType : public XdsResourceType { - public: - struct RouteConfigData : public ResourceData { - XdsApi::RdsUpdate resource; - }; - - absl::string_view type_url() const override { return XdsApi::kRdsTypeUrl; } - absl::string_view v2_type_url() const override { return kRdsV2TypeUrl; } - - absl::StatusOr Decode(const EncodingContext& context, - absl::string_view serialized_resource, - bool is_v2) const override { - // Parse serialized proto. - auto* resource = envoy_config_route_v3_RouteConfiguration_parse( - serialized_resource.data(), serialized_resource.size(), context.arena); - if (resource == nullptr) { - return absl::InvalidArgumentError("Can't parse Listener resource."); - } - MaybeLogRouteConfiguration(context, resource); - // Validate resource. - DecodeResult result; - result.name = UpbStringToStdString( - envoy_config_route_v3_RouteConfiguration_name(resource)); - auto route_config_data = absl::make_unique(); - grpc_error_handle error = RouteConfigParse(context, resource, is_v2, - &route_config_data->resource); - if (error != GRPC_ERROR_NONE) { - result.resource = - absl::InvalidArgumentError(grpc_error_std_string(error)); - GRPC_ERROR_UNREF(error); - } else { - result.resource = std::move(route_config_data); - } - return std::move(result); - } -}; - -class ClusterResourceType : public XdsResourceType { - public: - struct ClusterData : public ResourceData { - XdsApi::CdsUpdate resource; - }; - - absl::string_view type_url() const override { return XdsApi::kCdsTypeUrl; } - absl::string_view v2_type_url() const override { return kCdsV2TypeUrl; } - - absl::StatusOr Decode(const EncodingContext& context, - absl::string_view serialized_resource, - bool is_v2) const override { - // Parse serialized proto. - auto* resource = envoy_config_cluster_v3_Cluster_parse( - serialized_resource.data(), serialized_resource.size(), context.arena); - if (resource == nullptr) { - return absl::InvalidArgumentError("Can't parse Listener resource."); - } - MaybeLogCluster(context, resource); - // Validate resource. - DecodeResult result; - result.name = - UpbStringToStdString(envoy_config_cluster_v3_Cluster_name(resource)); - auto cluster_data = absl::make_unique(); - grpc_error_handle error = - CdsResourceParse(context, resource, is_v2, &cluster_data->resource); - if (error != GRPC_ERROR_NONE) { - result.resource = - absl::InvalidArgumentError(grpc_error_std_string(error)); - GRPC_ERROR_UNREF(error); - } else { - result.resource = std::move(cluster_data); - } - return std::move(result); - } -}; - -class EndpointResourceType : public XdsResourceType { - public: - struct EndpointData : public ResourceData { - XdsApi::EdsUpdate resource; - }; - - absl::string_view type_url() const override { return XdsApi::kEdsTypeUrl; } - absl::string_view v2_type_url() const override { return kEdsV2TypeUrl; } - - absl::StatusOr Decode(const EncodingContext& context, - absl::string_view serialized_resource, - bool is_v2) const override { - // Parse serialized proto. - auto* resource = envoy_config_endpoint_v3_ClusterLoadAssignment_parse( - serialized_resource.data(), serialized_resource.size(), context.arena); - if (resource == nullptr) { - return absl::InvalidArgumentError("Can't parse Listener resource."); - } - MaybeLogClusterLoadAssignment(context, resource); - // Validate resource. - DecodeResult result; - result.name = UpbStringToStdString( - envoy_config_endpoint_v3_ClusterLoadAssignment_cluster_name(resource)); - auto endpoint_data = absl::make_unique(); - grpc_error_handle error = - EdsResourceParse(context, resource, is_v2, &endpoint_data->resource); - if (error != GRPC_ERROR_NONE) { - result.resource = - absl::InvalidArgumentError(grpc_error_std_string(error)); - GRPC_ERROR_UNREF(error); - } else { - result.resource = std::move(endpoint_data); - } - return std::move(result); - } -}; - grpc_error_handle AdsResourceParse( - const EncodingContext& context, XdsResourceType* type, size_t idx, + const XdsEncodingContext& context, XdsResourceType* type, size_t idx, const google_protobuf_Any* resource_any, const std::map>& @@ -3675,12 +671,12 @@ XdsApi::AdsParseResult XdsApi::ParseAdsResponse( subscribed_eds_service_names) { AdsParseResult result; upb::Arena arena; - const EncodingContext context = {client_, - tracer_, - symtab_.ptr(), - arena.ptr(), - server.ShouldUseV3(), - certificate_provider_definition_map_}; + const XdsEncodingContext context = {client_, + tracer_, + symtab_.ptr(), + arena.ptr(), + server.ShouldUseV3(), + certificate_provider_definition_map_}; // Decode the response. const envoy_service_discovery_v3_DiscoveryResponse* response = envoy_service_discovery_v3_DiscoveryResponse_parse( @@ -3713,7 +709,7 @@ XdsApi::AdsParseResult XdsApi::ParseAdsResponse( // for templating and conditionals. grpc_error_handle parse_error = GRPC_ERROR_NONE; if (IsLds(result.type_url)) { - ListenerResourceType resource_type; + XdsListenerResourceType resource_type; auto& update_map = result.lds_update_map; parse_error = AdsResourceParse( context, &resource_type, i, resources[i], subscribed_listener_names, @@ -3721,13 +717,14 @@ XdsApi::AdsParseResult XdsApi::ParseAdsResponse( XdsApi::ResourceName resource_name, std::unique_ptr resource, std::string serialized_resource) { - return AddResult( + return AddResult( &update_map, resource_name_string, std::move(resource_name), std::move(resource), std::move(serialized_resource)); }, &result.resource_names_failed); } else if (IsRds(result.type_url)) { - RouteConfigResourceType resource_type; + XdsRouteConfigResourceType resource_type; auto& update_map = result.rds_update_map; parse_error = AdsResourceParse( context, &resource_type, i, resources[i], @@ -3737,13 +734,13 @@ XdsApi::AdsParseResult XdsApi::ParseAdsResponse( std::unique_ptr resource, std::string serialized_resource) { return AddResult( + XdsRouteConfigResourceType::RouteConfigData>( &update_map, resource_name_string, std::move(resource_name), std::move(resource), std::move(serialized_resource)); }, &result.resource_names_failed); } else if (IsCds(result.type_url)) { - ClusterResourceType resource_type; + XdsClusterResourceType resource_type; auto& update_map = result.cds_update_map; parse_error = AdsResourceParse( context, &resource_type, i, resources[i], subscribed_cluster_names, @@ -3751,13 +748,13 @@ XdsApi::AdsParseResult XdsApi::ParseAdsResponse( XdsApi::ResourceName resource_name, std::unique_ptr resource, std::string serialized_resource) { - return AddResult( + return AddResult( &update_map, resource_name_string, std::move(resource_name), std::move(resource), std::move(serialized_resource)); }, &result.resource_names_failed); } else if (IsEds(result.type_url)) { - EndpointResourceType resource_type; + XdsEndpointResourceType resource_type; auto& update_map = result.eds_update_map; parse_error = AdsResourceParse( context, &resource_type, i, resources[i], @@ -3766,7 +763,8 @@ XdsApi::AdsParseResult XdsApi::ParseAdsResponse( XdsApi::ResourceName resource_name, std::unique_ptr resource, std::string serialized_resource) { - return AddResult( + return AddResult( &update_map, resource_name_string, std::move(resource_name), std::move(resource), std::move(serialized_resource)); }, @@ -3782,7 +780,7 @@ XdsApi::AdsParseResult XdsApi::ParseAdsResponse( namespace { void MaybeLogLrsRequest( - const EncodingContext& context, + const XdsEncodingContext& context, const envoy_service_load_stats_v3_LoadStatsRequest* request) { if (GRPC_TRACE_FLAG_ENABLED(*context.tracer) && gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) { @@ -3796,7 +794,7 @@ void MaybeLogLrsRequest( } grpc_slice SerializeLrsRequest( - const EncodingContext& context, + const XdsEncodingContext& context, const envoy_service_load_stats_v3_LoadStatsRequest* request) { size_t output_length; char* output = envoy_service_load_stats_v3_LoadStatsRequest_serialize( @@ -3809,12 +807,12 @@ grpc_slice SerializeLrsRequest( grpc_slice XdsApi::CreateLrsInitialRequest( const XdsBootstrap::XdsServer& server) { upb::Arena arena; - const EncodingContext context = {client_, - tracer_, - symtab_.ptr(), - arena.ptr(), - server.ShouldUseV3(), - certificate_provider_definition_map_}; + const XdsEncodingContext context = {client_, + tracer_, + symtab_.ptr(), + arena.ptr(), + server.ShouldUseV3(), + certificate_provider_definition_map_}; // Create a request. envoy_service_load_stats_v3_LoadStatsRequest* request = envoy_service_load_stats_v3_LoadStatsRequest_new(arena.ptr()); @@ -3834,7 +832,7 @@ grpc_slice XdsApi::CreateLrsInitialRequest( namespace { void LocalityStatsPopulate( - const EncodingContext& context, + const XdsEncodingContext& context, envoy_config_endpoint_v3_UpstreamLocalityStats* output, const XdsLocalityName& locality_name, const XdsClusterLocalityStats::Snapshot& snapshot) { @@ -3884,7 +882,7 @@ void LocalityStatsPopulate( grpc_slice XdsApi::CreateLrsRequest( ClusterLoadReportMap cluster_load_report_map) { upb::Arena arena; - const EncodingContext context = { + const XdsEncodingContext context = { client_, tracer_, symtab_.ptr(), arena.ptr(), false, certificate_provider_definition_map_}; // Create a request. @@ -3988,8 +986,8 @@ grpc_error_handle XdsApi::ParseLrsResponse( namespace { -google_protobuf_Timestamp* GrpcMillisToTimestamp(const EncodingContext& context, - grpc_millis value) { +google_protobuf_Timestamp* GrpcMillisToTimestamp( + const XdsEncodingContext& context, grpc_millis value) { google_protobuf_Timestamp* timestamp = google_protobuf_Timestamp_new(context.arena); gpr_timespec timespec = grpc_millis_to_timespec(value, GPR_CLOCK_REALTIME); @@ -4008,7 +1006,7 @@ std::string XdsApi::AssembleClientConfig( // Fill-in the node information auto* node = envoy_service_status_v3_ClientConfig_mutable_node(client_config, arena.ptr()); - const EncodingContext context = { + const XdsEncodingContext context = { client_, tracer_, symtab_.ptr(), arena.ptr(), true, certificate_provider_definition_map_}; PopulateNode(context, node_, build_version_, user_agent_name_, diff --git a/src/core/ext/xds/xds_api.h b/src/core/ext/xds/xds_api.h index f25a21f8f83..fee90af02cb 100644 --- a/src/core/ext/xds/xds_api.h +++ b/src/core/ext/xds/xds_api.h @@ -1,20 +1,18 @@ -/* - * - * Copyright 2018 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ +// +// Copyright 2018 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// #ifndef GRPC_CORE_EXT_XDS_XDS_API_H #define GRPC_CORE_EXT_XDS_XDS_API_H @@ -37,14 +35,16 @@ #include "src/core/ext/filters/client_channel/server_address.h" #include "src/core/ext/xds/xds_bootstrap.h" #include "src/core/ext/xds/xds_client_stats.h" +#include "src/core/ext/xds/xds_cluster.h" +#include "src/core/ext/xds/xds_endpoint.h" #include "src/core/ext/xds/xds_http_filters.h" +#include "src/core/ext/xds/xds_listener.h" +#include "src/core/ext/xds/xds_route_config.h" #include "src/core/lib/channel/status_util.h" #include "src/core/lib/matchers/matchers.h" namespace grpc_core { -bool XdsRbacEnabled(); - class XdsClient; class XdsApi { @@ -54,362 +54,6 @@ class XdsApi { static const char* kCdsTypeUrl; static const char* kEdsTypeUrl; - struct Duration { - Duration() {} - int64_t seconds = 0; - int32_t nanos = 0; - bool operator==(const Duration& other) const { - return seconds == other.seconds && nanos == other.nanos; - } - std::string ToString() const { - return absl::StrFormat("Duration seconds: %ld, nanos %d", seconds, nanos); - } - }; - - using TypedPerFilterConfig = - std::map; - - struct RetryPolicy { - internal::StatusCodeSet retry_on; - uint32_t num_retries; - - struct RetryBackOff { - Duration base_interval; - Duration max_interval; - - bool operator==(const RetryBackOff& other) const { - return base_interval == other.base_interval && - max_interval == other.max_interval; - } - std::string ToString() const; - }; - RetryBackOff retry_back_off; - - bool operator==(const RetryPolicy& other) const { - return (retry_on == other.retry_on && num_retries == other.num_retries && - retry_back_off == other.retry_back_off); - } - std::string ToString() const; - }; - - // TODO(donnadionne): When we can use absl::variant<>, consider using that - // for: PathMatcher, HeaderMatcher, cluster_name and weighted_clusters - struct Route { - // Matchers for this route. - struct Matchers { - StringMatcher path_matcher; - std::vector header_matchers; - absl::optional fraction_per_million; - - bool operator==(const Matchers& other) const { - return path_matcher == other.path_matcher && - header_matchers == other.header_matchers && - fraction_per_million == other.fraction_per_million; - } - std::string ToString() const; - }; - - Matchers matchers; - - struct UnknownAction { - bool operator==(const UnknownAction& /* other */) const { return true; } - }; - - struct RouteAction { - struct HashPolicy { - enum Type { HEADER, CHANNEL_ID }; - Type type; - bool terminal = false; - // Fields used for type HEADER. - std::string header_name; - std::unique_ptr regex = nullptr; - std::string regex_substitution; - - HashPolicy() {} - - // Copyable. - HashPolicy(const HashPolicy& other); - HashPolicy& operator=(const HashPolicy& other); - - // Moveable. - HashPolicy(HashPolicy&& other) noexcept; - HashPolicy& operator=(HashPolicy&& other) noexcept; - - bool operator==(const HashPolicy& other) const; - std::string ToString() const; - }; - - struct ClusterWeight { - std::string name; - uint32_t weight; - TypedPerFilterConfig typed_per_filter_config; - - bool operator==(const ClusterWeight& other) const { - return name == other.name && weight == other.weight && - typed_per_filter_config == other.typed_per_filter_config; - } - std::string ToString() const; - }; - - std::vector hash_policies; - absl::optional retry_policy; - - // Action for this route. - // TODO(roth): When we can use absl::variant<>, consider using that - // here, to enforce the fact that only one of the two fields can be set. - std::string cluster_name; - std::vector weighted_clusters; - // Storing the timeout duration from route action: - // RouteAction.max_stream_duration.grpc_timeout_header_max or - // RouteAction.max_stream_duration.max_stream_duration if the former is - // not set. - absl::optional max_stream_duration; - - bool operator==(const RouteAction& other) const { - return hash_policies == other.hash_policies && - retry_policy == other.retry_policy && - cluster_name == other.cluster_name && - weighted_clusters == other.weighted_clusters && - max_stream_duration == other.max_stream_duration; - } - std::string ToString() const; - }; - - struct NonForwardingAction { - bool operator==(const NonForwardingAction& /* other */) const { - return true; - } - }; - - absl::variant action; - TypedPerFilterConfig typed_per_filter_config; - - bool operator==(const Route& other) const { - return matchers == other.matchers && action == other.action && - typed_per_filter_config == other.typed_per_filter_config; - } - std::string ToString() const; - }; - - struct RdsUpdate { - struct VirtualHost { - std::vector domains; - std::vector routes; - TypedPerFilterConfig typed_per_filter_config; - - bool operator==(const VirtualHost& other) const { - return domains == other.domains && routes == other.routes && - typed_per_filter_config == other.typed_per_filter_config; - } - }; - - std::vector virtual_hosts; - - bool operator==(const RdsUpdate& other) const { - return virtual_hosts == other.virtual_hosts; - } - std::string ToString() const; - }; - - struct CommonTlsContext { - struct CertificateProviderPluginInstance { - std::string instance_name; - std::string certificate_name; - - bool operator==(const CertificateProviderPluginInstance& other) const { - return instance_name == other.instance_name && - certificate_name == other.certificate_name; - } - - std::string ToString() const; - bool Empty() const; - }; - - struct CertificateValidationContext { - CertificateProviderPluginInstance ca_certificate_provider_instance; - std::vector match_subject_alt_names; - - bool operator==(const CertificateValidationContext& other) const { - return ca_certificate_provider_instance == - other.ca_certificate_provider_instance && - match_subject_alt_names == other.match_subject_alt_names; - } - - std::string ToString() const; - bool Empty() const; - }; - - CertificateValidationContext certificate_validation_context; - CertificateProviderPluginInstance tls_certificate_provider_instance; - - bool operator==(const CommonTlsContext& other) const { - return certificate_validation_context == - other.certificate_validation_context && - tls_certificate_provider_instance == - other.tls_certificate_provider_instance; - } - - std::string ToString() const; - bool Empty() const; - }; - - struct DownstreamTlsContext { - CommonTlsContext common_tls_context; - bool require_client_certificate = false; - - bool operator==(const DownstreamTlsContext& other) const { - return common_tls_context == other.common_tls_context && - require_client_certificate == other.require_client_certificate; - } - - std::string ToString() const; - bool Empty() const; - }; - - // TODO(roth): When we can use absl::variant<>, consider using that - // here, to enforce the fact that only one of the two fields can be set. - struct LdsUpdate { - enum class ListenerType { - kTcpListener = 0, - kHttpApiListener, - } type; - - struct HttpConnectionManager { - // The name to use in the RDS request. - std::string route_config_name; - // Storing the Http Connection Manager Common Http Protocol Option - // max_stream_duration - Duration http_max_stream_duration; - // The RouteConfiguration to use for this listener. - // Present only if it is inlined in the LDS response. - absl::optional rds_update; - - struct HttpFilter { - std::string name; - XdsHttpFilterImpl::FilterConfig config; - - bool operator==(const HttpFilter& other) const { - return name == other.name && config == other.config; - } - - std::string ToString() const; - }; - std::vector http_filters; - - bool operator==(const HttpConnectionManager& other) const { - return route_config_name == other.route_config_name && - http_max_stream_duration == other.http_max_stream_duration && - rds_update == other.rds_update && - http_filters == other.http_filters; - } - - std::string ToString() const; - }; - - // Populated for type=kHttpApiListener. - HttpConnectionManager http_connection_manager; - - // Populated for type=kTcpListener. - // host:port listening_address set when type is kTcpListener - std::string address; - - struct FilterChainData { - DownstreamTlsContext downstream_tls_context; - // This is in principle the filter list. - // We currently require exactly one filter, which is the HCM. - HttpConnectionManager http_connection_manager; - - bool operator==(const FilterChainData& other) const { - return downstream_tls_context == other.downstream_tls_context && - http_connection_manager == other.http_connection_manager; - } - - std::string ToString() const; - }; - - // A multi-level map used to determine which filter chain to use for a given - // incoming connection. Determining the right filter chain for a given - // connection checks the following properties, in order: - // - destination port (never matched, so not present in map) - // - destination IP address - // - server name (never matched, so not present in map) - // - transport protocol (allows only "raw_buffer" or unset, prefers the - // former, so only one of those two types is present in map) - // - application protocol (never matched, so not present in map) - // - connection source type (any, local or external) - // - source IP address - // - source port - // https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/listener/v3/listener_components.proto#config-listener-v3-filterchainmatch - // for more details - struct FilterChainMap { - struct FilterChainDataSharedPtr { - std::shared_ptr data; - bool operator==(const FilterChainDataSharedPtr& other) const { - return *data == *other.data; - } - }; - struct CidrRange { - grpc_resolved_address address; - uint32_t prefix_len; - - bool operator==(const CidrRange& other) const { - return memcmp(&address, &other.address, sizeof(address)) == 0 && - prefix_len == other.prefix_len; - } - - std::string ToString() const; - }; - using SourcePortsMap = std::map; - struct SourceIp { - absl::optional prefix_range; - SourcePortsMap ports_map; - - bool operator==(const SourceIp& other) const { - return prefix_range == other.prefix_range && - ports_map == other.ports_map; - } - }; - using SourceIpVector = std::vector; - enum class ConnectionSourceType { - kAny = 0, - kSameIpOrLoopback, - kExternal - }; - using ConnectionSourceTypesArray = std::array; - struct DestinationIp { - absl::optional prefix_range; - // We always fail match on server name, so those filter chains are not - // included here. - ConnectionSourceTypesArray source_types_array; - - bool operator==(const DestinationIp& other) const { - return prefix_range == other.prefix_range && - source_types_array == other.source_types_array; - } - }; - // We always fail match on destination ports map - using DestinationIpVector = std::vector; - DestinationIpVector destination_ip_vector; - - bool operator==(const FilterChainMap& other) const { - return destination_ip_vector == other.destination_ip_vector; - } - - std::string ToString() const; - } filter_chain_map; - - absl::optional default_filter_chain; - - bool operator==(const LdsUpdate& other) const { - return http_connection_manager == other.http_connection_manager && - address == other.address && - filter_chain_map == other.filter_chain_map && - default_filter_chain == other.default_filter_chain; - } - - std::string ToString() const; - }; - struct ResourceName { std::string authority; std::string id; @@ -422,162 +66,27 @@ class XdsApi { }; struct LdsResourceData { - LdsUpdate resource; + XdsListenerResource resource; std::string serialized_proto; }; - using LdsUpdateMap = std::map; struct RdsResourceData { - RdsUpdate resource; + XdsRouteConfigResource resource; std::string serialized_proto; }; - using RdsUpdateMap = std::map; - struct CdsUpdate { - enum ClusterType { EDS, LOGICAL_DNS, AGGREGATE }; - ClusterType cluster_type; - // For cluster type EDS. - // The name to use in the EDS request. - // If empty, the cluster name will be used. - std::string eds_service_name; - // For cluster type LOGICAL_DNS. - // The hostname to lookup in DNS. - std::string dns_hostname; - // For cluster type AGGREGATE. - // The prioritized list of cluster names. - std::vector prioritized_cluster_names; - - // Tls Context used by clients - CommonTlsContext common_tls_context; - - // The LRS server to use for load reporting. - // If not set, load reporting will be disabled. - // If set to the empty string, will use the same server we obtained the CDS - // data from. - absl::optional lrs_load_reporting_server_name; - - // The LB policy to use (e.g., "ROUND_ROBIN" or "RING_HASH"). - std::string lb_policy; - // Used for RING_HASH LB policy only. - uint64_t min_ring_size = 1024; - uint64_t max_ring_size = 8388608; - // Maximum number of outstanding requests can be made to the upstream - // cluster. - uint32_t max_concurrent_requests = 1024; - - bool operator==(const CdsUpdate& other) const { - return cluster_type == other.cluster_type && - eds_service_name == other.eds_service_name && - dns_hostname == other.dns_hostname && - prioritized_cluster_names == other.prioritized_cluster_names && - common_tls_context == other.common_tls_context && - lrs_load_reporting_server_name == - other.lrs_load_reporting_server_name && - lb_policy == other.lb_policy && - min_ring_size == other.min_ring_size && - max_ring_size == other.max_ring_size && - max_concurrent_requests == other.max_concurrent_requests; - } - - std::string ToString() const; - }; - struct CdsResourceData { - CdsUpdate resource; + XdsClusterResource resource; std::string serialized_proto; }; - using CdsUpdateMap = std::map; - struct EdsUpdate { - struct Priority { - struct Locality { - RefCountedPtr name; - uint32_t lb_weight; - ServerAddressList endpoints; - - bool operator==(const Locality& other) const { - return *name == *other.name && lb_weight == other.lb_weight && - endpoints == other.endpoints; - } - bool operator!=(const Locality& other) const { - return !(*this == other); - } - std::string ToString() const; - }; - - std::map localities; - - bool operator==(const Priority& other) const; - std::string ToString() const; - }; - using PriorityList = absl::InlinedVector; - - // There are two phases of accessing this class's content: - // 1. to initialize in the control plane combiner; - // 2. to use in the data plane combiner. - // So no additional synchronization is needed. - class DropConfig : public RefCounted { - public: - struct DropCategory { - bool operator==(const DropCategory& other) const { - return name == other.name && - parts_per_million == other.parts_per_million; - } - - std::string name; - const uint32_t parts_per_million; - }; - - using DropCategoryList = absl::InlinedVector; - - void AddCategory(std::string name, uint32_t parts_per_million) { - drop_category_list_.emplace_back( - DropCategory{std::move(name), parts_per_million}); - if (parts_per_million == 1000000) drop_all_ = true; - } - - // The only method invoked from outside the WorkSerializer (used in - // the data plane). - bool ShouldDrop(const std::string** category_name) const; - - const DropCategoryList& drop_category_list() const { - return drop_category_list_; - } - - bool drop_all() const { return drop_all_; } - - bool operator==(const DropConfig& other) const { - return drop_category_list_ == other.drop_category_list_; - } - bool operator!=(const DropConfig& other) const { - return !(*this == other); - } - - std::string ToString() const; - - private: - DropCategoryList drop_category_list_; - bool drop_all_ = false; - }; - - PriorityList priorities; - RefCountedPtr drop_config; - - bool operator==(const EdsUpdate& other) const { - return priorities == other.priorities && - *drop_config == *other.drop_config; - } - std::string ToString() const; - }; - struct EdsResourceData { - EdsUpdate resource; + XdsEndpointResource resource; std::string serialized_proto; }; - using EdsUpdateMap = std::map; struct ClusterLoadReport { @@ -743,4 +252,4 @@ class XdsApi { } // namespace grpc_core -#endif /* GRPC_CORE_EXT_XDS_XDS_API_H */ +#endif // GRPC_CORE_EXT_XDS_XDS_API_H diff --git a/src/core/ext/xds/xds_client.cc b/src/core/ext/xds/xds_client.cc index e1c70a06576..6a862e7f4b2 100644 --- a/src/core/ext/xds/xds_client.cc +++ b/src/core/ext/xds/xds_client.cc @@ -994,7 +994,7 @@ void XdsClient::ChannelState::AdsCallState::AcceptLdsUpdateLocked( auto& lds_state = state_map_[XdsApi::kLdsTypeUrl]; for (auto& p : lds_update_map) { const XdsApi::ResourceName& name = p.first; - XdsApi::LdsUpdate& lds_update = p.second.resource; + XdsListenerResource& lds_update = p.second.resource; auto it = lds_state.subscribed_resources.find(name.authority); if (it != lds_state.subscribed_resources.end()) { auto res_it = it->second.find(name.id); @@ -1094,7 +1094,7 @@ void XdsClient::ChannelState::AdsCallState::AcceptRdsUpdateLocked( auto& rds_state = state_map_[XdsApi::kRdsTypeUrl]; for (auto& p : rds_update_map) { const XdsApi::ResourceName& name = p.first; - XdsApi::RdsUpdate& rds_update = p.second.resource; + XdsRouteConfigResource& rds_update = p.second.resource; auto it = rds_state.subscribed_resources.find(name.authority); if (it != rds_state.subscribed_resources.end()) { auto res_it = it->second.find(name.id); @@ -1151,7 +1151,7 @@ void XdsClient::ChannelState::AdsCallState::AcceptCdsUpdateLocked( auto& cds_state = state_map_[XdsApi::kCdsTypeUrl]; for (auto& p : cds_update_map) { const XdsApi::ResourceName& name = p.first; - XdsApi::CdsUpdate& cds_update = p.second.resource; + XdsClusterResource& cds_update = p.second.resource; auto it = cds_state.subscribed_resources.find(name.authority); if (it != cds_state.subscribed_resources.end()) { auto res_it = it->second.find(name.id); @@ -1245,7 +1245,7 @@ void XdsClient::ChannelState::AdsCallState::AcceptEdsUpdateLocked( auto& eds_state = state_map_[XdsApi::kEdsTypeUrl]; for (auto& p : eds_update_map) { const XdsApi::ResourceName& name = p.first; - XdsApi::EdsUpdate& eds_update = p.second.resource; + XdsEndpointResource& eds_update = p.second.resource; auto it = eds_state.subscribed_resources.find(name.authority); if (it != eds_state.subscribed_resources.end()) { auto res_it = it->second.find(name.id); diff --git a/src/core/ext/xds/xds_client.h b/src/core/ext/xds/xds_client.h index 431f7c471fe..53400697dfa 100644 --- a/src/core/ext/xds/xds_client.h +++ b/src/core/ext/xds/xds_client.h @@ -46,7 +46,7 @@ class XdsClient : public DualRefCounted { // Listener data watcher interface. Implemented by callers. class ListenerWatcherInterface : public RefCounted { public: - virtual void OnListenerChanged(XdsApi::LdsUpdate listener) + virtual void OnListenerChanged(XdsListenerResource listener) ABSL_EXCLUSIVE_LOCKS_REQUIRED(&work_serializer_) = 0; virtual void OnError(grpc_error_handle error) ABSL_EXCLUSIVE_LOCKS_REQUIRED(&work_serializer_) = 0; @@ -58,7 +58,7 @@ class XdsClient : public DualRefCounted { class RouteConfigWatcherInterface : public RefCounted { public: - virtual void OnRouteConfigChanged(XdsApi::RdsUpdate route_config) + virtual void OnRouteConfigChanged(XdsRouteConfigResource route_config) ABSL_EXCLUSIVE_LOCKS_REQUIRED(&work_serializer_) = 0; virtual void OnError(grpc_error_handle error) ABSL_EXCLUSIVE_LOCKS_REQUIRED(&work_serializer_) = 0; @@ -69,7 +69,7 @@ class XdsClient : public DualRefCounted { // Cluster data watcher interface. Implemented by callers. class ClusterWatcherInterface : public RefCounted { public: - virtual void OnClusterChanged(XdsApi::CdsUpdate cluster_data) + virtual void OnClusterChanged(XdsClusterResource cluster_data) ABSL_EXCLUSIVE_LOCKS_REQUIRED(&work_serializer_) = 0; virtual void OnError(grpc_error_handle error) ABSL_EXCLUSIVE_LOCKS_REQUIRED(&work_serializer_) = 0; @@ -80,7 +80,7 @@ class XdsClient : public DualRefCounted { // Endpoint data watcher interface. Implemented by callers. class EndpointWatcherInterface : public RefCounted { public: - virtual void OnEndpointChanged(XdsApi::EdsUpdate update) + virtual void OnEndpointChanged(XdsEndpointResource update) ABSL_EXCLUSIVE_LOCKS_REQUIRED(&work_serializer_) = 0; virtual void OnError(grpc_error_handle error) ABSL_EXCLUSIVE_LOCKS_REQUIRED(&work_serializer_) = 0; @@ -268,7 +268,7 @@ class XdsClient : public DualRefCounted { std::map> watchers; // The latest data seen from LDS. - absl::optional update; + absl::optional update; XdsApi::ResourceMetadata meta; }; @@ -277,7 +277,7 @@ class XdsClient : public DualRefCounted { RefCountedPtr> watchers; // The latest data seen from RDS. - absl::optional update; + absl::optional update; XdsApi::ResourceMetadata meta; }; @@ -285,7 +285,7 @@ class XdsClient : public DualRefCounted { std::map> watchers; // The latest data seen from CDS. - absl::optional update; + absl::optional update; XdsApi::ResourceMetadata meta; }; @@ -293,7 +293,7 @@ class XdsClient : public DualRefCounted { std::map> watchers; // The latest data seen from EDS. - absl::optional update; + absl::optional update; XdsApi::ResourceMetadata meta; }; diff --git a/src/core/ext/xds/xds_cluster.cc b/src/core/ext/xds/xds_cluster.cc new file mode 100644 index 00000000000..328cab95503 --- /dev/null +++ b/src/core/ext/xds/xds_cluster.cc @@ -0,0 +1,442 @@ +// +// Copyright 2018 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include + +#include "src/core/ext/xds/xds_cluster.h" + +#include "absl/container/inlined_vector.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" +#include "absl/strings/str_join.h" +#include "envoy/config/cluster/v3/circuit_breaker.upb.h" +#include "envoy/config/cluster/v3/cluster.upb.h" +#include "envoy/config/cluster/v3/cluster.upbdefs.h" +#include "envoy/config/core/v3/address.upb.h" +#include "envoy/config/core/v3/base.upb.h" +#include "envoy/config/core/v3/config_source.upb.h" +#include "envoy/config/endpoint/v3/endpoint.upb.h" +#include "envoy/config/endpoint/v3/endpoint_components.upb.h" +#include "envoy/extensions/clusters/aggregate/v3/cluster.upb.h" +#include "google/protobuf/any.upb.h" +#include "google/protobuf/wrappers.upb.h" + +#include + +#include "src/core/lib/gpr/env.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/gprpp/host_port.h" + +namespace grpc_core { + +// +// XdsClusterResource +// + +std::string XdsClusterResource::ToString() const { + absl::InlinedVector contents; + switch (cluster_type) { + case EDS: + contents.push_back("cluster_type=EDS"); + if (!eds_service_name.empty()) { + contents.push_back( + absl::StrFormat("eds_service_name=%s", eds_service_name)); + } + break; + case LOGICAL_DNS: + contents.push_back("cluster_type=LOGICAL_DNS"); + contents.push_back(absl::StrFormat("dns_hostname=%s", dns_hostname)); + break; + case AGGREGATE: + contents.push_back("cluster_type=AGGREGATE"); + contents.push_back( + absl::StrFormat("prioritized_cluster_names=[%s]", + absl::StrJoin(prioritized_cluster_names, ", "))); + } + if (!common_tls_context.Empty()) { + contents.push_back(absl::StrFormat("common_tls_context=%s", + common_tls_context.ToString())); + } + if (lrs_load_reporting_server_name.has_value()) { + contents.push_back(absl::StrFormat("lrs_load_reporting_server_name=%s", + lrs_load_reporting_server_name.value())); + } + contents.push_back(absl::StrCat("lb_policy=", lb_policy)); + if (lb_policy == "RING_HASH") { + contents.push_back(absl::StrCat("min_ring_size=", min_ring_size)); + contents.push_back(absl::StrCat("max_ring_size=", max_ring_size)); + } + contents.push_back( + absl::StrFormat("max_concurrent_requests=%d", max_concurrent_requests)); + return absl::StrCat("{", absl::StrJoin(contents, ", "), "}"); +} + +// +// XdsClusterResourceType +// + +namespace { + +grpc_error_handle UpstreamTlsContextParse( + const XdsEncodingContext& context, + const envoy_config_core_v3_TransportSocket* transport_socket, + CommonTlsContext* common_tls_context) { + // Record Upstream tls context + absl::string_view name = UpbStringToAbsl( + envoy_config_core_v3_TransportSocket_name(transport_socket)); + if (name != "envoy.transport_sockets.tls") { + return GRPC_ERROR_CREATE_FROM_CPP_STRING( + absl::StrCat("Unrecognized transport socket: ", name)); + } + auto* typed_config = + envoy_config_core_v3_TransportSocket_typed_config(transport_socket); + if (typed_config != nullptr) { + const upb_strview encoded_upstream_tls_context = + google_protobuf_Any_value(typed_config); + auto* upstream_tls_context = + envoy_extensions_transport_sockets_tls_v3_UpstreamTlsContext_parse( + encoded_upstream_tls_context.data, + encoded_upstream_tls_context.size, context.arena); + if (upstream_tls_context == nullptr) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Can't decode upstream tls context."); + } + auto* common_tls_context_proto = + envoy_extensions_transport_sockets_tls_v3_UpstreamTlsContext_common_tls_context( + upstream_tls_context); + if (common_tls_context_proto != nullptr) { + grpc_error_handle error = CommonTlsContext::Parse( + context, common_tls_context_proto, common_tls_context); + if (error != GRPC_ERROR_NONE) { + return grpc_error_add_child(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Error parsing UpstreamTlsContext"), + error); + } + } + } + if (common_tls_context->certificate_validation_context + .ca_certificate_provider_instance.instance_name.empty()) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "UpstreamTlsContext: TLS configuration provided but no " + "ca_certificate_provider_instance found."); + } + return GRPC_ERROR_NONE; +} + +grpc_error_handle CdsLogicalDnsParse( + const envoy_config_cluster_v3_Cluster* cluster, + XdsClusterResource* cds_update) { + const auto* load_assignment = + envoy_config_cluster_v3_Cluster_load_assignment(cluster); + if (load_assignment == nullptr) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "load_assignment not present for LOGICAL_DNS cluster"); + } + size_t num_localities; + const auto* const* localities = + envoy_config_endpoint_v3_ClusterLoadAssignment_endpoints(load_assignment, + &num_localities); + if (num_localities != 1) { + return GRPC_ERROR_CREATE_FROM_CPP_STRING( + absl::StrCat("load_assignment for LOGICAL_DNS cluster must have " + "exactly one locality, found ", + num_localities)); + } + size_t num_endpoints; + const auto* const* endpoints = + envoy_config_endpoint_v3_LocalityLbEndpoints_lb_endpoints(localities[0], + &num_endpoints); + if (num_endpoints != 1) { + return GRPC_ERROR_CREATE_FROM_CPP_STRING( + absl::StrCat("locality for LOGICAL_DNS cluster must have " + "exactly one endpoint, found ", + num_endpoints)); + } + const auto* endpoint = + envoy_config_endpoint_v3_LbEndpoint_endpoint(endpoints[0]); + if (endpoint == nullptr) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "LbEndpoint endpoint field not set"); + } + const auto* address = envoy_config_endpoint_v3_Endpoint_address(endpoint); + if (address == nullptr) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Endpoint address field not set"); + } + const auto* socket_address = + envoy_config_core_v3_Address_socket_address(address); + if (socket_address == nullptr) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Address socket_address field not set"); + } + if (envoy_config_core_v3_SocketAddress_resolver_name(socket_address).size != + 0) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "LOGICAL_DNS clusters must NOT have a custom resolver name set"); + } + absl::string_view address_str = UpbStringToAbsl( + envoy_config_core_v3_SocketAddress_address(socket_address)); + if (address_str.empty()) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "SocketAddress address field not set"); + } + if (!envoy_config_core_v3_SocketAddress_has_port_value(socket_address)) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "SocketAddress port_value field not set"); + } + cds_update->dns_hostname = JoinHostPort( + address_str, + envoy_config_core_v3_SocketAddress_port_value(socket_address)); + return GRPC_ERROR_NONE; +} + +// TODO(donnadionne): Check to see if cluster types aggregate_cluster and +// logical_dns are enabled, this will be +// removed once the cluster types are fully integration-tested and enabled by +// default. +bool XdsAggregateAndLogicalDnsClusterEnabled() { + char* value = gpr_getenv( + "GRPC_XDS_EXPERIMENTAL_ENABLE_AGGREGATE_AND_LOGICAL_DNS_CLUSTER"); + bool parsed_value; + bool parse_succeeded = gpr_parse_bool_value(value, &parsed_value); + gpr_free(value); + return parse_succeeded && parsed_value; +} + +grpc_error_handle CdsResourceParse( + const XdsEncodingContext& context, + const envoy_config_cluster_v3_Cluster* cluster, bool /*is_v2*/, + XdsClusterResource* cds_update) { + std::vector errors; + // Check the cluster_discovery_type. + if (!envoy_config_cluster_v3_Cluster_has_type(cluster) && + !envoy_config_cluster_v3_Cluster_has_cluster_type(cluster)) { + errors.push_back( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("DiscoveryType not found.")); + } else if (envoy_config_cluster_v3_Cluster_type(cluster) == + envoy_config_cluster_v3_Cluster_EDS) { + cds_update->cluster_type = XdsClusterResource::ClusterType::EDS; + // Check the EDS config source. + const envoy_config_cluster_v3_Cluster_EdsClusterConfig* eds_cluster_config = + envoy_config_cluster_v3_Cluster_eds_cluster_config(cluster); + const envoy_config_core_v3_ConfigSource* eds_config = + envoy_config_cluster_v3_Cluster_EdsClusterConfig_eds_config( + eds_cluster_config); + if (!envoy_config_core_v3_ConfigSource_has_ads(eds_config)) { + errors.push_back( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("EDS ConfigSource is not ADS.")); + } + // Record EDS service_name (if any). + upb_strview service_name = + envoy_config_cluster_v3_Cluster_EdsClusterConfig_service_name( + eds_cluster_config); + if (service_name.size != 0) { + cds_update->eds_service_name = UpbStringToStdString(service_name); + } + } else if (!XdsAggregateAndLogicalDnsClusterEnabled()) { + errors.push_back( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("DiscoveryType is not valid.")); + } else if (envoy_config_cluster_v3_Cluster_type(cluster) == + envoy_config_cluster_v3_Cluster_LOGICAL_DNS) { + cds_update->cluster_type = XdsClusterResource::ClusterType::LOGICAL_DNS; + grpc_error_handle error = CdsLogicalDnsParse(cluster, cds_update); + if (error != GRPC_ERROR_NONE) errors.push_back(error); + } else { + if (!envoy_config_cluster_v3_Cluster_has_cluster_type(cluster)) { + errors.push_back( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("DiscoveryType is not valid.")); + } else { + const envoy_config_cluster_v3_Cluster_CustomClusterType* + custom_cluster_type = + envoy_config_cluster_v3_Cluster_cluster_type(cluster); + upb_strview type_name = + envoy_config_cluster_v3_Cluster_CustomClusterType_name( + custom_cluster_type); + if (UpbStringToAbsl(type_name) != "envoy.clusters.aggregate") { + errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "DiscoveryType is not valid.")); + } else { + cds_update->cluster_type = XdsClusterResource::ClusterType::AGGREGATE; + // Retrieve aggregate clusters. + const google_protobuf_Any* typed_config = + envoy_config_cluster_v3_Cluster_CustomClusterType_typed_config( + custom_cluster_type); + const upb_strview aggregate_cluster_config_upb_strview = + google_protobuf_Any_value(typed_config); + const envoy_extensions_clusters_aggregate_v3_ClusterConfig* + aggregate_cluster_config = + envoy_extensions_clusters_aggregate_v3_ClusterConfig_parse( + aggregate_cluster_config_upb_strview.data, + aggregate_cluster_config_upb_strview.size, context.arena); + if (aggregate_cluster_config == nullptr) { + errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Can't parse aggregate cluster.")); + } else { + size_t size; + const upb_strview* clusters = + envoy_extensions_clusters_aggregate_v3_ClusterConfig_clusters( + aggregate_cluster_config, &size); + for (size_t i = 0; i < size; ++i) { + const upb_strview cluster = clusters[i]; + cds_update->prioritized_cluster_names.emplace_back( + UpbStringToStdString(cluster)); + } + } + } + } + } + // Check the LB policy. + if (envoy_config_cluster_v3_Cluster_lb_policy(cluster) == + envoy_config_cluster_v3_Cluster_ROUND_ROBIN) { + cds_update->lb_policy = "ROUND_ROBIN"; + } else if (envoy_config_cluster_v3_Cluster_lb_policy(cluster) == + envoy_config_cluster_v3_Cluster_RING_HASH) { + cds_update->lb_policy = "RING_HASH"; + // Record ring hash lb config + auto* ring_hash_config = + envoy_config_cluster_v3_Cluster_ring_hash_lb_config(cluster); + if (ring_hash_config != nullptr) { + const google_protobuf_UInt64Value* max_ring_size = + envoy_config_cluster_v3_Cluster_RingHashLbConfig_maximum_ring_size( + ring_hash_config); + if (max_ring_size != nullptr) { + cds_update->max_ring_size = + google_protobuf_UInt64Value_value(max_ring_size); + if (cds_update->max_ring_size > 8388608 || + cds_update->max_ring_size == 0) { + errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "max_ring_size is not in the range of 1 to 8388608.")); + } + } + const google_protobuf_UInt64Value* min_ring_size = + envoy_config_cluster_v3_Cluster_RingHashLbConfig_minimum_ring_size( + ring_hash_config); + if (min_ring_size != nullptr) { + cds_update->min_ring_size = + google_protobuf_UInt64Value_value(min_ring_size); + if (cds_update->min_ring_size > 8388608 || + cds_update->min_ring_size == 0) { + errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "min_ring_size is not in the range of 1 to 8388608.")); + } + if (cds_update->min_ring_size > cds_update->max_ring_size) { + errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "min_ring_size cannot be greater than max_ring_size.")); + } + } + if (envoy_config_cluster_v3_Cluster_RingHashLbConfig_hash_function( + ring_hash_config) != + envoy_config_cluster_v3_Cluster_RingHashLbConfig_XX_HASH) { + errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "ring hash lb config has invalid hash function.")); + } + } + } else { + errors.push_back( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("LB policy is not supported.")); + } + auto* transport_socket = + envoy_config_cluster_v3_Cluster_transport_socket(cluster); + if (transport_socket != nullptr) { + grpc_error_handle error = UpstreamTlsContextParse( + context, transport_socket, &cds_update->common_tls_context); + if (error != GRPC_ERROR_NONE) { + errors.push_back( + grpc_error_add_child(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Error parsing security configuration"), + error)); + } + } + // Record LRS server name (if any). + const envoy_config_core_v3_ConfigSource* lrs_server = + envoy_config_cluster_v3_Cluster_lrs_server(cluster); + if (lrs_server != nullptr) { + if (!envoy_config_core_v3_ConfigSource_has_self(lrs_server)) { + errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + ": LRS ConfigSource is not self.")); + } + cds_update->lrs_load_reporting_server_name.emplace(""); + } + // The Cluster resource encodes the circuit breaking parameters in a list of + // Thresholds messages, where each message specifies the parameters for a + // particular RoutingPriority. we will look only at the first entry in the + // list for priority DEFAULT and default to 1024 if not found. + if (envoy_config_cluster_v3_Cluster_has_circuit_breakers(cluster)) { + const envoy_config_cluster_v3_CircuitBreakers* circuit_breakers = + envoy_config_cluster_v3_Cluster_circuit_breakers(cluster); + size_t num_thresholds; + const envoy_config_cluster_v3_CircuitBreakers_Thresholds* const* + thresholds = envoy_config_cluster_v3_CircuitBreakers_thresholds( + circuit_breakers, &num_thresholds); + for (size_t i = 0; i < num_thresholds; ++i) { + const auto* threshold = thresholds[i]; + if (envoy_config_cluster_v3_CircuitBreakers_Thresholds_priority( + threshold) == envoy_config_core_v3_DEFAULT) { + const google_protobuf_UInt32Value* max_requests = + envoy_config_cluster_v3_CircuitBreakers_Thresholds_max_requests( + threshold); + if (max_requests != nullptr) { + cds_update->max_concurrent_requests = + google_protobuf_UInt32Value_value(max_requests); + } + break; + } + } + } + return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing CDS resource", &errors); +} + +void MaybeLogCluster(const XdsEncodingContext& context, + const envoy_config_cluster_v3_Cluster* cluster) { + if (GRPC_TRACE_FLAG_ENABLED(*context.tracer) && + gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) { + const upb_msgdef* msg_type = + envoy_config_cluster_v3_Cluster_getmsgdef(context.symtab); + char buf[10240]; + upb_text_encode(cluster, msg_type, nullptr, 0, buf, sizeof(buf)); + gpr_log(GPR_DEBUG, "[xds_client %p] Cluster: %s", context.client, buf); + } +} + +} // namespace + +absl::StatusOr XdsClusterResourceType::Decode( + const XdsEncodingContext& context, absl::string_view serialized_resource, + bool is_v2) const { + // Parse serialized proto. + auto* resource = envoy_config_cluster_v3_Cluster_parse( + serialized_resource.data(), serialized_resource.size(), context.arena); + if (resource == nullptr) { + return absl::InvalidArgumentError("Can't parse Listener resource."); + } + MaybeLogCluster(context, resource); + // Validate resource. + DecodeResult result; + result.name = + UpbStringToStdString(envoy_config_cluster_v3_Cluster_name(resource)); + auto cluster_data = absl::make_unique(); + grpc_error_handle error = + CdsResourceParse(context, resource, is_v2, &cluster_data->resource); + if (error != GRPC_ERROR_NONE) { + result.resource = absl::InvalidArgumentError(grpc_error_std_string(error)); + GRPC_ERROR_UNREF(error); + } else { + result.resource = std::move(cluster_data); + } + return std::move(result); +} + +} // namespace grpc_core diff --git a/src/core/ext/xds/xds_cluster.h b/src/core/ext/xds/xds_cluster.h new file mode 100644 index 00000000000..de195113b69 --- /dev/null +++ b/src/core/ext/xds/xds_cluster.h @@ -0,0 +1,101 @@ +// +// Copyright 2018 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef GRPC_CORE_EXT_XDS_XDS_CLUSTER_H +#define GRPC_CORE_EXT_XDS_XDS_CLUSTER_H + +#include + +#include +#include + +#include "absl/types/optional.h" + +#include "src/core/ext/xds/xds_common_types.h" +#include "src/core/ext/xds/xds_resource_type.h" + +namespace grpc_core { + +struct XdsClusterResource { + enum ClusterType { EDS, LOGICAL_DNS, AGGREGATE }; + ClusterType cluster_type; + // For cluster type EDS. + // The name to use in the EDS request. + // If empty, the cluster name will be used. + std::string eds_service_name; + // For cluster type LOGICAL_DNS. + // The hostname to lookup in DNS. + std::string dns_hostname; + // For cluster type AGGREGATE. + // The prioritized list of cluster names. + std::vector prioritized_cluster_names; + + // Tls Context used by clients + CommonTlsContext common_tls_context; + + // The LRS server to use for load reporting. + // If not set, load reporting will be disabled. + // If set to the empty string, will use the same server we obtained the CDS + // data from. + absl::optional lrs_load_reporting_server_name; + + // The LB policy to use (e.g., "ROUND_ROBIN" or "RING_HASH"). + std::string lb_policy; + // Used for RING_HASH LB policy only. + uint64_t min_ring_size = 1024; + uint64_t max_ring_size = 8388608; + // Maximum number of outstanding requests can be made to the upstream + // cluster. + uint32_t max_concurrent_requests = 1024; + + bool operator==(const XdsClusterResource& other) const { + return cluster_type == other.cluster_type && + eds_service_name == other.eds_service_name && + dns_hostname == other.dns_hostname && + prioritized_cluster_names == other.prioritized_cluster_names && + common_tls_context == other.common_tls_context && + lrs_load_reporting_server_name == + other.lrs_load_reporting_server_name && + lb_policy == other.lb_policy && + min_ring_size == other.min_ring_size && + max_ring_size == other.max_ring_size && + max_concurrent_requests == other.max_concurrent_requests; + } + + std::string ToString() const; +}; + +class XdsClusterResourceType : public XdsResourceType { + public: + struct ClusterData : public ResourceData { + XdsClusterResource resource; + }; + + absl::string_view type_url() const override { + return "envoy.config.cluster.v3.Cluster"; + } + absl::string_view v2_type_url() const override { + return "envoy.api.v2.Cluster"; + } + + absl::StatusOr Decode(const XdsEncodingContext& context, + absl::string_view serialized_resource, + bool is_v2) const override; +}; + +} // namespace grpc_core + +#endif // GRPC_CORE_EXT_XDS_XDS_CLUSTER_H diff --git a/src/core/ext/xds/xds_common_types.cc b/src/core/ext/xds/xds_common_types.cc new file mode 100644 index 00000000000..3bdb6697fbd --- /dev/null +++ b/src/core/ext/xds/xds_common_types.cc @@ -0,0 +1,388 @@ +// +// Copyright 2018 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include + +#include "src/core/ext/xds/xds_common_types.h" + +#include "absl/container/inlined_vector.h" +#include "absl/status/statusor.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" +#include "absl/strings/str_join.h" +#include "envoy/extensions/transport_sockets/tls/v3/common.upb.h" +#include "envoy/extensions/transport_sockets/tls/v3/tls.upb.h" +#include "envoy/type/matcher/v3/regex.upb.h" +#include "envoy/type/matcher/v3/string.upb.h" +#include "google/protobuf/any.upb.h" +#include "google/protobuf/wrappers.upb.h" +#include "xds/type/v3/typed_struct.upb.h" + +namespace grpc_core { + +// +// CommonTlsContext::CertificateValidationContext +// + +std::string CommonTlsContext::CertificateValidationContext::ToString() const { + std::vector contents; + for (const auto& match : match_subject_alt_names) { + contents.push_back(match.ToString()); + } + return absl::StrFormat("{match_subject_alt_names=[%s]}", + absl::StrJoin(contents, ", ")); +} + +bool CommonTlsContext::CertificateValidationContext::Empty() const { + return match_subject_alt_names.empty(); +} + +// +// CommonTlsContext::CertificateProviderPluginInstance +// + +std::string CommonTlsContext::CertificateProviderPluginInstance::ToString() + const { + absl::InlinedVector contents; + if (!instance_name.empty()) { + contents.push_back(absl::StrFormat("instance_name=%s", instance_name)); + } + if (!certificate_name.empty()) { + contents.push_back( + absl::StrFormat("certificate_name=%s", certificate_name)); + } + return absl::StrCat("{", absl::StrJoin(contents, ", "), "}"); +} + +bool CommonTlsContext::CertificateProviderPluginInstance::Empty() const { + return instance_name.empty() && certificate_name.empty(); +} + +// +// CommonTlsContext +// + +std::string CommonTlsContext::ToString() const { + absl::InlinedVector contents; + if (!tls_certificate_provider_instance.Empty()) { + contents.push_back( + absl::StrFormat("tls_certificate_provider_instance=%s", + tls_certificate_provider_instance.ToString())); + } + if (!certificate_validation_context.Empty()) { + contents.push_back( + absl::StrFormat("certificate_validation_context=%s", + certificate_validation_context.ToString())); + } + return absl::StrCat("{", absl::StrJoin(contents, ", "), "}"); +} + +bool CommonTlsContext::Empty() const { + return tls_certificate_provider_instance.Empty() && + certificate_validation_context.Empty(); +} + +namespace { + +// CertificateProviderInstance is deprecated but we are still supporting it for +// backward compatibility reasons. Note that we still parse the data into the +// same CertificateProviderPluginInstance struct since the fields are the same. +// TODO(yashykt): Remove this once we stop supporting the old way of fetching +// certificate provider instances. +grpc_error_handle CertificateProviderInstanceParse( + const XdsEncodingContext& context, + const envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CertificateProviderInstance* + certificate_provider_instance_proto, + CommonTlsContext::CertificateProviderPluginInstance* + certificate_provider_plugin_instance) { + *certificate_provider_plugin_instance = { + UpbStringToStdString( + envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CertificateProviderInstance_instance_name( + certificate_provider_instance_proto)), + UpbStringToStdString( + envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CertificateProviderInstance_certificate_name( + certificate_provider_instance_proto))}; + if (context.certificate_provider_definition_map->find( + certificate_provider_plugin_instance->instance_name) == + context.certificate_provider_definition_map->end()) { + return GRPC_ERROR_CREATE_FROM_CPP_STRING( + absl::StrCat("Unrecognized certificate provider instance name: ", + certificate_provider_plugin_instance->instance_name)); + } + return GRPC_ERROR_NONE; +} + +grpc_error_handle CertificateProviderPluginInstanceParse( + const XdsEncodingContext& context, + const envoy_extensions_transport_sockets_tls_v3_CertificateProviderPluginInstance* + certificate_provider_plugin_instance_proto, + CommonTlsContext::CertificateProviderPluginInstance* + certificate_provider_plugin_instance) { + *certificate_provider_plugin_instance = { + UpbStringToStdString( + envoy_extensions_transport_sockets_tls_v3_CertificateProviderPluginInstance_instance_name( + certificate_provider_plugin_instance_proto)), + UpbStringToStdString( + envoy_extensions_transport_sockets_tls_v3_CertificateProviderPluginInstance_certificate_name( + certificate_provider_plugin_instance_proto))}; + if (context.certificate_provider_definition_map->find( + certificate_provider_plugin_instance->instance_name) == + context.certificate_provider_definition_map->end()) { + return GRPC_ERROR_CREATE_FROM_CPP_STRING( + absl::StrCat("Unrecognized certificate provider instance name: ", + certificate_provider_plugin_instance->instance_name)); + } + return GRPC_ERROR_NONE; +} + +grpc_error_handle CertificateValidationContextParse( + const XdsEncodingContext& context, + const envoy_extensions_transport_sockets_tls_v3_CertificateValidationContext* + certificate_validation_context_proto, + CommonTlsContext::CertificateValidationContext* + certificate_validation_context) { + std::vector errors; + size_t len = 0; + auto* subject_alt_names_matchers = + envoy_extensions_transport_sockets_tls_v3_CertificateValidationContext_match_subject_alt_names( + certificate_validation_context_proto, &len); + for (size_t i = 0; i < len; ++i) { + StringMatcher::Type type; + std::string matcher; + if (envoy_type_matcher_v3_StringMatcher_has_exact( + subject_alt_names_matchers[i])) { + type = StringMatcher::Type::kExact; + matcher = UpbStringToStdString(envoy_type_matcher_v3_StringMatcher_exact( + subject_alt_names_matchers[i])); + } else if (envoy_type_matcher_v3_StringMatcher_has_prefix( + subject_alt_names_matchers[i])) { + type = StringMatcher::Type::kPrefix; + matcher = UpbStringToStdString(envoy_type_matcher_v3_StringMatcher_prefix( + subject_alt_names_matchers[i])); + } else if (envoy_type_matcher_v3_StringMatcher_has_suffix( + subject_alt_names_matchers[i])) { + type = StringMatcher::Type::kSuffix; + matcher = UpbStringToStdString(envoy_type_matcher_v3_StringMatcher_suffix( + subject_alt_names_matchers[i])); + } else if (envoy_type_matcher_v3_StringMatcher_has_contains( + subject_alt_names_matchers[i])) { + type = StringMatcher::Type::kContains; + matcher = + UpbStringToStdString(envoy_type_matcher_v3_StringMatcher_contains( + subject_alt_names_matchers[i])); + } else if (envoy_type_matcher_v3_StringMatcher_has_safe_regex( + subject_alt_names_matchers[i])) { + type = StringMatcher::Type::kSafeRegex; + auto* regex_matcher = envoy_type_matcher_v3_StringMatcher_safe_regex( + subject_alt_names_matchers[i]); + matcher = UpbStringToStdString( + envoy_type_matcher_v3_RegexMatcher_regex(regex_matcher)); + } else { + errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Invalid StringMatcher specified")); + continue; + } + bool ignore_case = envoy_type_matcher_v3_StringMatcher_ignore_case( + subject_alt_names_matchers[i]); + absl::StatusOr string_matcher = + StringMatcher::Create(type, matcher, + /*case_sensitive=*/!ignore_case); + if (!string_matcher.ok()) { + errors.push_back(GRPC_ERROR_CREATE_FROM_CPP_STRING( + absl::StrCat("string matcher: ", string_matcher.status().message()))); + continue; + } + if (type == StringMatcher::Type::kSafeRegex && ignore_case) { + errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "StringMatcher: ignore_case has no effect for SAFE_REGEX.")); + continue; + } + certificate_validation_context->match_subject_alt_names.push_back( + std::move(string_matcher.value())); + } + auto* ca_certificate_provider_instance = + envoy_extensions_transport_sockets_tls_v3_CertificateValidationContext_ca_certificate_provider_instance( + certificate_validation_context_proto); + if (ca_certificate_provider_instance != nullptr) { + grpc_error_handle error = CertificateProviderPluginInstanceParse( + context, ca_certificate_provider_instance, + &certificate_validation_context->ca_certificate_provider_instance); + if (error != GRPC_ERROR_NONE) errors.push_back(error); + } + if (envoy_extensions_transport_sockets_tls_v3_CertificateValidationContext_verify_certificate_spki( + certificate_validation_context_proto, nullptr) != nullptr) { + errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "CertificateValidationContext: verify_certificate_spki " + "unsupported")); + } + if (envoy_extensions_transport_sockets_tls_v3_CertificateValidationContext_verify_certificate_hash( + certificate_validation_context_proto, nullptr) != nullptr) { + errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "CertificateValidationContext: verify_certificate_hash " + "unsupported")); + } + auto* require_signed_certificate_timestamp = + envoy_extensions_transport_sockets_tls_v3_CertificateValidationContext_require_signed_certificate_timestamp( + certificate_validation_context_proto); + if (require_signed_certificate_timestamp != nullptr && + google_protobuf_BoolValue_value(require_signed_certificate_timestamp)) { + errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "CertificateValidationContext: " + "require_signed_certificate_timestamp unsupported")); + } + if (envoy_extensions_transport_sockets_tls_v3_CertificateValidationContext_has_crl( + certificate_validation_context_proto)) { + errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "CertificateValidationContext: crl unsupported")); + } + if (envoy_extensions_transport_sockets_tls_v3_CertificateValidationContext_has_custom_validator_config( + certificate_validation_context_proto)) { + errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "CertificateValidationContext: custom_validator_config " + "unsupported")); + } + return GRPC_ERROR_CREATE_FROM_VECTOR( + "Error parsing CertificateValidationContext", &errors); +} + +} // namespace + +grpc_error_handle CommonTlsContext::Parse( + const XdsEncodingContext& context, + const envoy_extensions_transport_sockets_tls_v3_CommonTlsContext* + common_tls_context_proto, + CommonTlsContext* common_tls_context) { + std::vector errors; + // The validation context is derived from the oneof in + // 'validation_context_type'. 'validation_context_sds_secret_config' is not + // supported. + auto* combined_validation_context = + envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_combined_validation_context( + common_tls_context_proto); + if (combined_validation_context != nullptr) { + auto* default_validation_context = + envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CombinedCertificateValidationContext_default_validation_context( + combined_validation_context); + if (default_validation_context != nullptr) { + grpc_error_handle error = CertificateValidationContextParse( + context, default_validation_context, + &common_tls_context->certificate_validation_context); + if (error != GRPC_ERROR_NONE) errors.push_back(error); + } + // If after parsing default_validation_context, + // common_tls_context->certificate_validation_context.ca_certificate_provider_instance + // is empty, fall back onto + // 'validation_context_certificate_provider_instance' inside + // 'combined_validation_context'. Note that this way of fetching root + // certificates is deprecated and will be removed in the future. + // TODO(yashykt): Remove this once it's no longer needed. + auto* validation_context_certificate_provider_instance = + envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_CombinedCertificateValidationContext_validation_context_certificate_provider_instance( + combined_validation_context); + if (common_tls_context->certificate_validation_context + .ca_certificate_provider_instance.Empty() && + validation_context_certificate_provider_instance != nullptr) { + grpc_error_handle error = CertificateProviderInstanceParse( + context, validation_context_certificate_provider_instance, + &common_tls_context->certificate_validation_context + .ca_certificate_provider_instance); + if (error != GRPC_ERROR_NONE) errors.push_back(error); + } + } else { + auto* validation_context = + envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_validation_context( + common_tls_context_proto); + if (validation_context != nullptr) { + grpc_error_handle error = CertificateValidationContextParse( + context, validation_context, + &common_tls_context->certificate_validation_context); + if (error != GRPC_ERROR_NONE) errors.push_back(error); + } else if ( + envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_has_validation_context_sds_secret_config( + common_tls_context_proto)) { + errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "validation_context_sds_secret_config unsupported")); + } + } + auto* tls_certificate_provider_instance = + envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_tls_certificate_provider_instance( + common_tls_context_proto); + if (tls_certificate_provider_instance != nullptr) { + grpc_error_handle error = CertificateProviderPluginInstanceParse( + context, tls_certificate_provider_instance, + &common_tls_context->tls_certificate_provider_instance); + if (error != GRPC_ERROR_NONE) errors.push_back(error); + } else { + // Fall back onto 'tls_certificate_certificate_provider_instance'. Note that + // this way of fetching identity certificates is deprecated and will be + // removed in the future. + // TODO(yashykt): Remove this once it's no longer needed. + auto* tls_certificate_certificate_provider_instance = + envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_tls_certificate_certificate_provider_instance( + common_tls_context_proto); + if (tls_certificate_certificate_provider_instance != nullptr) { + grpc_error_handle error = CertificateProviderInstanceParse( + context, tls_certificate_certificate_provider_instance, + &common_tls_context->tls_certificate_provider_instance); + if (error != GRPC_ERROR_NONE) errors.push_back(error); + } else { + if (envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_has_tls_certificates( + common_tls_context_proto)) { + errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "tls_certificates unsupported")); + } + if (envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_has_tls_certificate_sds_secret_configs( + common_tls_context_proto)) { + errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "tls_certificate_sds_secret_configs unsupported")); + } + } + } + if (envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_has_tls_params( + common_tls_context_proto)) { + errors.push_back( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("tls_params unsupported")); + } + if (envoy_extensions_transport_sockets_tls_v3_CommonTlsContext_has_custom_handshaker( + common_tls_context_proto)) { + errors.push_back( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("custom_handshaker unsupported")); + } + return GRPC_ERROR_CREATE_FROM_VECTOR("Error parsing CommonTlsContext", + &errors); +} + +grpc_error_handle ExtractHttpFilterTypeName(const XdsEncodingContext& context, + const google_protobuf_Any* any, + absl::string_view* filter_type) { + *filter_type = UpbStringToAbsl(google_protobuf_Any_type_url(any)); + if (*filter_type == "type.googleapis.com/xds.type.v3.TypedStruct" || + *filter_type == "type.googleapis.com/udpa.type.v1.TypedStruct") { + upb_strview any_value = google_protobuf_Any_value(any); + const auto* typed_struct = xds_type_v3_TypedStruct_parse( + any_value.data, any_value.size, context.arena); + if (typed_struct == nullptr) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "could not parse TypedStruct from filter config"); + } + *filter_type = + UpbStringToAbsl(xds_type_v3_TypedStruct_type_url(typed_struct)); + } + *filter_type = absl::StripPrefix(*filter_type, "type.googleapis.com/"); + return GRPC_ERROR_NONE; +} + +} // namespace grpc_core diff --git a/src/core/ext/xds/xds_common_types.h b/src/core/ext/xds/xds_common_types.h new file mode 100644 index 00000000000..d5d3f5a00c0 --- /dev/null +++ b/src/core/ext/xds/xds_common_types.h @@ -0,0 +1,110 @@ +// +// Copyright 2018 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef GRPC_CORE_EXT_XDS_XDS_COMMON_TYPES_H +#define GRPC_CORE_EXT_XDS_XDS_COMMON_TYPES_H + +#include + +#include +#include + +#include "absl/strings/str_format.h" +#include "envoy/extensions/transport_sockets/tls/v3/tls.upb.h" +#include "google/protobuf/any.upb.h" +#include "google/protobuf/duration.upb.h" + +#include "src/core/ext/xds/upb_utils.h" +#include "src/core/lib/matchers/matchers.h" + +namespace grpc_core { + +struct Duration { + int64_t seconds = 0; + int32_t nanos = 0; + + Duration() = default; + + bool operator==(const Duration& other) const { + return seconds == other.seconds && nanos == other.nanos; + } + std::string ToString() const { + return absl::StrFormat("Duration seconds: %ld, nanos %d", seconds, nanos); + } + + static Duration Parse(const google_protobuf_Duration* proto_duration) { + Duration duration; + duration.seconds = google_protobuf_Duration_seconds(proto_duration); + duration.nanos = google_protobuf_Duration_nanos(proto_duration); + return duration; + } +}; + +struct CommonTlsContext { + struct CertificateProviderPluginInstance { + std::string instance_name; + std::string certificate_name; + + bool operator==(const CertificateProviderPluginInstance& other) const { + return instance_name == other.instance_name && + certificate_name == other.certificate_name; + } + + std::string ToString() const; + bool Empty() const; + }; + + struct CertificateValidationContext { + CertificateProviderPluginInstance ca_certificate_provider_instance; + std::vector match_subject_alt_names; + + bool operator==(const CertificateValidationContext& other) const { + return ca_certificate_provider_instance == + other.ca_certificate_provider_instance && + match_subject_alt_names == other.match_subject_alt_names; + } + + std::string ToString() const; + bool Empty() const; + }; + + CertificateValidationContext certificate_validation_context; + CertificateProviderPluginInstance tls_certificate_provider_instance; + + bool operator==(const CommonTlsContext& other) const { + return certificate_validation_context == + other.certificate_validation_context && + tls_certificate_provider_instance == + other.tls_certificate_provider_instance; + } + + std::string ToString() const; + bool Empty() const; + + static grpc_error_handle Parse( + const XdsEncodingContext& context, + const envoy_extensions_transport_sockets_tls_v3_CommonTlsContext* + common_tls_context_proto, + CommonTlsContext* common_tls_context); +}; + +grpc_error_handle ExtractHttpFilterTypeName(const XdsEncodingContext& context, + const google_protobuf_Any* any, + absl::string_view* filter_type); + +} // namespace grpc_core + +#endif // GRPC_CORE_EXT_XDS_XDS_COMMON_TYPES_H diff --git a/src/core/ext/xds/xds_endpoint.cc b/src/core/ext/xds/xds_endpoint.cc new file mode 100644 index 00000000000..1b438c2ea23 --- /dev/null +++ b/src/core/ext/xds/xds_endpoint.cc @@ -0,0 +1,352 @@ +// +// Copyright 2018 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include + +#include "src/core/ext/xds/xds_endpoint.h" + +#include "absl/memory/memory.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_join.h" +#include "envoy/config/core/v3/address.upb.h" +#include "envoy/config/core/v3/base.upb.h" +#include "envoy/config/core/v3/health_check.upb.h" +#include "envoy/config/endpoint/v3/endpoint.upb.h" +#include "envoy/config/endpoint/v3/endpoint.upbdefs.h" +#include "envoy/config/endpoint/v3/endpoint_components.upb.h" +#include "envoy/type/v3/percent.upb.h" +#include "google/protobuf/wrappers.upb.h" +#include "upb/text_encode.h" +#include "upb/upb.h" +#include "upb/upb.hpp" + +#include "src/core/ext/xds/upb_utils.h" +#include "src/core/lib/address_utils/sockaddr_utils.h" + +namespace grpc_core { + +// +// XdsEndpointResource +// + +std::string XdsEndpointResource::Priority::Locality::ToString() const { + std::vector endpoint_strings; + for (const ServerAddress& endpoint : endpoints) { + endpoint_strings.emplace_back(endpoint.ToString()); + } + return absl::StrCat("{name=", name->AsHumanReadableString(), + ", lb_weight=", lb_weight, ", endpoints=[", + absl::StrJoin(endpoint_strings, ", "), "]}"); +} + +bool XdsEndpointResource::Priority::operator==(const Priority& other) const { + if (localities.size() != other.localities.size()) return false; + auto it1 = localities.begin(); + auto it2 = other.localities.begin(); + while (it1 != localities.end()) { + if (*it1->first != *it2->first) return false; + if (it1->second != it2->second) return false; + ++it1; + ++it2; + } + return true; +} + +std::string XdsEndpointResource::Priority::ToString() const { + std::vector locality_strings; + for (const auto& p : localities) { + locality_strings.emplace_back(p.second.ToString()); + } + return absl::StrCat("[", absl::StrJoin(locality_strings, ", "), "]"); +} + +bool XdsEndpointResource::DropConfig::ShouldDrop( + const std::string** category_name) const { + for (size_t i = 0; i < drop_category_list_.size(); ++i) { + const auto& drop_category = drop_category_list_[i]; + // Generate a random number in [0, 1000000). + const uint32_t random = static_cast(rand()) % 1000000; + if (random < drop_category.parts_per_million) { + *category_name = &drop_category.name; + return true; + } + } + return false; +} + +std::string XdsEndpointResource::DropConfig::ToString() const { + std::vector category_strings; + for (const DropCategory& category : drop_category_list_) { + category_strings.emplace_back( + absl::StrCat(category.name, "=", category.parts_per_million)); + } + return absl::StrCat("{[", absl::StrJoin(category_strings, ", "), + "], drop_all=", drop_all_, "}"); +} + +std::string XdsEndpointResource::ToString() const { + std::vector priority_strings; + for (size_t i = 0; i < priorities.size(); ++i) { + const Priority& priority = priorities[i]; + priority_strings.emplace_back( + absl::StrCat("priority ", i, ": ", priority.ToString())); + } + return absl::StrCat("priorities=[", absl::StrJoin(priority_strings, ", "), + "], drop_config=", drop_config->ToString()); +} + +// +// XdsEndpointResourceType +// + +namespace { + +void MaybeLogClusterLoadAssignment( + const XdsEncodingContext& context, + const envoy_config_endpoint_v3_ClusterLoadAssignment* cla) { + if (GRPC_TRACE_FLAG_ENABLED(*context.tracer) && + gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) { + const upb_msgdef* msg_type = + envoy_config_endpoint_v3_ClusterLoadAssignment_getmsgdef( + context.symtab); + char buf[10240]; + upb_text_encode(cla, msg_type, nullptr, 0, buf, sizeof(buf)); + gpr_log(GPR_DEBUG, "[xds_client %p] ClusterLoadAssignment: %s", + context.client, buf); + } +} + +grpc_error_handle ServerAddressParseAndAppend( + const envoy_config_endpoint_v3_LbEndpoint* lb_endpoint, + ServerAddressList* list) { + // If health_status is not HEALTHY or UNKNOWN, skip this endpoint. + const int32_t health_status = + envoy_config_endpoint_v3_LbEndpoint_health_status(lb_endpoint); + if (health_status != envoy_config_core_v3_UNKNOWN && + health_status != envoy_config_core_v3_HEALTHY) { + return GRPC_ERROR_NONE; + } + // Find the ip:port. + const envoy_config_endpoint_v3_Endpoint* endpoint = + envoy_config_endpoint_v3_LbEndpoint_endpoint(lb_endpoint); + const envoy_config_core_v3_Address* address = + envoy_config_endpoint_v3_Endpoint_address(endpoint); + const envoy_config_core_v3_SocketAddress* socket_address = + envoy_config_core_v3_Address_socket_address(address); + std::string address_str = UpbStringToStdString( + envoy_config_core_v3_SocketAddress_address(socket_address)); + uint32_t port = envoy_config_core_v3_SocketAddress_port_value(socket_address); + if (GPR_UNLIKELY(port >> 16) != 0) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Invalid port."); + } + // Find load_balancing_weight for the endpoint. + const google_protobuf_UInt32Value* load_balancing_weight = + envoy_config_endpoint_v3_LbEndpoint_load_balancing_weight(lb_endpoint); + const int32_t weight = + load_balancing_weight != nullptr + ? google_protobuf_UInt32Value_value(load_balancing_weight) + : 500; + if (weight == 0) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Invalid endpoint weight of 0."); + } + // Populate grpc_resolved_address. + grpc_resolved_address addr; + grpc_error_handle error = + grpc_string_to_sockaddr(&addr, address_str.c_str(), port); + if (error != GRPC_ERROR_NONE) return error; + // Append the address to the list. + std::map> + attributes; + attributes[ServerAddressWeightAttribute::kServerAddressWeightAttributeKey] = + absl::make_unique(weight); + list->emplace_back(addr, nullptr, std::move(attributes)); + return GRPC_ERROR_NONE; +} + +grpc_error_handle LocalityParse( + const envoy_config_endpoint_v3_LocalityLbEndpoints* locality_lb_endpoints, + XdsEndpointResource::Priority::Locality* output_locality, + size_t* priority) { + // Parse LB weight. + const google_protobuf_UInt32Value* lb_weight = + envoy_config_endpoint_v3_LocalityLbEndpoints_load_balancing_weight( + locality_lb_endpoints); + // If LB weight is not specified, it means this locality is assigned no load. + // TODO(juanlishen): When we support CDS to configure the inter-locality + // policy, we should change the LB weight handling. + output_locality->lb_weight = + lb_weight != nullptr ? google_protobuf_UInt32Value_value(lb_weight) : 0; + if (output_locality->lb_weight == 0) return GRPC_ERROR_NONE; + // Parse locality name. + const envoy_config_core_v3_Locality* locality = + envoy_config_endpoint_v3_LocalityLbEndpoints_locality( + locality_lb_endpoints); + if (locality == nullptr) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Empty locality."); + } + std::string region = + UpbStringToStdString(envoy_config_core_v3_Locality_region(locality)); + std::string zone = + UpbStringToStdString(envoy_config_core_v3_Locality_region(locality)); + std::string sub_zone = + UpbStringToStdString(envoy_config_core_v3_Locality_sub_zone(locality)); + output_locality->name = MakeRefCounted( + std::move(region), std::move(zone), std::move(sub_zone)); + // Parse the addresses. + size_t size; + const envoy_config_endpoint_v3_LbEndpoint* const* lb_endpoints = + envoy_config_endpoint_v3_LocalityLbEndpoints_lb_endpoints( + locality_lb_endpoints, &size); + for (size_t i = 0; i < size; ++i) { + grpc_error_handle error = ServerAddressParseAndAppend( + lb_endpoints[i], &output_locality->endpoints); + if (error != GRPC_ERROR_NONE) return error; + } + // Parse the priority. + *priority = envoy_config_endpoint_v3_LocalityLbEndpoints_priority( + locality_lb_endpoints); + return GRPC_ERROR_NONE; +} + +grpc_error_handle DropParseAndAppend( + const envoy_config_endpoint_v3_ClusterLoadAssignment_Policy_DropOverload* + drop_overload, + XdsEndpointResource::DropConfig* drop_config) { + // Get the category. + std::string category = UpbStringToStdString( + envoy_config_endpoint_v3_ClusterLoadAssignment_Policy_DropOverload_category( + drop_overload)); + if (category.empty()) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Empty drop category name"); + } + // Get the drop rate (per million). + const envoy_type_v3_FractionalPercent* drop_percentage = + envoy_config_endpoint_v3_ClusterLoadAssignment_Policy_DropOverload_drop_percentage( + drop_overload); + uint32_t numerator = + envoy_type_v3_FractionalPercent_numerator(drop_percentage); + const auto denominator = + static_cast( + envoy_type_v3_FractionalPercent_denominator(drop_percentage)); + // Normalize to million. + switch (denominator) { + case envoy_type_v3_FractionalPercent_HUNDRED: + numerator *= 10000; + break; + case envoy_type_v3_FractionalPercent_TEN_THOUSAND: + numerator *= 100; + break; + case envoy_type_v3_FractionalPercent_MILLION: + break; + default: + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Unknown denominator type"); + } + // Cap numerator to 1000000. + numerator = std::min(numerator, 1000000u); + drop_config->AddCategory(std::move(category), numerator); + return GRPC_ERROR_NONE; +} + +grpc_error_handle EdsResourceParse( + const XdsEncodingContext& /*context*/, + const envoy_config_endpoint_v3_ClusterLoadAssignment* + cluster_load_assignment, + bool /*is_v2*/, XdsEndpointResource* eds_update) { + std::vector errors; + // Get the endpoints. + size_t locality_size; + const envoy_config_endpoint_v3_LocalityLbEndpoints* const* endpoints = + envoy_config_endpoint_v3_ClusterLoadAssignment_endpoints( + cluster_load_assignment, &locality_size); + for (size_t j = 0; j < locality_size; ++j) { + size_t priority; + XdsEndpointResource::Priority::Locality locality; + grpc_error_handle error = LocalityParse(endpoints[j], &locality, &priority); + if (error != GRPC_ERROR_NONE) { + errors.push_back(error); + continue; + } + // Filter out locality with weight 0. + if (locality.lb_weight == 0) continue; + // Make sure prorities is big enough. Note that they might not + // arrive in priority order. + while (eds_update->priorities.size() < priority + 1) { + eds_update->priorities.emplace_back(); + } + eds_update->priorities[priority].localities.emplace(locality.name.get(), + std::move(locality)); + } + for (const auto& priority : eds_update->priorities) { + if (priority.localities.empty()) { + errors.push_back( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("sparse priority list")); + } + } + // Get the drop config. + eds_update->drop_config = MakeRefCounted(); + const envoy_config_endpoint_v3_ClusterLoadAssignment_Policy* policy = + envoy_config_endpoint_v3_ClusterLoadAssignment_policy( + cluster_load_assignment); + if (policy != nullptr) { + size_t drop_size; + const envoy_config_endpoint_v3_ClusterLoadAssignment_Policy_DropOverload* const* + drop_overload = + envoy_config_endpoint_v3_ClusterLoadAssignment_Policy_drop_overloads( + policy, &drop_size); + for (size_t j = 0; j < drop_size; ++j) { + grpc_error_handle error = + DropParseAndAppend(drop_overload[j], eds_update->drop_config.get()); + if (error != GRPC_ERROR_NONE) { + errors.push_back( + grpc_error_add_child(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "drop config validation error"), + error)); + } + } + } + return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing EDS resource", &errors); +} + +} // namespace + +absl::StatusOr XdsEndpointResourceType::Decode( + const XdsEncodingContext& context, absl::string_view serialized_resource, + bool is_v2) const { + // Parse serialized proto. + auto* resource = envoy_config_endpoint_v3_ClusterLoadAssignment_parse( + serialized_resource.data(), serialized_resource.size(), context.arena); + if (resource == nullptr) { + return absl::InvalidArgumentError("Can't parse Listener resource."); + } + MaybeLogClusterLoadAssignment(context, resource); + // Validate resource. + DecodeResult result; + result.name = UpbStringToStdString( + envoy_config_endpoint_v3_ClusterLoadAssignment_cluster_name(resource)); + auto endpoint_data = absl::make_unique(); + grpc_error_handle error = + EdsResourceParse(context, resource, is_v2, &endpoint_data->resource); + if (error != GRPC_ERROR_NONE) { + result.resource = absl::InvalidArgumentError(grpc_error_std_string(error)); + GRPC_ERROR_UNREF(error); + } else { + result.resource = std::move(endpoint_data); + } + return std::move(result); +} + +} // namespace grpc_core diff --git a/src/core/ext/xds/xds_endpoint.h b/src/core/ext/xds/xds_endpoint.h new file mode 100644 index 00000000000..089d3e1b096 --- /dev/null +++ b/src/core/ext/xds/xds_endpoint.h @@ -0,0 +1,132 @@ +// +// Copyright 2018 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef GRPC_CORE_EXT_XDS_XDS_ENDPOINT_H +#define GRPC_CORE_EXT_XDS_XDS_ENDPOINT_H + +#include + +#include +#include +#include + +#include "absl/container/inlined_vector.h" + +#include "src/core/ext/filters/client_channel/server_address.h" +#include "src/core/ext/xds/xds_client_stats.h" +#include "src/core/ext/xds/xds_resource_type.h" +#include "src/core/lib/gprpp/ref_counted_ptr.h" + +namespace grpc_core { + +struct XdsEndpointResource { + struct Priority { + struct Locality { + RefCountedPtr name; + uint32_t lb_weight; + ServerAddressList endpoints; + + bool operator==(const Locality& other) const { + return *name == *other.name && lb_weight == other.lb_weight && + endpoints == other.endpoints; + } + bool operator!=(const Locality& other) const { return !(*this == other); } + std::string ToString() const; + }; + + std::map localities; + + bool operator==(const Priority& other) const; + std::string ToString() const; + }; + using PriorityList = absl::InlinedVector; + + // There are two phases of accessing this class's content: + // 1. to initialize in the control plane combiner; + // 2. to use in the data plane combiner. + // So no additional synchronization is needed. + class DropConfig : public RefCounted { + public: + struct DropCategory { + bool operator==(const DropCategory& other) const { + return name == other.name && + parts_per_million == other.parts_per_million; + } + + std::string name; + const uint32_t parts_per_million; + }; + + using DropCategoryList = absl::InlinedVector; + + void AddCategory(std::string name, uint32_t parts_per_million) { + drop_category_list_.emplace_back( + DropCategory{std::move(name), parts_per_million}); + if (parts_per_million == 1000000) drop_all_ = true; + } + + // The only method invoked from outside the WorkSerializer (used in + // the data plane). + bool ShouldDrop(const std::string** category_name) const; + + const DropCategoryList& drop_category_list() const { + return drop_category_list_; + } + + bool drop_all() const { return drop_all_; } + + bool operator==(const DropConfig& other) const { + return drop_category_list_ == other.drop_category_list_; + } + bool operator!=(const DropConfig& other) const { return !(*this == other); } + + std::string ToString() const; + + private: + DropCategoryList drop_category_list_; + bool drop_all_ = false; + }; + + PriorityList priorities; + RefCountedPtr drop_config; + + bool operator==(const XdsEndpointResource& other) const { + return priorities == other.priorities && *drop_config == *other.drop_config; + } + std::string ToString() const; +}; + +class XdsEndpointResourceType : public XdsResourceType { + public: + struct EndpointData : public ResourceData { + XdsEndpointResource resource; + }; + + absl::string_view type_url() const override { + return "envoy.config.endpoint.v3.ClusterLoadAssignment"; + } + absl::string_view v2_type_url() const override { + return "envoy.api.v2.ClusterLoadAssignment"; + } + + absl::StatusOr Decode(const XdsEncodingContext& context, + absl::string_view serialized_resource, + bool is_v2) const override; +}; + +} // namespace grpc_core + +#endif // GRPC_CORE_EXT_XDS_XDS_ENDPOINT_H diff --git a/src/core/ext/xds/xds_listener.cc b/src/core/ext/xds/xds_listener.cc new file mode 100644 index 00000000000..e51d2dec7c1 --- /dev/null +++ b/src/core/ext/xds/xds_listener.cc @@ -0,0 +1,1012 @@ +// +// Copyright 2018 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include + +#include "src/core/ext/xds/xds_listener.h" + +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" +#include "absl/strings/str_join.h" +#include "absl/strings/str_split.h" +#include "envoy/config/core/v3/address.upb.h" +#include "envoy/config/core/v3/base.upb.h" +#include "envoy/config/core/v3/config_source.upb.h" +#include "envoy/config/core/v3/protocol.upb.h" +#include "envoy/config/listener/v3/api_listener.upb.h" +#include "envoy/config/listener/v3/listener.upb.h" +#include "envoy/config/listener/v3/listener.upbdefs.h" +#include "envoy/config/listener/v3/listener_components.upb.h" +#include "envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.upb.h" +#include "envoy/extensions/filters/network/http_connection_manager/v3/http_connection_manager.upbdefs.h" +#include "google/protobuf/wrappers.upb.h" +#include "upb/text_encode.h" +#include "upb/upb.h" +#include "upb/upb.hpp" + +#include "src/core/lib/address_utils/sockaddr_utils.h" +#include "src/core/lib/gprpp/host_port.h" +#include "src/core/lib/iomgr/sockaddr.h" + +namespace grpc_core { + +// +// XdsListenerResource::DownstreamTlsContext +// + +std::string XdsListenerResource::DownstreamTlsContext::ToString() const { + return absl::StrFormat("common_tls_context=%s, require_client_certificate=%s", + common_tls_context.ToString(), + require_client_certificate ? "true" : "false"); +} + +bool XdsListenerResource::DownstreamTlsContext::Empty() const { + return common_tls_context.Empty(); +} + +// +// XdsListenerResource::HttpConnectionManager +// + +std::string XdsListenerResource::HttpConnectionManager::ToString() const { + absl::InlinedVector contents; + contents.push_back(absl::StrFormat( + "route_config_name=%s", + !route_config_name.empty() ? route_config_name.c_str() : "")); + contents.push_back(absl::StrFormat("http_max_stream_duration=%s", + http_max_stream_duration.ToString())); + if (rds_update.has_value()) { + contents.push_back( + absl::StrFormat("rds_update=%s", rds_update->ToString())); + } + if (!http_filters.empty()) { + std::vector filter_strings; + for (const auto& http_filter : http_filters) { + filter_strings.push_back(http_filter.ToString()); + } + contents.push_back(absl::StrCat("http_filters=[", + absl::StrJoin(filter_strings, ", "), "]")); + } + return absl::StrCat("{", absl::StrJoin(contents, ", "), "}"); +} + +// +// XdsListenerResource::HttpFilter +// + +std::string XdsListenerResource::HttpConnectionManager::HttpFilter::ToString() + const { + return absl::StrCat("{name=", name, ", config=", config.ToString(), "}"); +} + +// +// XdsListenerResource::FilterChainData +// + +std::string XdsListenerResource::FilterChainData::ToString() const { + return absl::StrCat( + "{downstream_tls_context=", downstream_tls_context.ToString(), + " http_connection_manager=", http_connection_manager.ToString(), "}"); +} + +// +// XdsListenerResource::FilterChainMap::CidrRange +// + +std::string XdsListenerResource::FilterChainMap::CidrRange::ToString() const { + return absl::StrCat( + "{address_prefix=", grpc_sockaddr_to_string(&address, false), + ", prefix_len=", prefix_len, "}"); +} + +// +// FilterChain +// + +struct FilterChain { + struct FilterChainMatch { + uint32_t destination_port = 0; + std::vector prefix_ranges; + XdsListenerResource::FilterChainMap::ConnectionSourceType source_type = + XdsListenerResource::FilterChainMap::ConnectionSourceType::kAny; + std::vector + source_prefix_ranges; + std::vector source_ports; + std::vector server_names; + std::string transport_protocol; + std::vector application_protocols; + + std::string ToString() const; + } filter_chain_match; + + std::shared_ptr filter_chain_data; +}; + +std::string FilterChain::FilterChainMatch::ToString() const { + absl::InlinedVector contents; + if (destination_port != 0) { + contents.push_back(absl::StrCat("destination_port=", destination_port)); + } + if (!prefix_ranges.empty()) { + std::vector prefix_ranges_content; + for (const auto& range : prefix_ranges) { + prefix_ranges_content.push_back(range.ToString()); + } + contents.push_back(absl::StrCat( + "prefix_ranges={", absl::StrJoin(prefix_ranges_content, ", "), "}")); + } + if (source_type == XdsListenerResource::FilterChainMap::ConnectionSourceType:: + kSameIpOrLoopback) { + contents.push_back("source_type=SAME_IP_OR_LOOPBACK"); + } else if (source_type == XdsListenerResource::FilterChainMap:: + ConnectionSourceType::kExternal) { + contents.push_back("source_type=EXTERNAL"); + } + if (!source_prefix_ranges.empty()) { + std::vector source_prefix_ranges_content; + for (const auto& range : source_prefix_ranges) { + source_prefix_ranges_content.push_back(range.ToString()); + } + contents.push_back( + absl::StrCat("source_prefix_ranges={", + absl::StrJoin(source_prefix_ranges_content, ", "), "}")); + } + if (!source_ports.empty()) { + contents.push_back( + absl::StrCat("source_ports={", absl::StrJoin(source_ports, ", "), "}")); + } + if (!server_names.empty()) { + contents.push_back( + absl::StrCat("server_names={", absl::StrJoin(server_names, ", "), "}")); + } + if (!transport_protocol.empty()) { + contents.push_back(absl::StrCat("transport_protocol=", transport_protocol)); + } + if (!application_protocols.empty()) { + contents.push_back(absl::StrCat("application_protocols={", + absl::StrJoin(application_protocols, ", "), + "}")); + } + return absl::StrCat("{", absl::StrJoin(contents, ", "), "}"); +} + +// +// XdsListenerResource::FilterChainMap +// + +std::string XdsListenerResource::FilterChainMap::ToString() const { + std::vector contents; + for (const auto& destination_ip : destination_ip_vector) { + for (int source_type = 0; source_type < 3; ++source_type) { + for (const auto& source_ip : + destination_ip.source_types_array[source_type]) { + for (const auto& source_port_pair : source_ip.ports_map) { + FilterChain::FilterChainMatch filter_chain_match; + if (destination_ip.prefix_range.has_value()) { + filter_chain_match.prefix_ranges.push_back( + *destination_ip.prefix_range); + } + filter_chain_match.source_type = static_cast< + XdsListenerResource::FilterChainMap::ConnectionSourceType>( + source_type); + if (source_ip.prefix_range.has_value()) { + filter_chain_match.source_prefix_ranges.push_back( + *source_ip.prefix_range); + } + if (source_port_pair.first != 0) { + filter_chain_match.source_ports.push_back(source_port_pair.first); + } + contents.push_back(absl::StrCat( + "{filter_chain_match=", filter_chain_match.ToString(), + ", filter_chain=", source_port_pair.second.data->ToString(), + "}")); + } + } + } + } + return absl::StrCat("{", absl::StrJoin(contents, ", "), "}"); +} + +// +// XdsListenerResource +// + +std::string XdsListenerResource::ToString() const { + absl::InlinedVector contents; + if (type == ListenerType::kTcpListener) { + contents.push_back(absl::StrCat("address=", address)); + contents.push_back( + absl::StrCat("filter_chain_map=", filter_chain_map.ToString())); + if (default_filter_chain.has_value()) { + contents.push_back(absl::StrCat("default_filter_chain=", + default_filter_chain->ToString())); + } + } else if (type == ListenerType::kHttpApiListener) { + contents.push_back(absl::StrFormat("http_connection_manager=%s", + http_connection_manager.ToString())); + } + return absl::StrCat("{", absl::StrJoin(contents, ", "), "}"); +} + +// +// XdsListenerResourceType +// + +namespace { + +void MaybeLogHttpConnectionManager( + const XdsEncodingContext& context, + const envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager* + http_connection_manager_config) { + if (GRPC_TRACE_FLAG_ENABLED(*context.tracer) && + gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) { + const upb_msgdef* msg_type = + envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_getmsgdef( + context.symtab); + char buf[10240]; + upb_text_encode(http_connection_manager_config, msg_type, nullptr, 0, buf, + sizeof(buf)); + gpr_log(GPR_DEBUG, "[xds_client %p] HttpConnectionManager: %s", + context.client, buf); + } +} + +grpc_error_handle HttpConnectionManagerParse( + bool is_client, const XdsEncodingContext& context, + const envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager* + http_connection_manager_proto, + bool is_v2, + XdsListenerResource::HttpConnectionManager* http_connection_manager) { + MaybeLogHttpConnectionManager(context, http_connection_manager_proto); + // Obtain max_stream_duration from Http Protocol Options. + const envoy_config_core_v3_HttpProtocolOptions* options = + envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_common_http_protocol_options( + http_connection_manager_proto); + if (options != nullptr) { + const google_protobuf_Duration* duration = + envoy_config_core_v3_HttpProtocolOptions_max_stream_duration(options); + if (duration != nullptr) { + http_connection_manager->http_max_stream_duration = + Duration::Parse(duration); + } + } + // Parse filters. + if (!is_v2) { + size_t num_filters = 0; + const auto* http_filters = + envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_http_filters( + http_connection_manager_proto, &num_filters); + std::set names_seen; + for (size_t i = 0; i < num_filters; ++i) { + const auto* http_filter = http_filters[i]; + absl::string_view name = UpbStringToAbsl( + envoy_extensions_filters_network_http_connection_manager_v3_HttpFilter_name( + http_filter)); + if (name.empty()) { + return GRPC_ERROR_CREATE_FROM_CPP_STRING( + absl::StrCat("empty filter name at index ", i)); + } + if (names_seen.find(name) != names_seen.end()) { + return GRPC_ERROR_CREATE_FROM_CPP_STRING( + absl::StrCat("duplicate HTTP filter name: ", name)); + } + names_seen.insert(name); + const bool is_optional = + envoy_extensions_filters_network_http_connection_manager_v3_HttpFilter_is_optional( + http_filter); + const google_protobuf_Any* any = + envoy_extensions_filters_network_http_connection_manager_v3_HttpFilter_typed_config( + http_filter); + if (any == nullptr) { + if (is_optional) continue; + return GRPC_ERROR_CREATE_FROM_CPP_STRING( + absl::StrCat("no filter config specified for filter name ", name)); + } + absl::string_view filter_type; + grpc_error_handle error = + ExtractHttpFilterTypeName(context, any, &filter_type); + if (error != GRPC_ERROR_NONE) return error; + const XdsHttpFilterImpl* filter_impl = + XdsHttpFilterRegistry::GetFilterForType(filter_type); + if (filter_impl == nullptr) { + if (is_optional) continue; + return GRPC_ERROR_CREATE_FROM_CPP_STRING( + absl::StrCat("no filter registered for config type ", filter_type)); + } + if ((is_client && !filter_impl->IsSupportedOnClients()) || + (!is_client && !filter_impl->IsSupportedOnServers())) { + if (is_optional) continue; + return GRPC_ERROR_CREATE_FROM_CPP_STRING( + absl::StrFormat("Filter %s is not supported on %s", filter_type, + is_client ? "clients" : "servers")); + } + absl::StatusOr filter_config = + filter_impl->GenerateFilterConfig(google_protobuf_Any_value(any), + context.arena); + if (!filter_config.ok()) { + return GRPC_ERROR_CREATE_FROM_CPP_STRING(absl::StrCat( + "filter config for type ", filter_type, + " failed to parse: ", filter_config.status().ToString())); + } + http_connection_manager->http_filters.emplace_back( + XdsListenerResource::HttpConnectionManager::HttpFilter{ + std::string(name), std::move(*filter_config)}); + } + if (http_connection_manager->http_filters.empty()) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Expected at least one HTTP filter"); + } + // Make sure that the last filter is terminal and non-last filters are + // non-terminal. Note that this check is being performed in a separate loop + // to take care of the case where there are two terminal filters in the list + // out of which only one gets added in the final list. + for (const auto& http_filter : http_connection_manager->http_filters) { + const XdsHttpFilterImpl* filter_impl = + XdsHttpFilterRegistry::GetFilterForType( + http_filter.config.config_proto_type_name); + if (&http_filter != &http_connection_manager->http_filters.back()) { + // Filters before the last filter must not be terminal. + if (filter_impl->IsTerminalFilter()) { + return GRPC_ERROR_CREATE_FROM_CPP_STRING( + absl::StrCat("terminal filter for config type ", + http_filter.config.config_proto_type_name, + " must be the last filter in the chain")); + } + } else { + // The last filter must be terminal. + if (!filter_impl->IsTerminalFilter()) { + return GRPC_ERROR_CREATE_FROM_CPP_STRING( + absl::StrCat("non-terminal filter for config type ", + http_filter.config.config_proto_type_name, + " is the last filter in the chain")); + } + } + } + } else { + // If using a v2 config, we just hard-code a list containing only the + // router filter without actually looking at the config. This ensures + // that the right thing happens in the xds resolver without having + // to expose whether the resource we received was v2 or v3. + http_connection_manager->http_filters.emplace_back( + XdsListenerResource::HttpConnectionManager::HttpFilter{ + "router", {kXdsHttpRouterFilterConfigName, Json()}}); + } + // Guarding parsing of RouteConfig on the server side with the environmental + // variable since that's the first feature on the server side that will be + // using this. + if (is_client || XdsRbacEnabled()) { + // Found inlined route_config. Parse it to find the cluster_name. + if (envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_has_route_config( + http_connection_manager_proto)) { + const envoy_config_route_v3_RouteConfiguration* route_config = + envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_route_config( + http_connection_manager_proto); + XdsRouteConfigResource rds_update; + grpc_error_handle error = + XdsRouteConfigResource::Parse(context, route_config, &rds_update); + if (error != GRPC_ERROR_NONE) return error; + http_connection_manager->rds_update = std::move(rds_update); + return GRPC_ERROR_NONE; + } + // Validate that RDS must be used to get the route_config dynamically. + const envoy_extensions_filters_network_http_connection_manager_v3_Rds* rds = + envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_rds( + http_connection_manager_proto); + if (rds == nullptr) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "HttpConnectionManager neither has inlined route_config nor RDS."); + } + // Check that the ConfigSource specifies ADS. + const envoy_config_core_v3_ConfigSource* config_source = + envoy_extensions_filters_network_http_connection_manager_v3_Rds_config_source( + rds); + if (config_source == nullptr) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "HttpConnectionManager missing config_source for RDS."); + } + if (!envoy_config_core_v3_ConfigSource_has_ads(config_source)) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "HttpConnectionManager ConfigSource for RDS does not specify ADS."); + } + // Get the route_config_name. + http_connection_manager->route_config_name = UpbStringToStdString( + envoy_extensions_filters_network_http_connection_manager_v3_Rds_route_config_name( + rds)); + } + return GRPC_ERROR_NONE; +} + +grpc_error_handle LdsResourceParseClient( + const XdsEncodingContext& context, + const envoy_config_listener_v3_ApiListener* api_listener, bool is_v2, + XdsListenerResource* lds_update) { + lds_update->type = XdsListenerResource::ListenerType::kHttpApiListener; + const upb_strview encoded_api_listener = google_protobuf_Any_value( + envoy_config_listener_v3_ApiListener_api_listener(api_listener)); + const auto* http_connection_manager = + envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_parse( + encoded_api_listener.data, encoded_api_listener.size, context.arena); + if (http_connection_manager == nullptr) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Could not parse HttpConnectionManager config from ApiListener"); + } + return HttpConnectionManagerParse(true /* is_client */, context, + http_connection_manager, is_v2, + &lds_update->http_connection_manager); +} + +grpc_error_handle DownstreamTlsContextParse( + const XdsEncodingContext& context, + const envoy_config_core_v3_TransportSocket* transport_socket, + XdsListenerResource::DownstreamTlsContext* downstream_tls_context) { + absl::string_view name = UpbStringToAbsl( + envoy_config_core_v3_TransportSocket_name(transport_socket)); + if (name != "envoy.transport_sockets.tls") { + return GRPC_ERROR_CREATE_FROM_CPP_STRING( + absl::StrCat("Unrecognized transport socket: ", name)); + } + auto* typed_config = + envoy_config_core_v3_TransportSocket_typed_config(transport_socket); + std::vector errors; + if (typed_config != nullptr) { + const upb_strview encoded_downstream_tls_context = + google_protobuf_Any_value(typed_config); + auto* downstream_tls_context_proto = + envoy_extensions_transport_sockets_tls_v3_DownstreamTlsContext_parse( + encoded_downstream_tls_context.data, + encoded_downstream_tls_context.size, context.arena); + if (downstream_tls_context_proto == nullptr) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Can't decode downstream tls context."); + } + auto* common_tls_context = + envoy_extensions_transport_sockets_tls_v3_DownstreamTlsContext_common_tls_context( + downstream_tls_context_proto); + if (common_tls_context != nullptr) { + grpc_error_handle error = + CommonTlsContext::Parse(context, common_tls_context, + &downstream_tls_context->common_tls_context); + if (error != GRPC_ERROR_NONE) errors.push_back(error); + } + auto* require_client_certificate = + envoy_extensions_transport_sockets_tls_v3_DownstreamTlsContext_require_client_certificate( + downstream_tls_context_proto); + if (require_client_certificate != nullptr) { + downstream_tls_context->require_client_certificate = + google_protobuf_BoolValue_value(require_client_certificate); + } + auto* require_sni = + envoy_extensions_transport_sockets_tls_v3_DownstreamTlsContext_require_sni( + downstream_tls_context_proto); + if (require_sni != nullptr && + google_protobuf_BoolValue_value(require_sni)) { + errors.push_back( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("require_sni: unsupported")); + } + if (envoy_extensions_transport_sockets_tls_v3_DownstreamTlsContext_ocsp_staple_policy( + downstream_tls_context_proto) != + envoy_extensions_transport_sockets_tls_v3_DownstreamTlsContext_LENIENT_STAPLING) { + errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "ocsp_staple_policy: Only LENIENT_STAPLING supported")); + } + } + if (downstream_tls_context->common_tls_context + .tls_certificate_provider_instance.instance_name.empty()) { + errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "TLS configuration provided but no " + "tls_certificate_provider_instance found.")); + } + if (downstream_tls_context->require_client_certificate && + downstream_tls_context->common_tls_context.certificate_validation_context + .ca_certificate_provider_instance.instance_name.empty()) { + errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "TLS configuration requires client certificates but no certificate " + "provider instance specified for validation.")); + } + if (!downstream_tls_context->common_tls_context.certificate_validation_context + .match_subject_alt_names.empty()) { + errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "match_subject_alt_names not supported on servers")); + } + return GRPC_ERROR_CREATE_FROM_VECTOR("Error parsing DownstreamTlsContext", + &errors); +} + +grpc_error_handle CidrRangeParse( + const envoy_config_core_v3_CidrRange* cidr_range_proto, + XdsListenerResource::FilterChainMap::CidrRange* cidr_range) { + std::string address_prefix = UpbStringToStdString( + envoy_config_core_v3_CidrRange_address_prefix(cidr_range_proto)); + grpc_error_handle error = + grpc_string_to_sockaddr(&cidr_range->address, address_prefix.c_str(), 0); + if (error != GRPC_ERROR_NONE) return error; + cidr_range->prefix_len = 0; + auto* prefix_len_proto = + envoy_config_core_v3_CidrRange_prefix_len(cidr_range_proto); + if (prefix_len_proto != nullptr) { + cidr_range->prefix_len = std::min( + google_protobuf_UInt32Value_value(prefix_len_proto), + (reinterpret_cast(cidr_range->address.addr)) + ->sa_family == GRPC_AF_INET + ? uint32_t(32) + : uint32_t(128)); + } + // Normalize the network address by masking it with prefix_len + grpc_sockaddr_mask_bits(&cidr_range->address, cidr_range->prefix_len); + return GRPC_ERROR_NONE; +} + +grpc_error_handle FilterChainMatchParse( + const envoy_config_listener_v3_FilterChainMatch* filter_chain_match_proto, + FilterChain::FilterChainMatch* filter_chain_match) { + auto* destination_port = + envoy_config_listener_v3_FilterChainMatch_destination_port( + filter_chain_match_proto); + if (destination_port != nullptr) { + filter_chain_match->destination_port = + google_protobuf_UInt32Value_value(destination_port); + } + size_t size = 0; + auto* prefix_ranges = envoy_config_listener_v3_FilterChainMatch_prefix_ranges( + filter_chain_match_proto, &size); + filter_chain_match->prefix_ranges.reserve(size); + for (size_t i = 0; i < size; i++) { + XdsListenerResource::FilterChainMap::CidrRange cidr_range; + grpc_error_handle error = CidrRangeParse(prefix_ranges[i], &cidr_range); + if (error != GRPC_ERROR_NONE) return error; + filter_chain_match->prefix_ranges.push_back(cidr_range); + } + filter_chain_match->source_type = + static_cast( + envoy_config_listener_v3_FilterChainMatch_source_type( + filter_chain_match_proto)); + auto* source_prefix_ranges = + envoy_config_listener_v3_FilterChainMatch_source_prefix_ranges( + filter_chain_match_proto, &size); + filter_chain_match->source_prefix_ranges.reserve(size); + for (size_t i = 0; i < size; i++) { + XdsListenerResource::FilterChainMap::CidrRange cidr_range; + grpc_error_handle error = + CidrRangeParse(source_prefix_ranges[i], &cidr_range); + if (error != GRPC_ERROR_NONE) return error; + filter_chain_match->source_prefix_ranges.push_back(cidr_range); + } + auto* source_ports = envoy_config_listener_v3_FilterChainMatch_source_ports( + filter_chain_match_proto, &size); + filter_chain_match->source_ports.reserve(size); + for (size_t i = 0; i < size; i++) { + filter_chain_match->source_ports.push_back(source_ports[i]); + } + auto* server_names = envoy_config_listener_v3_FilterChainMatch_server_names( + filter_chain_match_proto, &size); + for (size_t i = 0; i < size; i++) { + filter_chain_match->server_names.push_back( + UpbStringToStdString(server_names[i])); + } + filter_chain_match->transport_protocol = UpbStringToStdString( + envoy_config_listener_v3_FilterChainMatch_transport_protocol( + filter_chain_match_proto)); + auto* application_protocols = + envoy_config_listener_v3_FilterChainMatch_application_protocols( + filter_chain_match_proto, &size); + for (size_t i = 0; i < size; i++) { + filter_chain_match->application_protocols.push_back( + UpbStringToStdString(application_protocols[i])); + } + return GRPC_ERROR_NONE; +} + +grpc_error_handle FilterChainParse( + const XdsEncodingContext& context, + const envoy_config_listener_v3_FilterChain* filter_chain_proto, bool is_v2, + FilterChain* filter_chain) { + std::vector errors; + auto* filter_chain_match = + envoy_config_listener_v3_FilterChain_filter_chain_match( + filter_chain_proto); + if (filter_chain_match != nullptr) { + grpc_error_handle error = FilterChainMatchParse( + filter_chain_match, &filter_chain->filter_chain_match); + if (error != GRPC_ERROR_NONE) errors.push_back(error); + } + filter_chain->filter_chain_data = + std::make_shared(); + // Parse the filters list. Currently we only support HttpConnectionManager. + size_t size = 0; + auto* filters = + envoy_config_listener_v3_FilterChain_filters(filter_chain_proto, &size); + if (size != 1) { + errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "FilterChain should have exactly one filter: HttpConnectionManager; no " + "other filter is supported at the moment")); + } else { + auto* typed_config = + envoy_config_listener_v3_Filter_typed_config(filters[0]); + if (typed_config == nullptr) { + errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "No typed_config found in filter.")); + } else { + absl::string_view type_url = + UpbStringToAbsl(google_protobuf_Any_type_url(typed_config)); + if (type_url != + "type.googleapis.com/" + "envoy.extensions.filters.network.http_connection_manager.v3." + "HttpConnectionManager") { + errors.push_back(GRPC_ERROR_CREATE_FROM_CPP_STRING( + absl::StrCat("Unsupported filter type ", type_url))); + } else { + const upb_strview encoded_http_connection_manager = + google_protobuf_Any_value(typed_config); + const auto* http_connection_manager = + envoy_extensions_filters_network_http_connection_manager_v3_HttpConnectionManager_parse( + encoded_http_connection_manager.data, + encoded_http_connection_manager.size, context.arena); + if (http_connection_manager == nullptr) { + errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Could not parse HttpConnectionManager config from filter " + "typed_config")); + } else { + grpc_error_handle error = HttpConnectionManagerParse( + false /* is_client */, context, http_connection_manager, is_v2, + &filter_chain->filter_chain_data->http_connection_manager); + if (error != GRPC_ERROR_NONE) errors.push_back(error); + } + } + } + } + auto* transport_socket = + envoy_config_listener_v3_FilterChain_transport_socket(filter_chain_proto); + if (transport_socket != nullptr) { + grpc_error_handle error = DownstreamTlsContextParse( + context, transport_socket, + &filter_chain->filter_chain_data->downstream_tls_context); + if (error != GRPC_ERROR_NONE) errors.push_back(error); + } + return GRPC_ERROR_CREATE_FROM_VECTOR("Error parsing FilterChain", &errors); +} + +grpc_error_handle AddressParse( + const envoy_config_core_v3_Address* address_proto, std::string* address) { + const auto* socket_address = + envoy_config_core_v3_Address_socket_address(address_proto); + if (socket_address == nullptr) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Address does not have socket_address"); + } + if (envoy_config_core_v3_SocketAddress_protocol(socket_address) != + envoy_config_core_v3_SocketAddress_TCP) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "SocketAddress protocol is not TCP"); + } + uint32_t port = envoy_config_core_v3_SocketAddress_port_value(socket_address); + if (port > 65535) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Invalid port"); + } + *address = JoinHostPort( + UpbStringToAbsl( + envoy_config_core_v3_SocketAddress_address(socket_address)), + port); + return GRPC_ERROR_NONE; +} + +// An intermediate map for filter chains that we create to validate the list of +// filter chains received from the control plane and to finally create +// XdsListenerResource::FilterChainMap +struct InternalFilterChainMap { + using SourceIpMap = + std::map; + using ConnectionSourceTypesArray = std::array; + struct DestinationIp { + absl::optional prefix_range; + bool transport_protocol_raw_buffer_provided = false; + ConnectionSourceTypesArray source_types_array; + }; + using DestinationIpMap = std::map; + DestinationIpMap destination_ip_map; +}; + +grpc_error_handle AddFilterChainDataForSourcePort( + const FilterChain& filter_chain, + XdsListenerResource::FilterChainMap::SourcePortsMap* ports_map, + uint32_t port) { + auto insert_result = ports_map->emplace( + port, XdsListenerResource::FilterChainMap::FilterChainDataSharedPtr{ + filter_chain.filter_chain_data}); + if (!insert_result.second) { + return GRPC_ERROR_CREATE_FROM_CPP_STRING(absl::StrCat( + "Duplicate matching rules detected when adding filter chain: ", + filter_chain.filter_chain_match.ToString())); + } + return GRPC_ERROR_NONE; +} + +grpc_error_handle AddFilterChainDataForSourcePorts( + const FilterChain& filter_chain, + XdsListenerResource::FilterChainMap::SourcePortsMap* ports_map) { + if (filter_chain.filter_chain_match.source_ports.empty()) { + return AddFilterChainDataForSourcePort(filter_chain, ports_map, 0); + } else { + for (uint32_t port : filter_chain.filter_chain_match.source_ports) { + grpc_error_handle error = + AddFilterChainDataForSourcePort(filter_chain, ports_map, port); + if (error != GRPC_ERROR_NONE) return error; + } + } + return GRPC_ERROR_NONE; +} + +grpc_error_handle AddFilterChainDataForSourceIpRange( + const FilterChain& filter_chain, + InternalFilterChainMap::SourceIpMap* source_ip_map) { + if (filter_chain.filter_chain_match.source_prefix_ranges.empty()) { + auto insert_result = source_ip_map->emplace( + "", XdsListenerResource::FilterChainMap::SourceIp()); + return AddFilterChainDataForSourcePorts( + filter_chain, &insert_result.first->second.ports_map); + } else { + for (const auto& prefix_range : + filter_chain.filter_chain_match.source_prefix_ranges) { + auto insert_result = source_ip_map->emplace( + absl::StrCat(grpc_sockaddr_to_string(&prefix_range.address, false), + "/", prefix_range.prefix_len), + XdsListenerResource::FilterChainMap::SourceIp()); + if (insert_result.second) { + insert_result.first->second.prefix_range.emplace(prefix_range); + } + grpc_error_handle error = AddFilterChainDataForSourcePorts( + filter_chain, &insert_result.first->second.ports_map); + if (error != GRPC_ERROR_NONE) return error; + } + } + return GRPC_ERROR_NONE; +} + +grpc_error_handle AddFilterChainDataForSourceType( + const FilterChain& filter_chain, + InternalFilterChainMap::DestinationIp* destination_ip) { + GPR_ASSERT(static_cast( + filter_chain.filter_chain_match.source_type) < 3); + return AddFilterChainDataForSourceIpRange( + filter_chain, &destination_ip->source_types_array[static_cast( + filter_chain.filter_chain_match.source_type)]); +} + +grpc_error_handle AddFilterChainDataForApplicationProtocols( + const FilterChain& filter_chain, + InternalFilterChainMap::DestinationIp* destination_ip) { + // Only allow filter chains that do not mention application protocols + if (!filter_chain.filter_chain_match.application_protocols.empty()) { + return GRPC_ERROR_NONE; + } + return AddFilterChainDataForSourceType(filter_chain, destination_ip); +} + +grpc_error_handle AddFilterChainDataForTransportProtocol( + const FilterChain& filter_chain, + InternalFilterChainMap::DestinationIp* destination_ip) { + const std::string& transport_protocol = + filter_chain.filter_chain_match.transport_protocol; + // Only allow filter chains with no transport protocol or "raw_buffer" + if (!transport_protocol.empty() && transport_protocol != "raw_buffer") { + return GRPC_ERROR_NONE; + } + // If for this configuration, we've already seen filter chains that mention + // the transport protocol as "raw_buffer", we will never match filter chains + // that do not mention it. + if (destination_ip->transport_protocol_raw_buffer_provided && + transport_protocol.empty()) { + return GRPC_ERROR_NONE; + } + if (!transport_protocol.empty() && + !destination_ip->transport_protocol_raw_buffer_provided) { + destination_ip->transport_protocol_raw_buffer_provided = true; + // Clear out the previous entries if any since those entries did not mention + // "raw_buffer" + destination_ip->source_types_array = + InternalFilterChainMap::ConnectionSourceTypesArray(); + } + return AddFilterChainDataForApplicationProtocols(filter_chain, + destination_ip); +} + +grpc_error_handle AddFilterChainDataForServerNames( + const FilterChain& filter_chain, + InternalFilterChainMap::DestinationIp* destination_ip) { + // Don't continue adding filter chains with server names mentioned + if (!filter_chain.filter_chain_match.server_names.empty()) { + return GRPC_ERROR_NONE; + } + return AddFilterChainDataForTransportProtocol(filter_chain, destination_ip); +} + +grpc_error_handle AddFilterChainDataForDestinationIpRange( + const FilterChain& filter_chain, + InternalFilterChainMap::DestinationIpMap* destination_ip_map) { + if (filter_chain.filter_chain_match.prefix_ranges.empty()) { + auto insert_result = destination_ip_map->emplace( + "", InternalFilterChainMap::DestinationIp()); + return AddFilterChainDataForServerNames(filter_chain, + &insert_result.first->second); + } else { + for (const auto& prefix_range : + filter_chain.filter_chain_match.prefix_ranges) { + auto insert_result = destination_ip_map->emplace( + absl::StrCat(grpc_sockaddr_to_string(&prefix_range.address, false), + "/", prefix_range.prefix_len), + InternalFilterChainMap::DestinationIp()); + if (insert_result.second) { + insert_result.first->second.prefix_range.emplace(prefix_range); + } + grpc_error_handle error = AddFilterChainDataForServerNames( + filter_chain, &insert_result.first->second); + if (error != GRPC_ERROR_NONE) return error; + } + } + return GRPC_ERROR_NONE; +} + +XdsListenerResource::FilterChainMap BuildFromInternalFilterChainMap( + InternalFilterChainMap* internal_filter_chain_map) { + XdsListenerResource::FilterChainMap filter_chain_map; + for (auto& destination_ip_pair : + internal_filter_chain_map->destination_ip_map) { + XdsListenerResource::FilterChainMap::DestinationIp destination_ip; + destination_ip.prefix_range = destination_ip_pair.second.prefix_range; + for (int i = 0; i < 3; i++) { + auto& source_ip_map = destination_ip_pair.second.source_types_array[i]; + for (auto& source_ip_pair : source_ip_map) { + destination_ip.source_types_array[i].push_back( + std::move(source_ip_pair.second)); + } + } + filter_chain_map.destination_ip_vector.push_back(std::move(destination_ip)); + } + return filter_chain_map; +} + +grpc_error_handle BuildFilterChainMap( + const std::vector& filter_chains, + XdsListenerResource::FilterChainMap* filter_chain_map) { + InternalFilterChainMap internal_filter_chain_map; + for (const auto& filter_chain : filter_chains) { + // Discard filter chain entries that specify destination port + if (filter_chain.filter_chain_match.destination_port != 0) continue; + grpc_error_handle error = AddFilterChainDataForDestinationIpRange( + filter_chain, &internal_filter_chain_map.destination_ip_map); + if (error != GRPC_ERROR_NONE) return error; + } + *filter_chain_map = + BuildFromInternalFilterChainMap(&internal_filter_chain_map); + return GRPC_ERROR_NONE; +} + +grpc_error_handle LdsResourceParseServer( + const XdsEncodingContext& context, + const envoy_config_listener_v3_Listener* listener, bool is_v2, + XdsListenerResource* lds_update) { + lds_update->type = XdsListenerResource::ListenerType::kTcpListener; + grpc_error_handle error = + AddressParse(envoy_config_listener_v3_Listener_address(listener), + &lds_update->address); + if (error != GRPC_ERROR_NONE) return error; + const auto* use_original_dst = + envoy_config_listener_v3_Listener_use_original_dst(listener); + if (use_original_dst != nullptr) { + if (google_protobuf_BoolValue_value(use_original_dst)) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Field \'use_original_dst\' is not supported."); + } + } + size_t size = 0; + auto* filter_chains = + envoy_config_listener_v3_Listener_filter_chains(listener, &size); + std::vector parsed_filter_chains; + parsed_filter_chains.reserve(size); + for (size_t i = 0; i < size; i++) { + FilterChain filter_chain; + error = FilterChainParse(context, filter_chains[i], is_v2, &filter_chain); + if (error != GRPC_ERROR_NONE) return error; + parsed_filter_chains.push_back(std::move(filter_chain)); + } + error = + BuildFilterChainMap(parsed_filter_chains, &lds_update->filter_chain_map); + if (error != GRPC_ERROR_NONE) return error; + auto* default_filter_chain = + envoy_config_listener_v3_Listener_default_filter_chain(listener); + if (default_filter_chain != nullptr) { + FilterChain filter_chain; + error = + FilterChainParse(context, default_filter_chain, is_v2, &filter_chain); + if (error != GRPC_ERROR_NONE) return error; + if (filter_chain.filter_chain_data != nullptr) { + lds_update->default_filter_chain = + std::move(*filter_chain.filter_chain_data); + } + } + if (size == 0 && default_filter_chain == nullptr) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("No filter chain provided."); + } + return GRPC_ERROR_NONE; +} + +grpc_error_handle LdsResourceParse( + const XdsEncodingContext& context, + const envoy_config_listener_v3_Listener* listener, bool is_v2, + XdsListenerResource* lds_update) { + // Check whether it's a client or server listener. + const envoy_config_listener_v3_ApiListener* api_listener = + envoy_config_listener_v3_Listener_api_listener(listener); + const envoy_config_core_v3_Address* address = + envoy_config_listener_v3_Listener_address(listener); + if (api_listener != nullptr && address != nullptr) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Listener has both address and ApiListener"); + } + if (api_listener == nullptr && address == nullptr) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Listener has neither address nor ApiListener"); + } + // Validate Listener fields. + grpc_error_handle error = GRPC_ERROR_NONE; + if (api_listener != nullptr) { + error = LdsResourceParseClient(context, api_listener, is_v2, lds_update); + } else { + error = LdsResourceParseServer(context, listener, is_v2, lds_update); + } + return error; +} + +void MaybeLogListener(const XdsEncodingContext& context, + const envoy_config_listener_v3_Listener* listener) { + if (GRPC_TRACE_FLAG_ENABLED(*context.tracer) && + gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) { + const upb_msgdef* msg_type = + envoy_config_listener_v3_Listener_getmsgdef(context.symtab); + char buf[10240]; + upb_text_encode(listener, msg_type, nullptr, 0, buf, sizeof(buf)); + gpr_log(GPR_DEBUG, "[xds_client %p] Listener: %s", context.client, buf); + } +} + +} // namespace + +absl::StatusOr XdsListenerResourceType::Decode( + const XdsEncodingContext& context, absl::string_view serialized_resource, + bool is_v2) const { + // Parse serialized proto. + auto* resource = envoy_config_listener_v3_Listener_parse( + serialized_resource.data(), serialized_resource.size(), context.arena); + if (resource == nullptr) { + return absl::InvalidArgumentError("Can't parse Listener resource."); + } + MaybeLogListener(context, resource); + // Validate resource. + DecodeResult result; + result.name = + UpbStringToStdString(envoy_config_listener_v3_Listener_name(resource)); + auto listener_data = absl::make_unique(); + grpc_error_handle error = + LdsResourceParse(context, resource, is_v2, &listener_data->resource); + if (error != GRPC_ERROR_NONE) { + result.resource = absl::InvalidArgumentError(grpc_error_std_string(error)); + GRPC_ERROR_UNREF(error); + } else { + result.resource = std::move(listener_data); + } + return std::move(result); +} + +} // namespace grpc_core diff --git a/src/core/ext/xds/xds_listener.h b/src/core/ext/xds/xds_listener.h new file mode 100644 index 00000000000..d701de66154 --- /dev/null +++ b/src/core/ext/xds/xds_listener.h @@ -0,0 +1,210 @@ +// +// Copyright 2018 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef GRPC_CORE_EXT_XDS_XDS_LISTENER_H +#define GRPC_CORE_EXT_XDS_XDS_LISTENER_H + +#include + +#include +#include +#include +#include + +#include "absl/status/statusor.h" +#include "absl/strings/string_view.h" +#include "absl/types/optional.h" + +#include "src/core/ext/xds/xds_common_types.h" +#include "src/core/ext/xds/xds_http_filters.h" +#include "src/core/ext/xds/xds_route_config.h" + +namespace grpc_core { + +// TODO(roth): When we can use absl::variant<>, consider using that +// here, to enforce the fact that only one of the two fields can be set. +struct XdsListenerResource { + struct DownstreamTlsContext { + CommonTlsContext common_tls_context; + bool require_client_certificate = false; + + bool operator==(const DownstreamTlsContext& other) const { + return common_tls_context == other.common_tls_context && + require_client_certificate == other.require_client_certificate; + } + + std::string ToString() const; + bool Empty() const; + }; + + enum class ListenerType { + kTcpListener = 0, + kHttpApiListener, + } type; + + struct HttpConnectionManager { + // The name to use in the RDS request. + std::string route_config_name; + // Storing the Http Connection Manager Common Http Protocol Option + // max_stream_duration + Duration http_max_stream_duration; + // The RouteConfiguration to use for this listener. + // Present only if it is inlined in the LDS response. + absl::optional rds_update; + + struct HttpFilter { + std::string name; + XdsHttpFilterImpl::FilterConfig config; + + bool operator==(const HttpFilter& other) const { + return name == other.name && config == other.config; + } + + std::string ToString() const; + }; + std::vector http_filters; + + bool operator==(const HttpConnectionManager& other) const { + return route_config_name == other.route_config_name && + http_max_stream_duration == other.http_max_stream_duration && + rds_update == other.rds_update && + http_filters == other.http_filters; + } + + std::string ToString() const; + }; + + // Populated for type=kHttpApiListener. + HttpConnectionManager http_connection_manager; + + // Populated for type=kTcpListener. + // host:port listening_address set when type is kTcpListener + std::string address; + + struct FilterChainData { + DownstreamTlsContext downstream_tls_context; + // This is in principle the filter list. + // We currently require exactly one filter, which is the HCM. + HttpConnectionManager http_connection_manager; + + bool operator==(const FilterChainData& other) const { + return downstream_tls_context == other.downstream_tls_context && + http_connection_manager == other.http_connection_manager; + } + + std::string ToString() const; + }; + + // A multi-level map used to determine which filter chain to use for a given + // incoming connection. Determining the right filter chain for a given + // connection checks the following properties, in order: + // - destination port (never matched, so not present in map) + // - destination IP address + // - server name (never matched, so not present in map) + // - transport protocol (allows only "raw_buffer" or unset, prefers the + // former, so only one of those two types is present in map) + // - application protocol (never matched, so not present in map) + // - connection source type (any, local or external) + // - source IP address + // - source port + // https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/listener/v3/listener_components.proto#config-listener-v3-filterchainmatch + // for more details + struct FilterChainMap { + struct FilterChainDataSharedPtr { + std::shared_ptr data; + bool operator==(const FilterChainDataSharedPtr& other) const { + return *data == *other.data; + } + }; + struct CidrRange { + grpc_resolved_address address; + uint32_t prefix_len; + + bool operator==(const CidrRange& other) const { + return memcmp(&address, &other.address, sizeof(address)) == 0 && + prefix_len == other.prefix_len; + } + + std::string ToString() const; + }; + using SourcePortsMap = std::map; + struct SourceIp { + absl::optional prefix_range; + SourcePortsMap ports_map; + + bool operator==(const SourceIp& other) const { + return prefix_range == other.prefix_range && + ports_map == other.ports_map; + } + }; + using SourceIpVector = std::vector; + enum class ConnectionSourceType { kAny = 0, kSameIpOrLoopback, kExternal }; + using ConnectionSourceTypesArray = std::array; + struct DestinationIp { + absl::optional prefix_range; + // We always fail match on server name, so those filter chains are not + // included here. + ConnectionSourceTypesArray source_types_array; + + bool operator==(const DestinationIp& other) const { + return prefix_range == other.prefix_range && + source_types_array == other.source_types_array; + } + }; + // We always fail match on destination ports map + using DestinationIpVector = std::vector; + DestinationIpVector destination_ip_vector; + + bool operator==(const FilterChainMap& other) const { + return destination_ip_vector == other.destination_ip_vector; + } + + std::string ToString() const; + } filter_chain_map; + + absl::optional default_filter_chain; + + bool operator==(const XdsListenerResource& other) const { + return http_connection_manager == other.http_connection_manager && + address == other.address && + filter_chain_map == other.filter_chain_map && + default_filter_chain == other.default_filter_chain; + } + + std::string ToString() const; +}; + +class XdsListenerResourceType : public XdsResourceType { + public: + struct ListenerData : public ResourceData { + XdsListenerResource resource; + }; + + absl::string_view type_url() const override { + return "envoy.config.listener.v3.Listener"; + } + absl::string_view v2_type_url() const override { + return "envoy.api.v2.Listener"; + } + + absl::StatusOr Decode(const XdsEncodingContext& context, + absl::string_view serialized_resource, + bool is_v2) const override; +}; + +} // namespace grpc_core + +#endif // GRPC_CORE_EXT_XDS_XDS_LISTENER_H diff --git a/src/core/ext/xds/xds_resource_type.h b/src/core/ext/xds/xds_resource_type.h new file mode 100644 index 00000000000..d7819832159 --- /dev/null +++ b/src/core/ext/xds/xds_resource_type.h @@ -0,0 +1,77 @@ +// +// Copyright 2021 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include + +#include +#include + +#include "absl/status/statusor.h" +#include "absl/strings/string_view.h" + +#include "src/core/ext/xds/upb_utils.h" + +#ifndef GRPC_CORE_EXT_XDS_XDS_RESOURCE_TYPE_H +#define GRPC_CORE_EXT_XDS_XDS_RESOURCE_TYPE_H + +namespace grpc_core { + +class XdsResourceType { + public: + // A base type for resource data. + // Subclasses will extend this, and their DecodeResults will be + // downcastable to their extended type. + struct ResourceData { + virtual ~ResourceData() = default; + }; + + // Result returned by Decode(). + struct DecodeResult { + std::string name; + absl::StatusOr> resource; + }; + + virtual ~XdsResourceType() = default; + + // Returns v3 resource type. + virtual absl::string_view type_url() const = 0; + + // Returns v2 resource type. + virtual absl::string_view v2_type_url() const = 0; + + // Decodes and validates a serialized resource proto. + // If the resource fails protobuf deserialization, returns non-OK status. + // If the deserialized resource fails validation, returns a DecodeResult + // whose resource field is set to a non-OK status. + // Otherwise, returns a DecodeResult with a valid resource. + virtual absl::StatusOr Decode( + const XdsEncodingContext& context, absl::string_view serialized_resource, + bool is_v2) const = 0; + + // Convenient method for checking if a resource type matches this type. + bool IsType(absl::string_view resource_type, bool* is_v2) const { + if (resource_type == type_url()) return true; + if (resource_type == v2_type_url()) { + if (is_v2 != nullptr) *is_v2 = true; + return true; + } + return false; + } +}; + +} // namespace grpc_core + +#endif // GRPC_CORE_EXT_XDS_XDS_RESOURCE_TYPE_H diff --git a/src/core/ext/xds/xds_route_config.cc b/src/core/ext/xds/xds_route_config.cc new file mode 100644 index 00000000000..a9120b21b8b --- /dev/null +++ b/src/core/ext/xds/xds_route_config.cc @@ -0,0 +1,982 @@ +// +// Copyright 2018 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include + +#include "absl/memory/memory.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" +#include "absl/strings/str_join.h" +#include "absl/strings/str_split.h" +#include "absl/strings/string_view.h" +#include "envoy/config/core/v3/base.upb.h" +#include "envoy/config/route/v3/route.upb.h" +#include "envoy/config/route/v3/route.upbdefs.h" +#include "envoy/config/route/v3/route_components.upb.h" +#include "envoy/config/route/v3/route_components.upbdefs.h" +#include "envoy/type/matcher/v3/regex.upb.h" +#include "envoy/type/matcher/v3/string.upb.h" +#include "envoy/type/v3/percent.upb.h" +#include "envoy/type/v3/range.upb.h" +#include "google/protobuf/any.upb.h" +#include "google/protobuf/wrappers.upb.h" +#include "upb/text_encode.h" +#include "upb/upb.h" +#include "upb/upb.hpp" + +#include "src/core/ext/xds/upb_utils.h" +#include "src/core/ext/xds/xds_api.h" +#include "src/core/ext/xds/xds_common_types.h" +#include "src/core/ext/xds/xds_resource_type.h" +#include "src/core/ext/xds/xds_routing.h" +#include "src/core/lib/gpr/env.h" +#include "src/core/lib/gpr/string.h" +#include "src/core/lib/iomgr/error.h" + +namespace grpc_core { + +// TODO(yashykt): Remove once RBAC is no longer experimental +bool XdsRbacEnabled() { + char* value = gpr_getenv("GRPC_XDS_EXPERIMENTAL_RBAC"); + bool parsed_value; + bool parse_succeeded = gpr_parse_bool_value(value, &parsed_value); + gpr_free(value); + return parse_succeeded && parsed_value; +} + +// +// XdsRouteConfigResource::RetryPolicy +// + +std::string XdsRouteConfigResource::RetryPolicy::RetryBackOff::ToString() + const { + std::vector contents; + contents.push_back( + absl::StrCat("RetryBackOff Base: ", base_interval.ToString())); + contents.push_back( + absl::StrCat("RetryBackOff max: ", max_interval.ToString())); + return absl::StrJoin(contents, ","); +} + +std::string XdsRouteConfigResource::RetryPolicy::ToString() const { + std::vector contents; + contents.push_back(absl::StrFormat("num_retries=%d", num_retries)); + contents.push_back(retry_back_off.ToString()); + return absl::StrCat("{", absl::StrJoin(contents, ","), "}"); +} + +// +// XdsRouteConfigResource::Route::Matchers +// + +std::string XdsRouteConfigResource::Route::Matchers::ToString() const { + std::vector contents; + contents.push_back( + absl::StrFormat("PathMatcher{%s}", path_matcher.ToString())); + for (const HeaderMatcher& header_matcher : header_matchers) { + contents.push_back(header_matcher.ToString()); + } + if (fraction_per_million.has_value()) { + contents.push_back(absl::StrFormat("Fraction Per Million %d", + fraction_per_million.value())); + } + return absl::StrJoin(contents, "\n"); +} + +// +// XdsRouteConfigResource::Route::RouteAction::HashPolicy +// + +XdsRouteConfigResource::Route::RouteAction::HashPolicy::HashPolicy( + const HashPolicy& other) + : type(other.type), + header_name(other.header_name), + regex_substitution(other.regex_substitution) { + if (other.regex != nullptr) { + regex = + absl::make_unique(other.regex->pattern(), other.regex->options()); + } +} + +XdsRouteConfigResource::Route::RouteAction::HashPolicy& +XdsRouteConfigResource::Route::RouteAction::HashPolicy::operator=( + const HashPolicy& other) { + type = other.type; + header_name = other.header_name; + if (other.regex != nullptr) { + regex = + absl::make_unique(other.regex->pattern(), other.regex->options()); + } + regex_substitution = other.regex_substitution; + return *this; +} + +XdsRouteConfigResource::Route::RouteAction::HashPolicy::HashPolicy( + HashPolicy&& other) noexcept + : type(other.type), + header_name(std::move(other.header_name)), + regex(std::move(other.regex)), + regex_substitution(std::move(other.regex_substitution)) {} + +XdsRouteConfigResource::Route::RouteAction::HashPolicy& +XdsRouteConfigResource::Route::RouteAction::HashPolicy::operator=( + HashPolicy&& other) noexcept { + type = other.type; + header_name = std::move(other.header_name); + regex = std::move(other.regex); + regex_substitution = std::move(other.regex_substitution); + return *this; +} + +bool XdsRouteConfigResource::Route::RouteAction::HashPolicy::HashPolicy:: +operator==(const HashPolicy& other) const { + if (type != other.type) return false; + if (type == Type::HEADER) { + if (regex == nullptr) { + if (other.regex != nullptr) return false; + } else { + if (other.regex == nullptr) return false; + return header_name == other.header_name && + regex->pattern() == other.regex->pattern() && + regex_substitution == other.regex_substitution; + } + } + return true; +} + +std::string XdsRouteConfigResource::Route::RouteAction::HashPolicy::ToString() + const { + std::vector contents; + switch (type) { + case Type::HEADER: + contents.push_back("type=HEADER"); + break; + case Type::CHANNEL_ID: + contents.push_back("type=CHANNEL_ID"); + break; + } + contents.push_back( + absl::StrFormat("terminal=%s", terminal ? "true" : "false")); + if (type == Type::HEADER) { + contents.push_back(absl::StrFormat( + "Header %s:/%s/%s", header_name, + (regex == nullptr) ? "" : regex->pattern(), regex_substitution)); + } + return absl::StrCat("{", absl::StrJoin(contents, ", "), "}"); +} + +// +// XdsRouteConfigResource::Route::RouteAction::ClusterWeight +// + +std::string +XdsRouteConfigResource::Route::RouteAction::ClusterWeight::ToString() const { + std::vector contents; + contents.push_back(absl::StrCat("cluster=", name)); + contents.push_back(absl::StrCat("weight=", weight)); + if (!typed_per_filter_config.empty()) { + std::vector parts; + for (const auto& p : typed_per_filter_config) { + const std::string& key = p.first; + const auto& config = p.second; + parts.push_back(absl::StrCat(key, "=", config.ToString())); + } + contents.push_back(absl::StrCat("typed_per_filter_config={", + absl::StrJoin(parts, ", "), "}")); + } + return absl::StrCat("{", absl::StrJoin(contents, ", "), "}"); +} + +// +// XdsRouteConfigResource::Route::RouteAction +// + +std::string XdsRouteConfigResource::Route::RouteAction::ToString() const { + std::vector contents; + for (const HashPolicy& hash_policy : hash_policies) { + contents.push_back(absl::StrCat("hash_policy=", hash_policy.ToString())); + } + if (retry_policy.has_value()) { + contents.push_back(absl::StrCat("retry_policy=", retry_policy->ToString())); + } + if (!cluster_name.empty()) { + contents.push_back(absl::StrFormat("Cluster name: %s", cluster_name)); + } + for (const ClusterWeight& cluster_weight : weighted_clusters) { + contents.push_back(cluster_weight.ToString()); + } + if (max_stream_duration.has_value()) { + contents.push_back(max_stream_duration->ToString()); + } + return absl::StrCat("{", absl::StrJoin(contents, ", "), "}"); +} + +// +// XdsRouteConfigResource::Route +// + +std::string XdsRouteConfigResource::Route::ToString() const { + std::vector contents; + contents.push_back(matchers.ToString()); + auto* route_action = + absl::get_if(&action); + if (route_action != nullptr) { + contents.push_back(absl::StrCat("route=", route_action->ToString())); + } else if (absl::holds_alternative< + XdsRouteConfigResource::Route::NonForwardingAction>(action)) { + contents.push_back("non_forwarding_action={}"); + } else { + contents.push_back("unknown_action={}"); + } + if (!typed_per_filter_config.empty()) { + contents.push_back("typed_per_filter_config={"); + for (const auto& p : typed_per_filter_config) { + const std::string& name = p.first; + const auto& config = p.second; + contents.push_back(absl::StrCat(" ", name, "=", config.ToString())); + } + contents.push_back("}"); + } + return absl::StrJoin(contents, "\n"); +} + +// +// XdsRouteConfigResource +// + +std::string XdsRouteConfigResource::ToString() const { + std::vector vhosts; + for (const VirtualHost& vhost : virtual_hosts) { + vhosts.push_back( + absl::StrCat("vhost={\n" + " domains=[", + absl::StrJoin(vhost.domains, ", "), + "]\n" + " routes=[\n")); + for (const XdsRouteConfigResource::Route& route : vhost.routes) { + vhosts.push_back(" {\n"); + vhosts.push_back(route.ToString()); + vhosts.push_back("\n }\n"); + } + vhosts.push_back(" ]\n"); + vhosts.push_back(" typed_per_filter_config={\n"); + for (const auto& p : vhost.typed_per_filter_config) { + const std::string& name = p.first; + const auto& config = p.second; + vhosts.push_back( + absl::StrCat(" ", name, "=", config.ToString(), "\n")); + } + vhosts.push_back(" }\n"); + vhosts.push_back("]\n"); + } + return absl::StrJoin(vhosts, ""); +} + +namespace { + +grpc_error_handle RoutePathMatchParse( + const envoy_config_route_v3_RouteMatch* match, + XdsRouteConfigResource::Route* route, bool* ignore_route) { + auto* case_sensitive_ptr = + envoy_config_route_v3_RouteMatch_case_sensitive(match); + bool case_sensitive = true; + if (case_sensitive_ptr != nullptr) { + case_sensitive = google_protobuf_BoolValue_value(case_sensitive_ptr); + } + StringMatcher::Type type; + std::string match_string; + if (envoy_config_route_v3_RouteMatch_has_prefix(match)) { + absl::string_view prefix = + UpbStringToAbsl(envoy_config_route_v3_RouteMatch_prefix(match)); + // Empty prefix "" is accepted. + if (!prefix.empty()) { + // Prefix "/" is accepted. + if (prefix[0] != '/') { + // Prefix which does not start with a / will never match anything, so + // ignore this route. + *ignore_route = true; + return GRPC_ERROR_NONE; + } + std::vector prefix_elements = + absl::StrSplit(prefix.substr(1), absl::MaxSplits('/', 2)); + if (prefix_elements.size() > 2) { + // Prefix cannot have more than 2 slashes. + *ignore_route = true; + return GRPC_ERROR_NONE; + } else if (prefix_elements.size() == 2 && prefix_elements[0].empty()) { + // Prefix contains empty string between the 2 slashes + *ignore_route = true; + return GRPC_ERROR_NONE; + } + } + type = StringMatcher::Type::kPrefix; + match_string = std::string(prefix); + } else if (envoy_config_route_v3_RouteMatch_has_path(match)) { + absl::string_view path = + UpbStringToAbsl(envoy_config_route_v3_RouteMatch_path(match)); + if (path.empty()) { + // Path that is empty will never match anything, so ignore this route. + *ignore_route = true; + return GRPC_ERROR_NONE; + } + if (path[0] != '/') { + // Path which does not start with a / will never match anything, so + // ignore this route. + *ignore_route = true; + return GRPC_ERROR_NONE; + } + std::vector path_elements = + absl::StrSplit(path.substr(1), absl::MaxSplits('/', 2)); + if (path_elements.size() != 2) { + // Path not in the required format of /service/method will never match + // anything, so ignore this route. + *ignore_route = true; + return GRPC_ERROR_NONE; + } else if (path_elements[0].empty()) { + // Path contains empty service name will never match anything, so ignore + // this route. + *ignore_route = true; + return GRPC_ERROR_NONE; + } else if (path_elements[1].empty()) { + // Path contains empty method name will never match anything, so ignore + // this route. + *ignore_route = true; + return GRPC_ERROR_NONE; + } + type = StringMatcher::Type::kExact; + match_string = std::string(path); + } else if (envoy_config_route_v3_RouteMatch_has_safe_regex(match)) { + const envoy_type_matcher_v3_RegexMatcher* regex_matcher = + envoy_config_route_v3_RouteMatch_safe_regex(match); + GPR_ASSERT(regex_matcher != nullptr); + type = StringMatcher::Type::kSafeRegex; + match_string = UpbStringToStdString( + envoy_type_matcher_v3_RegexMatcher_regex(regex_matcher)); + } else { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Invalid route path specifier specified."); + } + absl::StatusOr string_matcher = + StringMatcher::Create(type, match_string, case_sensitive); + if (!string_matcher.ok()) { + return GRPC_ERROR_CREATE_FROM_CPP_STRING( + absl::StrCat("path matcher: ", string_matcher.status().message())); + } + route->matchers.path_matcher = std::move(string_matcher.value()); + return GRPC_ERROR_NONE; +} + +grpc_error_handle RouteHeaderMatchersParse( + const envoy_config_route_v3_RouteMatch* match, + XdsRouteConfigResource::Route* route) { + size_t size; + const envoy_config_route_v3_HeaderMatcher* const* headers = + envoy_config_route_v3_RouteMatch_headers(match, &size); + for (size_t i = 0; i < size; ++i) { + const envoy_config_route_v3_HeaderMatcher* header = headers[i]; + const std::string name = + UpbStringToStdString(envoy_config_route_v3_HeaderMatcher_name(header)); + HeaderMatcher::Type type; + std::string match_string; + int64_t range_start = 0; + int64_t range_end = 0; + bool present_match = false; + if (envoy_config_route_v3_HeaderMatcher_has_exact_match(header)) { + type = HeaderMatcher::Type::kExact; + match_string = UpbStringToStdString( + envoy_config_route_v3_HeaderMatcher_exact_match(header)); + } else if (envoy_config_route_v3_HeaderMatcher_has_safe_regex_match( + header)) { + const envoy_type_matcher_v3_RegexMatcher* regex_matcher = + envoy_config_route_v3_HeaderMatcher_safe_regex_match(header); + GPR_ASSERT(regex_matcher != nullptr); + type = HeaderMatcher::Type::kSafeRegex; + match_string = UpbStringToStdString( + envoy_type_matcher_v3_RegexMatcher_regex(regex_matcher)); + } else if (envoy_config_route_v3_HeaderMatcher_has_range_match(header)) { + type = HeaderMatcher::Type::kRange; + const envoy_type_v3_Int64Range* range_matcher = + envoy_config_route_v3_HeaderMatcher_range_match(header); + range_start = envoy_type_v3_Int64Range_start(range_matcher); + range_end = envoy_type_v3_Int64Range_end(range_matcher); + } else if (envoy_config_route_v3_HeaderMatcher_has_present_match(header)) { + type = HeaderMatcher::Type::kPresent; + present_match = envoy_config_route_v3_HeaderMatcher_present_match(header); + } else if (envoy_config_route_v3_HeaderMatcher_has_prefix_match(header)) { + type = HeaderMatcher::Type::kPrefix; + match_string = UpbStringToStdString( + envoy_config_route_v3_HeaderMatcher_prefix_match(header)); + } else if (envoy_config_route_v3_HeaderMatcher_has_suffix_match(header)) { + type = HeaderMatcher::Type::kSuffix; + match_string = UpbStringToStdString( + envoy_config_route_v3_HeaderMatcher_suffix_match(header)); + } else if (envoy_config_route_v3_HeaderMatcher_has_contains_match(header)) { + type = HeaderMatcher::Type::kContains; + match_string = UpbStringToStdString( + envoy_config_route_v3_HeaderMatcher_contains_match(header)); + } else { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Invalid route header matcher specified."); + } + bool invert_match = + envoy_config_route_v3_HeaderMatcher_invert_match(header); + absl::StatusOr header_matcher = + HeaderMatcher::Create(name, type, match_string, range_start, range_end, + present_match, invert_match); + if (!header_matcher.ok()) { + return GRPC_ERROR_CREATE_FROM_CPP_STRING( + absl::StrCat("header matcher: ", header_matcher.status().message())); + } + route->matchers.header_matchers.emplace_back( + std::move(header_matcher.value())); + } + return GRPC_ERROR_NONE; +} + +grpc_error_handle RouteRuntimeFractionParse( + const envoy_config_route_v3_RouteMatch* match, + XdsRouteConfigResource::Route* route) { + const envoy_config_core_v3_RuntimeFractionalPercent* runtime_fraction = + envoy_config_route_v3_RouteMatch_runtime_fraction(match); + if (runtime_fraction != nullptr) { + const envoy_type_v3_FractionalPercent* fraction = + envoy_config_core_v3_RuntimeFractionalPercent_default_value( + runtime_fraction); + if (fraction != nullptr) { + uint32_t numerator = envoy_type_v3_FractionalPercent_numerator(fraction); + const auto denominator = + static_cast( + envoy_type_v3_FractionalPercent_denominator(fraction)); + // Normalize to million. + switch (denominator) { + case envoy_type_v3_FractionalPercent_HUNDRED: + numerator *= 10000; + break; + case envoy_type_v3_FractionalPercent_TEN_THOUSAND: + numerator *= 100; + break; + case envoy_type_v3_FractionalPercent_MILLION: + break; + default: + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "Unknown denominator type"); + } + route->matchers.fraction_per_million = numerator; + } + } + return GRPC_ERROR_NONE; +} + +template +grpc_error_handle ParseTypedPerFilterConfig( + const XdsEncodingContext& context, const ParentType* parent, + const EntryType* (*entry_func)(const ParentType*, size_t*), + upb_strview (*key_func)(const EntryType*), + const google_protobuf_Any* (*value_func)(const EntryType*), + XdsRouteConfigResource::TypedPerFilterConfig* typed_per_filter_config) { + size_t filter_it = UPB_MAP_BEGIN; + while (true) { + const auto* filter_entry = entry_func(parent, &filter_it); + if (filter_entry == nullptr) break; + absl::string_view key = UpbStringToAbsl(key_func(filter_entry)); + if (key.empty()) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("empty filter name in map"); + } + const google_protobuf_Any* any = value_func(filter_entry); + GPR_ASSERT(any != nullptr); + absl::string_view filter_type = + UpbStringToAbsl(google_protobuf_Any_type_url(any)); + if (filter_type.empty()) { + return GRPC_ERROR_CREATE_FROM_CPP_STRING( + absl::StrCat("no filter config specified for filter name ", key)); + } + bool is_optional = false; + if (filter_type == + "type.googleapis.com/envoy.config.route.v3.FilterConfig") { + upb_strview any_value = google_protobuf_Any_value(any); + const auto* filter_config = envoy_config_route_v3_FilterConfig_parse( + any_value.data, any_value.size, context.arena); + if (filter_config == nullptr) { + return GRPC_ERROR_CREATE_FROM_CPP_STRING( + absl::StrCat("could not parse FilterConfig wrapper for ", key)); + } + is_optional = + envoy_config_route_v3_FilterConfig_is_optional(filter_config); + any = envoy_config_route_v3_FilterConfig_config(filter_config); + if (any == nullptr) { + if (is_optional) continue; + return GRPC_ERROR_CREATE_FROM_CPP_STRING( + absl::StrCat("no filter config specified for filter name ", key)); + } + } + grpc_error_handle error = + ExtractHttpFilterTypeName(context, any, &filter_type); + if (error != GRPC_ERROR_NONE) return error; + const XdsHttpFilterImpl* filter_impl = + XdsHttpFilterRegistry::GetFilterForType(filter_type); + if (filter_impl == nullptr) { + if (is_optional) continue; + return GRPC_ERROR_CREATE_FROM_CPP_STRING( + absl::StrCat("no filter registered for config type ", filter_type)); + } + absl::StatusOr filter_config = + filter_impl->GenerateFilterConfigOverride( + google_protobuf_Any_value(any), context.arena); + if (!filter_config.ok()) { + return GRPC_ERROR_CREATE_FROM_CPP_STRING(absl::StrCat( + "filter config for type ", filter_type, + " failed to parse: ", filter_config.status().ToString())); + } + (*typed_per_filter_config)[std::string(key)] = std::move(*filter_config); + } + return GRPC_ERROR_NONE; +} + +grpc_error_handle RetryPolicyParse( + const XdsEncodingContext& context, + const envoy_config_route_v3_RetryPolicy* retry_policy, + absl::optional* retry) { + std::vector errors; + XdsRouteConfigResource::RetryPolicy retry_to_return; + auto retry_on = UpbStringToStdString( + envoy_config_route_v3_RetryPolicy_retry_on(retry_policy)); + std::vector codes = absl::StrSplit(retry_on, ','); + for (const auto& code : codes) { + if (code == "cancelled") { + retry_to_return.retry_on.Add(GRPC_STATUS_CANCELLED); + } else if (code == "deadline-exceeded") { + retry_to_return.retry_on.Add(GRPC_STATUS_DEADLINE_EXCEEDED); + } else if (code == "internal") { + retry_to_return.retry_on.Add(GRPC_STATUS_INTERNAL); + } else if (code == "resource-exhausted") { + retry_to_return.retry_on.Add(GRPC_STATUS_RESOURCE_EXHAUSTED); + } else if (code == "unavailable") { + retry_to_return.retry_on.Add(GRPC_STATUS_UNAVAILABLE); + } else { + if (GRPC_TRACE_FLAG_ENABLED(*context.tracer)) { + gpr_log(GPR_INFO, "Unsupported retry_on policy %s.", + std::string(code).c_str()); + } + } + } + const google_protobuf_UInt32Value* num_retries = + envoy_config_route_v3_RetryPolicy_num_retries(retry_policy); + if (num_retries != nullptr) { + uint32_t num_retries_value = google_protobuf_UInt32Value_value(num_retries); + if (num_retries_value == 0) { + errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "RouteAction RetryPolicy num_retries set to invalid value 0.")); + } else { + retry_to_return.num_retries = num_retries_value; + } + } else { + retry_to_return.num_retries = 1; + } + const envoy_config_route_v3_RetryPolicy_RetryBackOff* backoff = + envoy_config_route_v3_RetryPolicy_retry_back_off(retry_policy); + if (backoff != nullptr) { + const google_protobuf_Duration* base_interval = + envoy_config_route_v3_RetryPolicy_RetryBackOff_base_interval(backoff); + if (base_interval == nullptr) { + errors.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "RouteAction RetryPolicy RetryBackoff missing base interval.")); + } else { + retry_to_return.retry_back_off.base_interval = + Duration::Parse(base_interval); + } + const google_protobuf_Duration* max_interval = + envoy_config_route_v3_RetryPolicy_RetryBackOff_max_interval(backoff); + Duration max; + if (max_interval != nullptr) { + max = Duration::Parse(max_interval); + } else { + // if max interval is not set, it is 10x the base, if the value in nanos + // can yield another second, adjust the value in seconds accordingly. + max.seconds = retry_to_return.retry_back_off.base_interval.seconds * 10; + max.nanos = retry_to_return.retry_back_off.base_interval.nanos * 10; + if (max.nanos > 1000000000) { + max.seconds += max.nanos / 1000000000; + max.nanos = max.nanos % 1000000000; + } + } + retry_to_return.retry_back_off.max_interval = max; + } else { + retry_to_return.retry_back_off.base_interval.seconds = 0; + retry_to_return.retry_back_off.base_interval.nanos = 25000000; + retry_to_return.retry_back_off.max_interval.seconds = 0; + retry_to_return.retry_back_off.max_interval.nanos = 250000000; + } + if (errors.empty()) { + *retry = retry_to_return; + return GRPC_ERROR_NONE; + } else { + return GRPC_ERROR_CREATE_FROM_VECTOR("errors parsing retry policy", + &errors); + } +} + +grpc_error_handle RouteActionParse( + const XdsEncodingContext& context, + const envoy_config_route_v3_Route* route_msg, + XdsRouteConfigResource::Route::RouteAction* route, bool* ignore_route) { + const envoy_config_route_v3_RouteAction* route_action = + envoy_config_route_v3_Route_route(route_msg); + // Get the cluster or weighted_clusters in the RouteAction. + if (envoy_config_route_v3_RouteAction_has_cluster(route_action)) { + route->cluster_name = UpbStringToStdString( + envoy_config_route_v3_RouteAction_cluster(route_action)); + if (route->cluster_name.empty()) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "RouteAction cluster contains empty cluster name."); + } + } else if (envoy_config_route_v3_RouteAction_has_weighted_clusters( + route_action)) { + const envoy_config_route_v3_WeightedCluster* weighted_cluster = + envoy_config_route_v3_RouteAction_weighted_clusters(route_action); + uint32_t total_weight = 100; + const google_protobuf_UInt32Value* weight = + envoy_config_route_v3_WeightedCluster_total_weight(weighted_cluster); + if (weight != nullptr) { + total_weight = google_protobuf_UInt32Value_value(weight); + } + size_t clusters_size; + const envoy_config_route_v3_WeightedCluster_ClusterWeight* const* clusters = + envoy_config_route_v3_WeightedCluster_clusters(weighted_cluster, + &clusters_size); + uint32_t sum_of_weights = 0; + for (size_t j = 0; j < clusters_size; ++j) { + const envoy_config_route_v3_WeightedCluster_ClusterWeight* + cluster_weight = clusters[j]; + XdsRouteConfigResource::Route::RouteAction::ClusterWeight cluster; + cluster.name = UpbStringToStdString( + envoy_config_route_v3_WeightedCluster_ClusterWeight_name( + cluster_weight)); + if (cluster.name.empty()) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "RouteAction weighted_cluster cluster contains empty cluster " + "name."); + } + const google_protobuf_UInt32Value* weight = + envoy_config_route_v3_WeightedCluster_ClusterWeight_weight( + cluster_weight); + if (weight == nullptr) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "RouteAction weighted_cluster cluster missing weight"); + } + cluster.weight = google_protobuf_UInt32Value_value(weight); + if (cluster.weight == 0) continue; + sum_of_weights += cluster.weight; + if (context.use_v3) { + grpc_error_handle error = ParseTypedPerFilterConfig< + envoy_config_route_v3_WeightedCluster_ClusterWeight, + envoy_config_route_v3_WeightedCluster_ClusterWeight_TypedPerFilterConfigEntry>( + context, cluster_weight, + envoy_config_route_v3_WeightedCluster_ClusterWeight_typed_per_filter_config_next, + envoy_config_route_v3_WeightedCluster_ClusterWeight_TypedPerFilterConfigEntry_key, + envoy_config_route_v3_WeightedCluster_ClusterWeight_TypedPerFilterConfigEntry_value, + &cluster.typed_per_filter_config); + if (error != GRPC_ERROR_NONE) return error; + } + route->weighted_clusters.emplace_back(std::move(cluster)); + } + if (total_weight != sum_of_weights) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "RouteAction weighted_cluster has incorrect total weight"); + } + if (route->weighted_clusters.empty()) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "RouteAction weighted_cluster has no valid clusters specified."); + } + } else { + // No cluster or weighted_clusters found in RouteAction, ignore this route. + *ignore_route = true; + } + if (!*ignore_route) { + const envoy_config_route_v3_RouteAction_MaxStreamDuration* + max_stream_duration = + envoy_config_route_v3_RouteAction_max_stream_duration(route_action); + if (max_stream_duration != nullptr) { + const google_protobuf_Duration* duration = + envoy_config_route_v3_RouteAction_MaxStreamDuration_grpc_timeout_header_max( + max_stream_duration); + if (duration == nullptr) { + duration = + envoy_config_route_v3_RouteAction_MaxStreamDuration_max_stream_duration( + max_stream_duration); + } + if (duration != nullptr) { + route->max_stream_duration = Duration::Parse(duration); + } + } + } + // Get HashPolicy from RouteAction + size_t size = 0; + const envoy_config_route_v3_RouteAction_HashPolicy* const* hash_policies = + envoy_config_route_v3_RouteAction_hash_policy(route_action, &size); + for (size_t i = 0; i < size; ++i) { + const envoy_config_route_v3_RouteAction_HashPolicy* hash_policy = + hash_policies[i]; + XdsRouteConfigResource::Route::RouteAction::HashPolicy policy; + policy.terminal = + envoy_config_route_v3_RouteAction_HashPolicy_terminal(hash_policy); + const envoy_config_route_v3_RouteAction_HashPolicy_Header* header; + const envoy_config_route_v3_RouteAction_HashPolicy_FilterState* + filter_state; + if ((header = envoy_config_route_v3_RouteAction_HashPolicy_header( + hash_policy)) != nullptr) { + policy.type = + XdsRouteConfigResource::Route::RouteAction::HashPolicy::Type::HEADER; + policy.header_name = UpbStringToStdString( + envoy_config_route_v3_RouteAction_HashPolicy_Header_header_name( + header)); + const struct envoy_type_matcher_v3_RegexMatchAndSubstitute* + regex_rewrite = + envoy_config_route_v3_RouteAction_HashPolicy_Header_regex_rewrite( + header); + if (regex_rewrite != nullptr) { + const envoy_type_matcher_v3_RegexMatcher* regex_matcher = + envoy_type_matcher_v3_RegexMatchAndSubstitute_pattern( + regex_rewrite); + if (regex_matcher == nullptr) { + gpr_log( + GPR_DEBUG, + "RouteAction HashPolicy contains policy specifier Header with " + "RegexMatchAndSubstitution but RegexMatcher pattern is " + "missing"); + continue; + } + RE2::Options options; + policy.regex = absl::make_unique( + UpbStringToStdString( + envoy_type_matcher_v3_RegexMatcher_regex(regex_matcher)), + options); + if (!policy.regex->ok()) { + gpr_log( + GPR_DEBUG, + "RouteAction HashPolicy contains policy specifier Header with " + "RegexMatchAndSubstitution but RegexMatcher pattern does not " + "compile"); + continue; + } + policy.regex_substitution = UpbStringToStdString( + envoy_type_matcher_v3_RegexMatchAndSubstitute_substitution( + regex_rewrite)); + } + } else if ((filter_state = + envoy_config_route_v3_RouteAction_HashPolicy_filter_state( + hash_policy)) != nullptr) { + std::string key = UpbStringToStdString( + envoy_config_route_v3_RouteAction_HashPolicy_FilterState_key( + filter_state)); + if (key == "io.grpc.channel_id") { + policy.type = XdsRouteConfigResource::Route::RouteAction::HashPolicy:: + Type::CHANNEL_ID; + } else { + gpr_log(GPR_DEBUG, + "RouteAction HashPolicy contains policy specifier " + "FilterState but " + "key is not io.grpc.channel_id."); + continue; + } + } else { + gpr_log(GPR_DEBUG, + "RouteAction HashPolicy contains unsupported policy specifier."); + continue; + } + route->hash_policies.emplace_back(std::move(policy)); + } + // Get retry policy + const envoy_config_route_v3_RetryPolicy* retry_policy = + envoy_config_route_v3_RouteAction_retry_policy(route_action); + if (retry_policy != nullptr) { + absl::optional retry; + grpc_error_handle error = RetryPolicyParse(context, retry_policy, &retry); + if (error != GRPC_ERROR_NONE) return error; + route->retry_policy = retry; + } + return GRPC_ERROR_NONE; +} + +} // namespace + +grpc_error_handle XdsRouteConfigResource::Parse( + const XdsEncodingContext& context, + const envoy_config_route_v3_RouteConfiguration* route_config, + XdsRouteConfigResource* rds_update) { + // Get the virtual hosts. + size_t num_virtual_hosts; + const envoy_config_route_v3_VirtualHost* const* virtual_hosts = + envoy_config_route_v3_RouteConfiguration_virtual_hosts( + route_config, &num_virtual_hosts); + for (size_t i = 0; i < num_virtual_hosts; ++i) { + rds_update->virtual_hosts.emplace_back(); + XdsRouteConfigResource::VirtualHost& vhost = + rds_update->virtual_hosts.back(); + // Parse domains. + size_t domain_size; + upb_strview const* domains = envoy_config_route_v3_VirtualHost_domains( + virtual_hosts[i], &domain_size); + for (size_t j = 0; j < domain_size; ++j) { + std::string domain_pattern = UpbStringToStdString(domains[j]); + if (!XdsRouting::IsValidDomainPattern(domain_pattern)) { + return GRPC_ERROR_CREATE_FROM_CPP_STRING( + absl::StrCat("Invalid domain pattern \"", domain_pattern, "\".")); + } + vhost.domains.emplace_back(std::move(domain_pattern)); + } + if (vhost.domains.empty()) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("VirtualHost has no domains"); + } + // Parse typed_per_filter_config. + if (context.use_v3) { + grpc_error_handle error = ParseTypedPerFilterConfig< + envoy_config_route_v3_VirtualHost, + envoy_config_route_v3_VirtualHost_TypedPerFilterConfigEntry>( + context, virtual_hosts[i], + envoy_config_route_v3_VirtualHost_typed_per_filter_config_next, + envoy_config_route_v3_VirtualHost_TypedPerFilterConfigEntry_key, + envoy_config_route_v3_VirtualHost_TypedPerFilterConfigEntry_value, + &vhost.typed_per_filter_config); + if (error != GRPC_ERROR_NONE) return error; + } + // Parse retry policy. + absl::optional + virtual_host_retry_policy; + const envoy_config_route_v3_RetryPolicy* retry_policy = + envoy_config_route_v3_VirtualHost_retry_policy(virtual_hosts[i]); + if (retry_policy != nullptr) { + grpc_error_handle error = + RetryPolicyParse(context, retry_policy, &virtual_host_retry_policy); + if (error != GRPC_ERROR_NONE) return error; + } + // Parse routes. + size_t num_routes; + const envoy_config_route_v3_Route* const* routes = + envoy_config_route_v3_VirtualHost_routes(virtual_hosts[i], &num_routes); + if (num_routes < 1) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING( + "No route found in the virtual host."); + } + // Loop over the whole list of routes + for (size_t j = 0; j < num_routes; ++j) { + const envoy_config_route_v3_RouteMatch* match = + envoy_config_route_v3_Route_match(routes[j]); + if (match == nullptr) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("Match can't be null."); + } + size_t query_parameters_size; + static_cast(envoy_config_route_v3_RouteMatch_query_parameters( + match, &query_parameters_size)); + if (query_parameters_size > 0) { + continue; + } + XdsRouteConfigResource::Route route; + bool ignore_route = false; + grpc_error_handle error = + RoutePathMatchParse(match, &route, &ignore_route); + if (error != GRPC_ERROR_NONE) return error; + if (ignore_route) continue; + error = RouteHeaderMatchersParse(match, &route); + if (error != GRPC_ERROR_NONE) return error; + error = RouteRuntimeFractionParse(match, &route); + if (error != GRPC_ERROR_NONE) return error; + if (envoy_config_route_v3_Route_has_route(routes[j])) { + route.action.emplace(); + auto& route_action = + absl::get(route.action); + error = + RouteActionParse(context, routes[j], &route_action, &ignore_route); + if (error != GRPC_ERROR_NONE) return error; + if (ignore_route) continue; + if (route_action.retry_policy == absl::nullopt && + retry_policy != nullptr) { + route_action.retry_policy = virtual_host_retry_policy; + } + } else if (envoy_config_route_v3_Route_has_non_forwarding_action( + routes[j])) { + route.action + .emplace(); + } + if (context.use_v3) { + grpc_error_handle error = ParseTypedPerFilterConfig< + envoy_config_route_v3_Route, + envoy_config_route_v3_Route_TypedPerFilterConfigEntry>( + context, routes[j], + envoy_config_route_v3_Route_typed_per_filter_config_next, + envoy_config_route_v3_Route_TypedPerFilterConfigEntry_key, + envoy_config_route_v3_Route_TypedPerFilterConfigEntry_value, + &route.typed_per_filter_config); + if (error != GRPC_ERROR_NONE) return error; + } + vhost.routes.emplace_back(std::move(route)); + } + if (vhost.routes.empty()) { + return GRPC_ERROR_CREATE_FROM_STATIC_STRING("No valid routes specified."); + } + } + return GRPC_ERROR_NONE; +} + +// +// XdsRouteConfigResourceType +// + +namespace { + +void MaybeLogRouteConfiguration( + const XdsEncodingContext& context, + const envoy_config_route_v3_RouteConfiguration* route_config) { + if (GRPC_TRACE_FLAG_ENABLED(*context.tracer) && + gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) { + const upb_msgdef* msg_type = + envoy_config_route_v3_RouteConfiguration_getmsgdef(context.symtab); + char buf[10240]; + upb_text_encode(route_config, msg_type, nullptr, 0, buf, sizeof(buf)); + gpr_log(GPR_DEBUG, "[xds_client %p] RouteConfiguration: %s", context.client, + buf); + } +} + +} // namespace + +absl::StatusOr +XdsRouteConfigResourceType::Decode(const XdsEncodingContext& context, + absl::string_view serialized_resource, + bool /*is_v2*/) const { + // Parse serialized proto. + auto* resource = envoy_config_route_v3_RouteConfiguration_parse( + serialized_resource.data(), serialized_resource.size(), context.arena); + if (resource == nullptr) { + return absl::InvalidArgumentError("Can't parse Listener resource."); + } + MaybeLogRouteConfiguration(context, resource); + // Validate resource. + DecodeResult result; + result.name = UpbStringToStdString( + envoy_config_route_v3_RouteConfiguration_name(resource)); + auto route_config_data = absl::make_unique(); + grpc_error_handle error = XdsRouteConfigResource::Parse( + context, resource, &route_config_data->resource); + if (error != GRPC_ERROR_NONE) { + result.resource = absl::InvalidArgumentError(grpc_error_std_string(error)); + GRPC_ERROR_UNREF(error); + } else { + result.resource = std::move(route_config_data); + } + return std::move(result); +} + +} // namespace grpc_core diff --git a/src/core/ext/xds/xds_route_config.h b/src/core/ext/xds/xds_route_config.h new file mode 100644 index 00000000000..7adac7676f8 --- /dev/null +++ b/src/core/ext/xds/xds_route_config.h @@ -0,0 +1,211 @@ +// +// Copyright 2018 gRPC authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#ifndef GRPC_CORE_EXT_XDS_XDS_ROUTE_CONFIG_H +#define GRPC_CORE_EXT_XDS_XDS_ROUTE_CONFIG_H + +#include + +#include +#include +#include + +#include "absl/types/optional.h" +#include "absl/types/variant.h" +#include "envoy/config/route/v3/route.upb.h" +#include "re2/re2.h" + +#include "src/core/ext/xds/xds_common_types.h" +#include "src/core/ext/xds/xds_http_filters.h" +#include "src/core/ext/xds/xds_resource_type.h" +#include "src/core/lib/channel/status_util.h" +#include "src/core/lib/matchers/matchers.h" + +namespace grpc_core { + +bool XdsRbacEnabled(); + +struct XdsRouteConfigResource { + using TypedPerFilterConfig = + std::map; + + struct RetryPolicy { + internal::StatusCodeSet retry_on; + uint32_t num_retries; + + struct RetryBackOff { + Duration base_interval; + Duration max_interval; + + bool operator==(const RetryBackOff& other) const { + return base_interval == other.base_interval && + max_interval == other.max_interval; + } + std::string ToString() const; + }; + RetryBackOff retry_back_off; + + bool operator==(const RetryPolicy& other) const { + return (retry_on == other.retry_on && num_retries == other.num_retries && + retry_back_off == other.retry_back_off); + } + std::string ToString() const; + }; + + // TODO(donnadionne): When we can use absl::variant<>, consider using that + // for: PathMatcher, HeaderMatcher, cluster_name and weighted_clusters + struct Route { + // Matchers for this route. + struct Matchers { + StringMatcher path_matcher; + std::vector header_matchers; + absl::optional fraction_per_million; + + bool operator==(const Matchers& other) const { + return path_matcher == other.path_matcher && + header_matchers == other.header_matchers && + fraction_per_million == other.fraction_per_million; + } + std::string ToString() const; + }; + + Matchers matchers; + + struct UnknownAction { + bool operator==(const UnknownAction& /* other */) const { return true; } + }; + + struct RouteAction { + struct HashPolicy { + enum Type { HEADER, CHANNEL_ID }; + Type type; + bool terminal = false; + // Fields used for type HEADER. + std::string header_name; + std::unique_ptr regex = nullptr; + std::string regex_substitution; + + HashPolicy() {} + + // Copyable. + HashPolicy(const HashPolicy& other); + HashPolicy& operator=(const HashPolicy& other); + + // Moveable. + HashPolicy(HashPolicy&& other) noexcept; + HashPolicy& operator=(HashPolicy&& other) noexcept; + + bool operator==(const HashPolicy& other) const; + std::string ToString() const; + }; + + struct ClusterWeight { + std::string name; + uint32_t weight; + TypedPerFilterConfig typed_per_filter_config; + + bool operator==(const ClusterWeight& other) const { + return name == other.name && weight == other.weight && + typed_per_filter_config == other.typed_per_filter_config; + } + std::string ToString() const; + }; + + std::vector hash_policies; + absl::optional retry_policy; + + // Action for this route. + // TODO(roth): When we can use absl::variant<>, consider using that + // here, to enforce the fact that only one of the two fields can be set. + std::string cluster_name; + std::vector weighted_clusters; + // Storing the timeout duration from route action: + // RouteAction.max_stream_duration.grpc_timeout_header_max or + // RouteAction.max_stream_duration.max_stream_duration if the former is + // not set. + absl::optional max_stream_duration; + + bool operator==(const RouteAction& other) const { + return hash_policies == other.hash_policies && + retry_policy == other.retry_policy && + cluster_name == other.cluster_name && + weighted_clusters == other.weighted_clusters && + max_stream_duration == other.max_stream_duration; + } + std::string ToString() const; + }; + + struct NonForwardingAction { + bool operator==(const NonForwardingAction& /* other */) const { + return true; + } + }; + + absl::variant action; + TypedPerFilterConfig typed_per_filter_config; + + bool operator==(const Route& other) const { + return matchers == other.matchers && action == other.action && + typed_per_filter_config == other.typed_per_filter_config; + } + std::string ToString() const; + }; + + struct VirtualHost { + std::vector domains; + std::vector routes; + TypedPerFilterConfig typed_per_filter_config; + + bool operator==(const VirtualHost& other) const { + return domains == other.domains && routes == other.routes && + typed_per_filter_config == other.typed_per_filter_config; + } + }; + + std::vector virtual_hosts; + + bool operator==(const XdsRouteConfigResource& other) const { + return virtual_hosts == other.virtual_hosts; + } + std::string ToString() const; + + static grpc_error_handle Parse( + const XdsEncodingContext& context, + const envoy_config_route_v3_RouteConfiguration* route_config, + XdsRouteConfigResource* rds_update); +}; + +class XdsRouteConfigResourceType : public XdsResourceType { + public: + struct RouteConfigData : public ResourceData { + XdsRouteConfigResource resource; + }; + + absl::string_view type_url() const override { + return "envoy.config.route.v3.RouteConfiguration"; + } + absl::string_view v2_type_url() const override { + return "envoy.api.v2.RouteConfiguration"; + } + + absl::StatusOr Decode(const XdsEncodingContext& context, + absl::string_view serialized_resource, + bool /*is_v2*/) const override; +}; + +} // namespace grpc_core + +#endif // GRPC_CORE_EXT_XDS_XDS_ROUTE_CONFIG_H diff --git a/src/core/ext/xds/xds_routing.cc b/src/core/ext/xds/xds_routing.cc index ceb77d355a2..3c9ad087a12 100644 --- a/src/core/ext/xds/xds_routing.cc +++ b/src/core/ext/xds/xds_routing.cc @@ -147,7 +147,7 @@ absl::optional XdsRouting::GetRouteForRequest( const RouteListIterator& route_list_iterator, absl::string_view path, grpc_metadata_batch* initial_metadata) { for (size_t i = 0; i < route_list_iterator.Size(); ++i) { - const XdsApi::Route::Matchers& matchers = + const XdsRouteConfigResource::Route::Matchers& matchers = route_list_iterator.GetMatchersForRoute(i); if (matchers.path_matcher.Match(path) && HeadersMatch(matchers.header_matchers, initial_metadata) && @@ -182,8 +182,10 @@ namespace { const XdsHttpFilterImpl::FilterConfig* FindFilterConfigOverride( const std::string& instance_name, - const XdsApi::RdsUpdate::VirtualHost& vhost, const XdsApi::Route& route, - const XdsApi::Route::RouteAction::ClusterWeight* cluster_weight) { + const XdsRouteConfigResource::VirtualHost& vhost, + const XdsRouteConfigResource::Route& route, + const XdsRouteConfigResource::Route::RouteAction::ClusterWeight* + cluster_weight) { // Check ClusterWeight, if any. if (cluster_weight != nullptr) { auto it = cluster_weight->typed_per_filter_config.find(instance_name); @@ -203,10 +205,12 @@ const XdsHttpFilterImpl::FilterConfig* FindFilterConfigOverride( XdsRouting::GeneratePerHttpFilterConfigsResult XdsRouting::GeneratePerHTTPFilterConfigs( - const std::vector& + const std::vector& http_filters, - const XdsApi::RdsUpdate::VirtualHost& vhost, const XdsApi::Route& route, - const XdsApi::Route::RouteAction::ClusterWeight* cluster_weight, + const XdsRouteConfigResource::VirtualHost& vhost, + const XdsRouteConfigResource::Route& route, + const XdsRouteConfigResource::Route::RouteAction::ClusterWeight* + cluster_weight, grpc_channel_args* args) { GeneratePerHttpFilterConfigsResult result; result.args = args; diff --git a/src/core/ext/xds/xds_routing.h b/src/core/ext/xds/xds_routing.h index 9b1db2ee6f0..ba1fce3ceff 100644 --- a/src/core/ext/xds/xds_routing.h +++ b/src/core/ext/xds/xds_routing.h @@ -51,7 +51,7 @@ class XdsRouting { // Number of routes. virtual size_t Size() const = 0; // Returns the matchers for the route at the specified index. - virtual const XdsApi::Route::Matchers& GetMatchersForRoute( + virtual const XdsRouteConfigResource::Route::Matchers& GetMatchersForRoute( size_t index) const = 0; }; @@ -86,10 +86,12 @@ class XdsRouting { // Generates a map of per_filter_configs. \a args is consumed. static GeneratePerHttpFilterConfigsResult GeneratePerHTTPFilterConfigs( - const std::vector& + const std::vector& http_filters, - const XdsApi::RdsUpdate::VirtualHost& vhost, const XdsApi::Route& route, - const XdsApi::Route::RouteAction::ClusterWeight* cluster_weight, + const XdsRouteConfigResource::VirtualHost& vhost, + const XdsRouteConfigResource::Route& route, + const XdsRouteConfigResource::Route::RouteAction::ClusterWeight* + cluster_weight, grpc_channel_args* args); }; diff --git a/src/core/ext/xds/xds_server_config_fetcher.cc b/src/core/ext/xds/xds_server_config_fetcher.cc index a67377ac02a..9992abd5da9 100644 --- a/src/core/ext/xds/xds_server_config_fetcher.cc +++ b/src/core/ext/xds/xds_server_config_fetcher.cc @@ -94,7 +94,7 @@ class XdsServerConfigFetcher::ListenerWatcher grpc_server_xds_status_notifier serving_status_notifier, std::string listening_address); - void OnListenerChanged(XdsApi::LdsUpdate listener) override; + void OnListenerChanged(XdsListenerResource listener) override; void OnError(grpc_error_handle error) override; @@ -140,10 +140,10 @@ class XdsServerConfigFetcher::ListenerWatcher class XdsServerConfigFetcher::ListenerWatcher::FilterChainMatchManager : public grpc_server_config_fetcher::ConnectionManager { public: - FilterChainMatchManager( - RefCountedPtr xds_client, - XdsApi::LdsUpdate::FilterChainMap filter_chain_map, - absl::optional default_filter_chain); + FilterChainMatchManager(RefCountedPtr xds_client, + XdsListenerResource::FilterChainMap filter_chain_map, + absl::optional + default_filter_chain); absl::StatusOr UpdateChannelArgsForConnection( grpc_channel_args* args, grpc_endpoint* tcp) override; @@ -154,11 +154,11 @@ class XdsServerConfigFetcher::ListenerWatcher::FilterChainMatchManager void StartRdsWatch(RefCountedPtr listener_watcher) ABSL_EXCLUSIVE_LOCKS_REQUIRED(&ListenerWatcher::mu_); - const XdsApi::LdsUpdate::FilterChainMap& filter_chain_map() const { + const XdsListenerResource::FilterChainMap& filter_chain_map() const { return filter_chain_map_; } - const absl::optional& + const absl::optional& default_filter_chain() const { return default_filter_chain_; } @@ -176,7 +176,7 @@ class XdsServerConfigFetcher::ListenerWatcher::FilterChainMatchManager class RouteConfigWatcher; struct RdsUpdateState { RouteConfigWatcher* watcher; - absl::optional> rds_update; + absl::optional> rds_update; }; class XdsServerConfigSelector; @@ -185,12 +185,12 @@ class XdsServerConfigFetcher::ListenerWatcher::FilterChainMatchManager absl::StatusOr> CreateOrGetXdsCertificateProviderFromFilterChainData( - const XdsApi::LdsUpdate::FilterChainData* filter_chain); + const XdsListenerResource::FilterChainData* filter_chain); // Helper functions invoked by RouteConfigWatcher when there are updates to // RDS resources. void OnRouteConfigChanged(const std::string& resource_name, - XdsApi::RdsUpdate route_config); + XdsRouteConfigResource route_config); void OnError(const std::string& resource_name, grpc_error_handle error); void OnResourceDoesNotExist(const std::string& resource_name); @@ -198,14 +198,14 @@ class XdsServerConfigFetcher::ListenerWatcher::FilterChainMatchManager // This ref is only kept around till the FilterChainMatchManager becomes // ready. RefCountedPtr listener_watcher_; - const XdsApi::LdsUpdate::FilterChainMap filter_chain_map_; - const absl::optional + const XdsListenerResource::FilterChainMap filter_chain_map_; + const absl::optional default_filter_chain_; Mutex mu_; size_t rds_resources_yet_to_fetch_ ABSL_GUARDED_BY(mu_) = 0; std::map rds_map_ ABSL_GUARDED_BY(mu_); - std::map + std::map certificate_providers_map_ ABSL_GUARDED_BY(mu_); }; @@ -225,7 +225,7 @@ class XdsServerConfigFetcher::ListenerWatcher::FilterChainMatchManager:: : resource_name_(std::move(resource_name)), filter_chain_match_manager_(std::move(filter_chain_match_manager)) {} - void OnRouteConfigChanged(XdsApi::RdsUpdate route_config) override { + void OnRouteConfigChanged(XdsRouteConfigResource route_config) override { filter_chain_match_manager_->OnRouteConfigChanged(resource_name_, std::move(route_config)); } @@ -251,8 +251,8 @@ class XdsServerConfigFetcher::ListenerWatcher::FilterChainMatchManager:: XdsServerConfigSelector : public ServerConfigSelector { public: static absl::StatusOr> Create( - XdsApi::RdsUpdate rds_update, - const std::vector& + XdsRouteConfigResource rds_update, + const std::vector& http_filters); ~XdsServerConfigSelector() override = default; @@ -263,7 +263,7 @@ class XdsServerConfigFetcher::ListenerWatcher::FilterChainMatchManager:: struct Route { // true if an action other than kNonForwardingAction is configured. bool unsupported_action; - XdsApi::Route::Matchers matchers; + XdsRouteConfigResource::Route::Matchers matchers; RefCountedPtr method_config; }; @@ -274,7 +274,7 @@ class XdsServerConfigFetcher::ListenerWatcher::FilterChainMatchManager:: size_t Size() const override { return routes_->size(); } - const XdsApi::Route::Matchers& GetMatchersForRoute( + const XdsRouteConfigResource::Route::Matchers& GetMatchersForRoute( size_t index) const override { return (*routes_)[index].matchers; } @@ -314,8 +314,8 @@ class XdsServerConfigFetcher::ListenerWatcher::FilterChainMatchManager:: : public ServerConfigSelectorProvider { public: StaticXdsServerConfigSelectorProvider( - absl::StatusOr static_resource, - std::vector + absl::StatusOr static_resource, + std::vector http_filters) : static_resource_(std::move(static_resource)), http_filters_(std::move(http_filters)) {} @@ -335,8 +335,8 @@ class XdsServerConfigFetcher::ListenerWatcher::FilterChainMatchManager:: void CancelWatch() override { watcher_.reset(); } private: - absl::StatusOr static_resource_; - std::vector + absl::StatusOr static_resource_; + std::vector http_filters_; std::unique_ptr watcher_; @@ -350,8 +350,8 @@ class XdsServerConfigFetcher::ListenerWatcher::FilterChainMatchManager:: public: DynamicXdsServerConfigSelectorProvider( RefCountedPtr xds_client, std::string resource_name, - absl::StatusOr initial_resource, - std::vector + absl::StatusOr initial_resource, + std::vector http_filters); absl::StatusOr> Watch( @@ -362,19 +362,19 @@ class XdsServerConfigFetcher::ListenerWatcher::FilterChainMatchManager:: private: class RouteConfigWatcher; - void OnRouteConfigChanged(XdsApi::RdsUpdate rds_update); + void OnRouteConfigChanged(XdsRouteConfigResource rds_update); void OnError(grpc_error_handle error); void OnResourceDoesNotExist(); RefCountedPtr xds_client_; std::string resource_name_; - std::vector + std::vector http_filters_; RouteConfigWatcher* route_config_watcher_ = nullptr; Mutex mu_; std::unique_ptr watcher_ ABSL_GUARDED_BY(mu_); - absl::StatusOr resource_ ABSL_GUARDED_BY(mu_); + absl::StatusOr resource_ ABSL_GUARDED_BY(mu_); }; // A watcher implementation for updating the RDS resource used by @@ -387,7 +387,7 @@ class XdsServerConfigFetcher::ListenerWatcher::FilterChainMatchManager:: RefCountedPtr parent) : parent_(std::move(parent)) {} - void OnRouteConfigChanged(XdsApi::RdsUpdate route_config) override { + void OnRouteConfigChanged(XdsRouteConfigResource route_config) override { parent_->OnRouteConfigChanged(std::move(route_config)); } @@ -458,7 +458,7 @@ XdsServerConfigFetcher::ListenerWatcher::ListenerWatcher( listening_address_(std::move(listening_address)) {} void XdsServerConfigFetcher::ListenerWatcher::OnListenerChanged( - XdsApi::LdsUpdate listener) { + XdsListenerResource listener) { if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_server_config_fetcher_trace)) { gpr_log(GPR_INFO, "[ListenerWatcher %p] Received LDS update from xds client %p: %s", @@ -578,8 +578,9 @@ void XdsServerConfigFetcher::ListenerWatcher:: XdsServerConfigFetcher::ListenerWatcher::FilterChainMatchManager:: FilterChainMatchManager( RefCountedPtr xds_client, - XdsApi::LdsUpdate::FilterChainMap filter_chain_map, - absl::optional default_filter_chain) + XdsListenerResource::FilterChainMap filter_chain_map, + absl::optional + default_filter_chain) : xds_client_(std::move(xds_client)), filter_chain_map_(std::move(filter_chain_map)), default_filter_chain_(std::move(default_filter_chain)) {} @@ -647,7 +648,7 @@ void XdsServerConfigFetcher::ListenerWatcher::FilterChainMatchManager:: absl::StatusOr> XdsServerConfigFetcher::ListenerWatcher::FilterChainMatchManager:: CreateOrGetXdsCertificateProviderFromFilterChainData( - const XdsApi::LdsUpdate::FilterChainData* filter_chain) { + const XdsListenerResource::FilterChainData* filter_chain) { MutexLock lock(&mu_); auto it = certificate_providers_map_.find(filter_chain); if (it != certificate_providers_map_.end()) { @@ -711,7 +712,7 @@ XdsServerConfigFetcher::ListenerWatcher::FilterChainMatchManager:: void XdsServerConfigFetcher::ListenerWatcher::FilterChainMatchManager:: OnRouteConfigChanged(const std::string& resource_name, - XdsApi::RdsUpdate route_config) { + XdsRouteConfigResource route_config) { RefCountedPtr listener_watcher; { MutexLock lock(&mu_); @@ -777,8 +778,8 @@ void XdsServerConfigFetcher::ListenerWatcher::FilterChainMatchManager:: } } -const XdsApi::LdsUpdate::FilterChainData* FindFilterChainDataForSourcePort( - const XdsApi::LdsUpdate::FilterChainMap::SourcePortsMap& source_ports_map, +const XdsListenerResource::FilterChainData* FindFilterChainDataForSourcePort( + const XdsListenerResource::FilterChainMap::SourcePortsMap& source_ports_map, absl::string_view port_str) { int port = 0; if (!absl::SimpleAtoi(port_str, &port)) return nullptr; @@ -794,10 +795,10 @@ const XdsApi::LdsUpdate::FilterChainData* FindFilterChainDataForSourcePort( return nullptr; } -const XdsApi::LdsUpdate::FilterChainData* FindFilterChainDataForSourceIp( - const XdsApi::LdsUpdate::FilterChainMap::SourceIpVector& source_ip_vector, +const XdsListenerResource::FilterChainData* FindFilterChainDataForSourceIp( + const XdsListenerResource::FilterChainMap::SourceIpVector& source_ip_vector, const grpc_resolved_address* source_ip, absl::string_view port) { - const XdsApi::LdsUpdate::FilterChainMap::SourceIp* best_match = nullptr; + const XdsListenerResource::FilterChainMap::SourceIp* best_match = nullptr; for (const auto& entry : source_ip_vector) { // Special case for catch-all if (!entry.prefix_range.has_value()) { @@ -840,8 +841,8 @@ bool IsLoopbackIp(const grpc_resolved_address* address) { return false; } -const XdsApi::LdsUpdate::FilterChainData* FindFilterChainDataForSourceType( - const XdsApi::LdsUpdate::FilterChainMap::ConnectionSourceTypesArray& +const XdsListenerResource::FilterChainData* FindFilterChainDataForSourceType( + const XdsListenerResource::FilterChainMap::ConnectionSourceTypesArray& source_types_array, grpc_endpoint* tcp, absl::string_view destination_ip) { auto source_uri = URI::Parse(grpc_endpoint_get_peer(tcp)); @@ -865,34 +866,34 @@ const XdsApi::LdsUpdate::FilterChainData* FindFilterChainDataForSourceType( } // Use kAny only if kSameIporLoopback and kExternal are empty if (source_types_array[static_cast( - XdsApi::LdsUpdate::FilterChainMap:: + XdsListenerResource::FilterChainMap:: ConnectionSourceType::kSameIpOrLoopback)] .empty() && - source_types_array[static_cast(XdsApi::LdsUpdate::FilterChainMap:: + source_types_array[static_cast(XdsListenerResource::FilterChainMap:: ConnectionSourceType::kExternal)] .empty()) { return FindFilterChainDataForSourceIp( source_types_array[static_cast( - XdsApi::LdsUpdate::FilterChainMap::ConnectionSourceType::kAny)], + XdsListenerResource::FilterChainMap::ConnectionSourceType::kAny)], &source_addr, port); } if (IsLoopbackIp(&source_addr) || host == destination_ip) { return FindFilterChainDataForSourceIp( source_types_array[static_cast( - XdsApi::LdsUpdate::FilterChainMap::ConnectionSourceType:: + XdsListenerResource::FilterChainMap::ConnectionSourceType:: kSameIpOrLoopback)], &source_addr, port); } else { return FindFilterChainDataForSourceIp( source_types_array[static_cast( - XdsApi::LdsUpdate::FilterChainMap::ConnectionSourceType:: + XdsListenerResource::FilterChainMap::ConnectionSourceType:: kExternal)], &source_addr, port); } } -const XdsApi::LdsUpdate::FilterChainData* FindFilterChainDataForDestinationIp( - const XdsApi::LdsUpdate::FilterChainMap::DestinationIpVector +const XdsListenerResource::FilterChainData* FindFilterChainDataForDestinationIp( + const XdsListenerResource::FilterChainMap::DestinationIpVector destination_ip_vector, grpc_endpoint* tcp) { auto destination_uri = URI::Parse(grpc_endpoint_get_local_address(tcp)); @@ -914,7 +915,8 @@ const XdsApi::LdsUpdate::FilterChainData* FindFilterChainDataForDestinationIp( GRPC_ERROR_UNREF(error); return nullptr; } - const XdsApi::LdsUpdate::FilterChainMap::DestinationIp* best_match = nullptr; + const XdsListenerResource::FilterChainMap::DestinationIp* best_match = + nullptr; for (const auto& entry : destination_ip_vector) { // Special case for catch-all if (!entry.prefix_range.has_value()) { @@ -986,7 +988,7 @@ absl::StatusOr XdsServerConfigFetcher::ListenerWatcher:: filter_chain->http_connection_manager.rds_update.value(), filter_chain->http_connection_manager.http_filters); } else { - absl::StatusOr initial_resource; + absl::StatusOr initial_resource; { MutexLock lock(&mu_); initial_resource = @@ -1035,8 +1037,9 @@ absl::StatusOr< FilterChainMatchManager::XdsServerConfigSelector>> XdsServerConfigFetcher::ListenerWatcher::FilterChainMatchManager:: XdsServerConfigSelector::Create( - XdsApi::RdsUpdate rds_update, - const std::vector& + XdsRouteConfigResource rds_update, + const std::vector< + XdsListenerResource::HttpConnectionManager::HttpFilter>& http_filters) { auto config_selector = MakeRefCounted(); for (auto& vhost : rds_update.virtual_hosts) { @@ -1048,8 +1051,8 @@ XdsServerConfigFetcher::ListenerWatcher::FilterChainMatchManager:: auto& config_selector_route = virtual_host.routes.back(); config_selector_route.matchers = std::move(route.matchers); config_selector_route.unsupported_action = - absl::get_if(&route.action) == - nullptr; + absl::get_if( + &route.action) == nullptr; XdsRouting::GeneratePerHttpFilterConfigsResult result = XdsRouting::GeneratePerHTTPFilterConfigs(http_filters, vhost, route, nullptr, nullptr); @@ -1146,8 +1149,8 @@ XdsServerConfigFetcher::ListenerWatcher::FilterChainMatchManager:: DynamicXdsServerConfigSelectorProvider:: DynamicXdsServerConfigSelectorProvider( RefCountedPtr xds_client, std::string resource_name, - absl::StatusOr initial_resource, - std::vector + absl::StatusOr initial_resource, + std::vector http_filters) : xds_client_(std::move(xds_client)), resource_name_(std::move(resource_name)), @@ -1166,7 +1169,7 @@ XdsServerConfigFetcher::ListenerWatcher::FilterChainMatchManager:: std::unique_ptr< ServerConfigSelectorProvider::ServerConfigSelectorWatcher> watcher) { - absl::StatusOr resource; + absl::StatusOr resource; { MutexLock lock(&mu_); GPR_ASSERT(watcher_ == nullptr); @@ -1189,7 +1192,7 @@ void XdsServerConfigFetcher::ListenerWatcher::FilterChainMatchManager:: void XdsServerConfigFetcher::ListenerWatcher::FilterChainMatchManager:: DynamicXdsServerConfigSelectorProvider::OnRouteConfigChanged( - XdsApi::RdsUpdate rds_update) { + XdsRouteConfigResource rds_update) { MutexLock lock(&mu_); resource_ = std::move(rds_update); if (watcher_ == nullptr) { diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py index e6245355ff0..f98177c0a97 100644 --- a/src/python/grpcio/grpc_core_dependencies.py +++ b/src/python/grpcio/grpc_core_dependencies.py @@ -340,8 +340,13 @@ CORE_SOURCE_FILES = [ 'src/core/ext/xds/xds_channel_stack_modifier.cc', 'src/core/ext/xds/xds_client.cc', 'src/core/ext/xds/xds_client_stats.cc', + 'src/core/ext/xds/xds_cluster.cc', + 'src/core/ext/xds/xds_common_types.cc', + 'src/core/ext/xds/xds_endpoint.cc', 'src/core/ext/xds/xds_http_fault_filter.cc', 'src/core/ext/xds/xds_http_filters.cc', + 'src/core/ext/xds/xds_listener.cc', + 'src/core/ext/xds/xds_route_config.cc', 'src/core/ext/xds/xds_routing.cc', 'src/core/ext/xds/xds_server_config_fetcher.cc', 'src/core/lib/address_utils/parse_address.cc', diff --git a/tools/doxygen/Doxyfile.c++.internal b/tools/doxygen/Doxyfile.c++.internal index ad298f2ce22..9f0fe4a2936 100644 --- a/tools/doxygen/Doxyfile.c++.internal +++ b/tools/doxygen/Doxyfile.c++.internal @@ -1706,6 +1706,7 @@ src/core/ext/xds/certificate_provider_store.cc \ src/core/ext/xds/certificate_provider_store.h \ src/core/ext/xds/file_watcher_certificate_provider_factory.cc \ src/core/ext/xds/file_watcher_certificate_provider_factory.h \ +src/core/ext/xds/upb_utils.h \ src/core/ext/xds/xds_api.cc \ src/core/ext/xds/xds_api.h \ src/core/ext/xds/xds_bootstrap.cc \ @@ -1719,10 +1720,21 @@ src/core/ext/xds/xds_client.cc \ src/core/ext/xds/xds_client.h \ src/core/ext/xds/xds_client_stats.cc \ src/core/ext/xds/xds_client_stats.h \ +src/core/ext/xds/xds_cluster.cc \ +src/core/ext/xds/xds_cluster.h \ +src/core/ext/xds/xds_common_types.cc \ +src/core/ext/xds/xds_common_types.h \ +src/core/ext/xds/xds_endpoint.cc \ +src/core/ext/xds/xds_endpoint.h \ src/core/ext/xds/xds_http_fault_filter.cc \ src/core/ext/xds/xds_http_fault_filter.h \ src/core/ext/xds/xds_http_filters.cc \ src/core/ext/xds/xds_http_filters.h \ +src/core/ext/xds/xds_listener.cc \ +src/core/ext/xds/xds_listener.h \ +src/core/ext/xds/xds_resource_type.h \ +src/core/ext/xds/xds_route_config.cc \ +src/core/ext/xds/xds_route_config.h \ src/core/ext/xds/xds_routing.cc \ src/core/ext/xds/xds_routing.h \ src/core/ext/xds/xds_server_config_fetcher.cc \ diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal index 24d40046449..eda73349ded 100644 --- a/tools/doxygen/Doxyfile.core.internal +++ b/tools/doxygen/Doxyfile.core.internal @@ -1500,6 +1500,7 @@ src/core/ext/xds/certificate_provider_store.cc \ src/core/ext/xds/certificate_provider_store.h \ src/core/ext/xds/file_watcher_certificate_provider_factory.cc \ src/core/ext/xds/file_watcher_certificate_provider_factory.h \ +src/core/ext/xds/upb_utils.h \ src/core/ext/xds/xds_api.cc \ src/core/ext/xds/xds_api.h \ src/core/ext/xds/xds_bootstrap.cc \ @@ -1513,10 +1514,21 @@ src/core/ext/xds/xds_client.cc \ src/core/ext/xds/xds_client.h \ src/core/ext/xds/xds_client_stats.cc \ src/core/ext/xds/xds_client_stats.h \ +src/core/ext/xds/xds_cluster.cc \ +src/core/ext/xds/xds_cluster.h \ +src/core/ext/xds/xds_common_types.cc \ +src/core/ext/xds/xds_common_types.h \ +src/core/ext/xds/xds_endpoint.cc \ +src/core/ext/xds/xds_endpoint.h \ src/core/ext/xds/xds_http_fault_filter.cc \ src/core/ext/xds/xds_http_fault_filter.h \ src/core/ext/xds/xds_http_filters.cc \ src/core/ext/xds/xds_http_filters.h \ +src/core/ext/xds/xds_listener.cc \ +src/core/ext/xds/xds_listener.h \ +src/core/ext/xds/xds_resource_type.h \ +src/core/ext/xds/xds_route_config.cc \ +src/core/ext/xds/xds_route_config.h \ src/core/ext/xds/xds_routing.cc \ src/core/ext/xds/xds_routing.h \ src/core/ext/xds/xds_server_config_fetcher.cc \