diff --git a/BUILD b/BUILD
index 4f9181a6d1c86e..b33418d118bbd8 100644
--- a/BUILD
+++ b/BUILD
@@ -3818,6 +3818,7 @@ grpc_cc_library(
"//src/core:iomgr_fwd",
"//src/core:json",
"//src/core:latch",
+ "//src/core:lb_metadata",
"//src/core:lb_policy",
"//src/core:lb_policy_registry",
"//src/core:loop",
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 838ae79c140b6a..0272420d75222c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1876,6 +1876,7 @@ add_library(grpc
src/core/client_channel/direct_channel.cc
src/core/client_channel/dynamic_filters.cc
src/core/client_channel/global_subchannel_pool.cc
+ src/core/client_channel/lb_metadata.cc
src/core/client_channel/load_balanced_call_destination.cc
src/core/client_channel/local_subchannel_pool.cc
src/core/client_channel/retry_filter.cc
@@ -2969,6 +2970,7 @@ add_library(grpc_unsecure
src/core/client_channel/direct_channel.cc
src/core/client_channel/dynamic_filters.cc
src/core/client_channel/global_subchannel_pool.cc
+ src/core/client_channel/lb_metadata.cc
src/core/client_channel/load_balanced_call_destination.cc
src/core/client_channel/local_subchannel_pool.cc
src/core/client_channel/retry_filter.cc
diff --git a/Makefile b/Makefile
index d4e982f903c651..652e940ec9e27b 100644
--- a/Makefile
+++ b/Makefile
@@ -678,6 +678,7 @@ LIBGRPC_SRC = \
src/core/client_channel/direct_channel.cc \
src/core/client_channel/dynamic_filters.cc \
src/core/client_channel/global_subchannel_pool.cc \
+ src/core/client_channel/lb_metadata.cc \
src/core/client_channel/load_balanced_call_destination.cc \
src/core/client_channel/local_subchannel_pool.cc \
src/core/client_channel/retry_filter.cc \
diff --git a/Package.swift b/Package.swift
index 260c070857e67e..bbdc95a5037262 100644
--- a/Package.swift
+++ b/Package.swift
@@ -144,6 +144,8 @@ let package = Package(
"src/core/client_channel/dynamic_filters.h",
"src/core/client_channel/global_subchannel_pool.cc",
"src/core/client_channel/global_subchannel_pool.h",
+ "src/core/client_channel/lb_metadata.cc",
+ "src/core/client_channel/lb_metadata.h",
"src/core/client_channel/load_balanced_call_destination.cc",
"src/core/client_channel/load_balanced_call_destination.h",
"src/core/client_channel/local_subchannel_pool.cc",
@@ -1946,6 +1948,7 @@ let package = Package(
"src/core/util/time_precise.cc",
"src/core/util/time_precise.h",
"src/core/util/tmpfile.h",
+ "src/core/util/upb_utils.h",
"src/core/util/useful.h",
"src/core/util/windows/cpu.cc",
"src/core/util/windows/log.cc",
@@ -1958,7 +1961,6 @@ let package = Package(
"src/core/xds/grpc/certificate_provider_store.h",
"src/core/xds/grpc/file_watcher_certificate_provider_factory.cc",
"src/core/xds/grpc/file_watcher_certificate_provider_factory.h",
- "src/core/xds/grpc/upb_utils.h",
"src/core/xds/grpc/xds_audit_logger_registry.cc",
"src/core/xds/grpc/xds_audit_logger_registry.h",
"src/core/xds/grpc/xds_bootstrap_grpc.cc",
diff --git a/build_autogenerated.yaml b/build_autogenerated.yaml
index 3f93c7d81998a4..7184929424d1f9 100644
--- a/build_autogenerated.yaml
+++ b/build_autogenerated.yaml
@@ -237,6 +237,7 @@ libs:
- src/core/client_channel/direct_channel.h
- src/core/client_channel/dynamic_filters.h
- src/core/client_channel/global_subchannel_pool.h
+ - src/core/client_channel/lb_metadata.h
- src/core/client_channel/load_balanced_call_destination.h
- src/core/client_channel/local_subchannel_pool.h
- src/core/client_channel/retry_filter.h
@@ -1216,9 +1217,9 @@ libs:
- src/core/util/json/json_util.h
- src/core/util/json/json_writer.h
- src/core/util/spinlock.h
+ - src/core/util/upb_utils.h
- src/core/xds/grpc/certificate_provider_store.h
- src/core/xds/grpc/file_watcher_certificate_provider_factory.h
- - src/core/xds/grpc/upb_utils.h
- src/core/xds/grpc/xds_audit_logger_registry.h
- src/core/xds/grpc/xds_bootstrap_grpc.h
- src/core/xds/grpc/xds_certificate_provider.h
@@ -1261,6 +1262,7 @@ libs:
- src/core/client_channel/direct_channel.cc
- src/core/client_channel/dynamic_filters.cc
- src/core/client_channel/global_subchannel_pool.cc
+ - src/core/client_channel/lb_metadata.cc
- src/core/client_channel/load_balanced_call_destination.cc
- src/core/client_channel/local_subchannel_pool.cc
- src/core/client_channel/retry_filter.cc
@@ -2223,6 +2225,7 @@ libs:
- src/core/client_channel/direct_channel.h
- src/core/client_channel/dynamic_filters.h
- src/core/client_channel/global_subchannel_pool.h
+ - src/core/client_channel/lb_metadata.h
- src/core/client_channel/load_balanced_call_destination.h
- src/core/client_channel/local_subchannel_pool.h
- src/core/client_channel/retry_filter.h
@@ -2685,6 +2688,7 @@ libs:
- src/core/util/json/json_reader.h
- src/core/util/json/json_writer.h
- src/core/util/spinlock.h
+ - src/core/util/upb_utils.h
- third_party/upb/upb/generated_code_support.h
- third_party/upb/upb/mini_descriptor/build_enum.h
- third_party/upb/upb/mini_descriptor/decode.h
@@ -2717,6 +2721,7 @@ libs:
- src/core/client_channel/direct_channel.cc
- src/core/client_channel/dynamic_filters.cc
- src/core/client_channel/global_subchannel_pool.cc
+ - src/core/client_channel/lb_metadata.cc
- src/core/client_channel/load_balanced_call_destination.cc
- src/core/client_channel/local_subchannel_pool.cc
- src/core/client_channel/retry_filter.cc
diff --git a/config.m4 b/config.m4
index d3c633249d3819..6435dff6bcc5f8 100644
--- a/config.m4
+++ b/config.m4
@@ -53,6 +53,7 @@ if test "$PHP_GRPC" != "no"; then
src/core/client_channel/direct_channel.cc \
src/core/client_channel/dynamic_filters.cc \
src/core/client_channel/global_subchannel_pool.cc \
+ src/core/client_channel/lb_metadata.cc \
src/core/client_channel/load_balanced_call_destination.cc \
src/core/client_channel/local_subchannel_pool.cc \
src/core/client_channel/retry_filter.cc \
diff --git a/config.w32 b/config.w32
index ade25276f75e73..1c871b2300b3c6 100644
--- a/config.w32
+++ b/config.w32
@@ -18,6 +18,7 @@ if (PHP_GRPC != "no") {
"src\\core\\client_channel\\direct_channel.cc " +
"src\\core\\client_channel\\dynamic_filters.cc " +
"src\\core\\client_channel\\global_subchannel_pool.cc " +
+ "src\\core\\client_channel\\lb_metadata.cc " +
"src\\core\\client_channel\\load_balanced_call_destination.cc " +
"src\\core\\client_channel\\local_subchannel_pool.cc " +
"src\\core\\client_channel\\retry_filter.cc " +
diff --git a/gRPC-C++.podspec b/gRPC-C++.podspec
index 45d20d5aa5d81c..4901373eb91da2 100644
--- a/gRPC-C++.podspec
+++ b/gRPC-C++.podspec
@@ -283,6 +283,7 @@ Pod::Spec.new do |s|
'src/core/client_channel/direct_channel.h',
'src/core/client_channel/dynamic_filters.h',
'src/core/client_channel/global_subchannel_pool.h',
+ 'src/core/client_channel/lb_metadata.h',
'src/core/client_channel/load_balanced_call_destination.h',
'src/core/client_channel/local_subchannel_pool.h',
'src/core/client_channel/retry_filter.h',
@@ -1323,10 +1324,10 @@ Pod::Spec.new do |s|
'src/core/util/string.h',
'src/core/util/time_precise.h',
'src/core/util/tmpfile.h',
+ 'src/core/util/upb_utils.h',
'src/core/util/useful.h',
'src/core/xds/grpc/certificate_provider_store.h',
'src/core/xds/grpc/file_watcher_certificate_provider_factory.h',
- 'src/core/xds/grpc/upb_utils.h',
'src/core/xds/grpc/xds_audit_logger_registry.h',
'src/core/xds/grpc/xds_bootstrap_grpc.h',
'src/core/xds/grpc/xds_certificate_provider.h',
@@ -1574,6 +1575,7 @@ Pod::Spec.new do |s|
'src/core/client_channel/direct_channel.h',
'src/core/client_channel/dynamic_filters.h',
'src/core/client_channel/global_subchannel_pool.h',
+ 'src/core/client_channel/lb_metadata.h',
'src/core/client_channel/load_balanced_call_destination.h',
'src/core/client_channel/local_subchannel_pool.h',
'src/core/client_channel/retry_filter.h',
@@ -2596,10 +2598,10 @@ Pod::Spec.new do |s|
'src/core/util/string.h',
'src/core/util/time_precise.h',
'src/core/util/tmpfile.h',
+ 'src/core/util/upb_utils.h',
'src/core/util/useful.h',
'src/core/xds/grpc/certificate_provider_store.h',
'src/core/xds/grpc/file_watcher_certificate_provider_factory.h',
- 'src/core/xds/grpc/upb_utils.h',
'src/core/xds/grpc/xds_audit_logger_registry.h',
'src/core/xds/grpc/xds_bootstrap_grpc.h',
'src/core/xds/grpc/xds_certificate_provider.h',
diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec
index e4e3f69602daf3..d812ea38794ecb 100644
--- a/gRPC-Core.podspec
+++ b/gRPC-Core.podspec
@@ -263,6 +263,8 @@ Pod::Spec.new do |s|
'src/core/client_channel/dynamic_filters.h',
'src/core/client_channel/global_subchannel_pool.cc',
'src/core/client_channel/global_subchannel_pool.h',
+ 'src/core/client_channel/lb_metadata.cc',
+ 'src/core/client_channel/lb_metadata.h',
'src/core/client_channel/load_balanced_call_destination.cc',
'src/core/client_channel/load_balanced_call_destination.h',
'src/core/client_channel/local_subchannel_pool.cc',
@@ -2061,6 +2063,7 @@ Pod::Spec.new do |s|
'src/core/util/time_precise.cc',
'src/core/util/time_precise.h',
'src/core/util/tmpfile.h',
+ 'src/core/util/upb_utils.h',
'src/core/util/useful.h',
'src/core/util/windows/cpu.cc',
'src/core/util/windows/log.cc',
@@ -2073,7 +2076,6 @@ Pod::Spec.new do |s|
'src/core/xds/grpc/certificate_provider_store.h',
'src/core/xds/grpc/file_watcher_certificate_provider_factory.cc',
'src/core/xds/grpc/file_watcher_certificate_provider_factory.h',
- 'src/core/xds/grpc/upb_utils.h',
'src/core/xds/grpc/xds_audit_logger_registry.cc',
'src/core/xds/grpc/xds_audit_logger_registry.h',
'src/core/xds/grpc/xds_bootstrap_grpc.cc',
@@ -2365,6 +2367,7 @@ Pod::Spec.new do |s|
'src/core/client_channel/direct_channel.h',
'src/core/client_channel/dynamic_filters.h',
'src/core/client_channel/global_subchannel_pool.h',
+ 'src/core/client_channel/lb_metadata.h',
'src/core/client_channel/load_balanced_call_destination.h',
'src/core/client_channel/local_subchannel_pool.h',
'src/core/client_channel/retry_filter.h',
@@ -3367,10 +3370,10 @@ Pod::Spec.new do |s|
'src/core/util/string.h',
'src/core/util/time_precise.h',
'src/core/util/tmpfile.h',
+ 'src/core/util/upb_utils.h',
'src/core/util/useful.h',
'src/core/xds/grpc/certificate_provider_store.h',
'src/core/xds/grpc/file_watcher_certificate_provider_factory.h',
- 'src/core/xds/grpc/upb_utils.h',
'src/core/xds/grpc/xds_audit_logger_registry.h',
'src/core/xds/grpc/xds_bootstrap_grpc.h',
'src/core/xds/grpc/xds_certificate_provider.h',
diff --git a/grpc.gemspec b/grpc.gemspec
index 423a5aa0140914..213e157d0eb4b9 100644
--- a/grpc.gemspec
+++ b/grpc.gemspec
@@ -150,6 +150,8 @@ Gem::Specification.new do |s|
s.files += %w( src/core/client_channel/dynamic_filters.h )
s.files += %w( src/core/client_channel/global_subchannel_pool.cc )
s.files += %w( src/core/client_channel/global_subchannel_pool.h )
+ s.files += %w( src/core/client_channel/lb_metadata.cc )
+ s.files += %w( src/core/client_channel/lb_metadata.h )
s.files += %w( src/core/client_channel/load_balanced_call_destination.cc )
s.files += %w( src/core/client_channel/load_balanced_call_destination.h )
s.files += %w( src/core/client_channel/local_subchannel_pool.cc )
@@ -1948,6 +1950,7 @@ Gem::Specification.new do |s|
s.files += %w( src/core/util/time_precise.cc )
s.files += %w( src/core/util/time_precise.h )
s.files += %w( src/core/util/tmpfile.h )
+ s.files += %w( src/core/util/upb_utils.h )
s.files += %w( src/core/util/useful.h )
s.files += %w( src/core/util/windows/cpu.cc )
s.files += %w( src/core/util/windows/log.cc )
@@ -1960,7 +1963,6 @@ Gem::Specification.new do |s|
s.files += %w( src/core/xds/grpc/certificate_provider_store.h )
s.files += %w( src/core/xds/grpc/file_watcher_certificate_provider_factory.cc )
s.files += %w( src/core/xds/grpc/file_watcher_certificate_provider_factory.h )
- s.files += %w( src/core/xds/grpc/upb_utils.h )
s.files += %w( src/core/xds/grpc/xds_audit_logger_registry.cc )
s.files += %w( src/core/xds/grpc/xds_audit_logger_registry.h )
s.files += %w( src/core/xds/grpc/xds_bootstrap_grpc.cc )
diff --git a/package.xml b/package.xml
index b3c2b6e941b792..3fcb69ed9813e5 100644
--- a/package.xml
+++ b/package.xml
@@ -132,6 +132,8 @@
+
+
@@ -1930,6 +1932,7 @@
+
@@ -1942,7 +1945,6 @@
-
diff --git a/src/core/BUILD b/src/core/BUILD
index d5bb1efd7259ac..b9b1f80cf30828 100644
--- a/src/core/BUILD
+++ b/src/core/BUILD
@@ -3456,6 +3456,7 @@ grpc_cc_library(
hdrs = ["load_balancing/lb_policy.h"],
external_deps = [
"absl/base:core_headers",
+ "absl/container:inlined_vector",
"absl/status",
"absl/status:statusor",
"absl/strings",
@@ -3525,6 +3526,21 @@ grpc_cc_library(
],
)
+grpc_cc_library(
+ name = "lb_metadata",
+ srcs = ["client_channel/lb_metadata.cc"],
+ hdrs = ["client_channel/lb_metadata.h"],
+ external_deps = [
+ "absl/log:log",
+ "absl/strings",
+ "absl/types:optional",
+ ],
+ deps = [
+ "lb_policy",
+ "metadata_batch",
+ ],
+)
+
grpc_cc_library(
name = "subchannel_interface",
hdrs = ["load_balancing/subchannel_interface.h"],
@@ -5009,6 +5025,7 @@ grpc_cc_library(
"slice_refcount",
"status_helper",
"time",
+ "upb_utils",
"uuid_v4",
"validation_errors",
"//:backoff",
@@ -5039,14 +5056,13 @@ grpc_cc_library(
grpc_cc_library(
name = "upb_utils",
hdrs = [
- "xds/grpc/upb_utils.h",
+ "util/upb_utils.h",
],
external_deps = [
"absl/strings",
"@com_google_protobuf//upb:base",
],
language = "c++",
- deps = ["//:gpr_platform"],
)
grpc_cc_library(
diff --git a/src/core/client_channel/client_channel_filter.cc b/src/core/client_channel/client_channel_filter.cc
index 0ad970b549229b..35cbb04e6553c5 100644
--- a/src/core/client_channel/client_channel_filter.cc
+++ b/src/core/client_channel/client_channel_filter.cc
@@ -57,6 +57,7 @@
#include "src/core/client_channel/config_selector.h"
#include "src/core/client_channel/dynamic_filters.h"
#include "src/core/client_channel/global_subchannel_pool.h"
+#include "src/core/client_channel/lb_metadata.h"
#include "src/core/client_channel/local_subchannel_pool.h"
#include "src/core/client_channel/retry_filter.h"
#include "src/core/client_channel/subchannel.h"
@@ -2353,79 +2354,6 @@ class ClientChannelFilter::LoadBalancedCall::LbCallState final
LoadBalancedCall* lb_call_;
};
-//
-// ClientChannelFilter::LoadBalancedCall::Metadata
-//
-
-class ClientChannelFilter::LoadBalancedCall::Metadata final
- : public LoadBalancingPolicy::MetadataInterface {
- public:
- explicit Metadata(grpc_metadata_batch* batch) : batch_(batch) {}
-
- void Add(absl::string_view key, absl::string_view value) override {
- if (batch_ == nullptr) return;
- // Gross, egregious hack to support legacy grpclb behavior.
- // TODO(ctiller): Use a promise context for this once that plumbing is done.
- if (key == GrpcLbClientStatsMetadata::key()) {
- batch_->Set(
- GrpcLbClientStatsMetadata(),
- const_cast(
- reinterpret_cast(value.data())));
- return;
- }
- batch_->Append(key, Slice::FromStaticString(value),
- [key](absl::string_view error, const Slice& value) {
- LOG(ERROR) << error << " key:" << key
- << " value:" << value.as_string_view();
- });
- }
-
- std::vector> TestOnlyCopyToVector()
- override {
- if (batch_ == nullptr) return {};
- Encoder encoder;
- batch_->Encode(&encoder);
- return encoder.Take();
- }
-
- absl::optional Lookup(absl::string_view key,
- std::string* buffer) const override {
- if (batch_ == nullptr) return absl::nullopt;
- return batch_->GetStringValue(key, buffer);
- }
-
- private:
- class Encoder final {
- public:
- void Encode(const Slice& key, const Slice& value) {
- out_.emplace_back(std::string(key.as_string_view()),
- std::string(value.as_string_view()));
- }
-
- template
- void Encode(Which, const typename Which::ValueType& value) {
- auto value_slice = Which::Encode(value);
- out_.emplace_back(std::string(Which::key()),
- std::string(value_slice.as_string_view()));
- }
-
- void Encode(GrpcTimeoutMetadata,
- const typename GrpcTimeoutMetadata::ValueType&) {}
- void Encode(HttpPathMetadata, const Slice&) {}
- void Encode(HttpMethodMetadata,
- const typename HttpMethodMetadata::ValueType&) {}
-
- std::vector> Take() {
- return std::move(out_);
- }
-
- private:
- std::vector> out_;
- };
-
- grpc_metadata_batch* batch_;
-};
-
//
// ClientChannelFilter::LoadBalancedCall::LbCallState
//
@@ -2536,7 +2464,7 @@ void ClientChannelFilter::LoadBalancedCall::RecordCallCompletion(
// If the LB policy requested a callback for trailing metadata, invoke
// the callback.
if (lb_subchannel_call_tracker_ != nullptr) {
- Metadata trailing_metadata(recv_trailing_metadata);
+ LbMetadata trailing_metadata(recv_trailing_metadata);
BackendMetricAccessor backend_metric_accessor(this, recv_trailing_metadata);
LoadBalancingPolicy::SubchannelCallTrackerInterface::FinishArgs args = {
peer_address, status, &trailing_metadata, &backend_metric_accessor};
@@ -2683,7 +2611,7 @@ bool ClientChannelFilter::LoadBalancedCall::PickSubchannelImpl(
pick_args.path = path->as_string_view();
LbCallState lb_call_state(this);
pick_args.call_state = &lb_call_state;
- Metadata initial_metadata(send_initial_metadata());
+ LbMetadata initial_metadata(send_initial_metadata());
pick_args.initial_metadata = &initial_metadata;
auto result = picker->Pick(pick_args);
return HandlePickResult(
@@ -2716,6 +2644,9 @@ bool ClientChannelFilter::LoadBalancedCall::PickSubchannelImpl(
if (lb_subchannel_call_tracker_ != nullptr) {
lb_subchannel_call_tracker_->Start();
}
+ // Handle metadata mutations.
+ MetadataMutationHandler::Apply(complete_pick->metadata_mutations,
+ send_initial_metadata());
return true;
},
// QueuePick
diff --git a/src/core/client_channel/lb_metadata.cc b/src/core/client_channel/lb_metadata.cc
new file mode 100644
index 00000000000000..d66552d053ad00
--- /dev/null
+++ b/src/core/client_channel/lb_metadata.cc
@@ -0,0 +1,99 @@
+// Copyright 2024 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 "src/core/client_channel/lb_metadata.h"
+
+#include "absl/log/log.h"
+
+namespace grpc_core {
+
+//
+// LbMetadata
+//
+
+namespace {
+
+class Encoder {
+ public:
+ void Encode(const Slice& key, const Slice& value) {
+ out_.emplace_back(std::string(key.as_string_view()),
+ std::string(value.as_string_view()));
+ }
+
+ template
+ void Encode(Which, const typename Which::ValueType& value) {
+ auto value_slice = Which::Encode(value);
+ out_.emplace_back(std::string(Which::key()),
+ std::string(value_slice.as_string_view()));
+ }
+
+ void Encode(GrpcTimeoutMetadata,
+ const typename GrpcTimeoutMetadata::ValueType&) {}
+ void Encode(HttpPathMetadata, const Slice&) {}
+ void Encode(HttpMethodMetadata,
+ const typename HttpMethodMetadata::ValueType&) {}
+
+ std::vector> Take() {
+ return std::move(out_);
+ }
+
+ private:
+ std::vector> out_;
+};
+
+} // namespace
+
+absl::optional LbMetadata::Lookup(
+ absl::string_view key, std::string* buffer) const {
+ if (batch_ == nullptr) return absl::nullopt;
+ return batch_->GetStringValue(key, buffer);
+}
+
+std::vector>
+LbMetadata::TestOnlyCopyToVector() const {
+ if (batch_ == nullptr) return {};
+ Encoder encoder;
+ batch_->Encode(&encoder);
+ return encoder.Take();
+}
+
+//
+// MetadataMutationHandler
+//
+
+void MetadataMutationHandler::Apply(
+ LoadBalancingPolicy::MetadataMutations& metadata_mutations,
+ grpc_metadata_batch* metadata) {
+ for (auto& p : metadata_mutations.additions_) {
+ absl::string_view key = p.first;
+ Slice& value =
+ grpc_event_engine::experimental::internal::SliceCast(p.second);
+ // Gross, egregious hack to support legacy grpclb behavior.
+ // TODO(ctiller): Use a promise context for this once that plumbing is done.
+ if (key == GrpcLbClientStatsMetadata::key()) {
+ metadata->Set(
+ GrpcLbClientStatsMetadata(),
+ const_cast(
+ reinterpret_cast(value.data())));
+ continue;
+ }
+ metadata->Append(key, std::move(value),
+ [key](absl::string_view error, const Slice& value) {
+ LOG(ERROR) << error << " key:" << key
+ << " value:" << value.as_string_view();
+ });
+ }
+}
+
+} // namespace grpc_core
diff --git a/src/core/client_channel/lb_metadata.h b/src/core/client_channel/lb_metadata.h
new file mode 100644
index 00000000000000..6c1fe7eeb7b64b
--- /dev/null
+++ b/src/core/client_channel/lb_metadata.h
@@ -0,0 +1,50 @@
+// Copyright 2024 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_SRC_CORE_CLIENT_CHANNEL_LB_METADATA_H
+#define GRPC_SRC_CORE_CLIENT_CHANNEL_LB_METADATA_H
+
+#include
+#include
+
+#include "absl/strings/string_view.h"
+#include "absl/types/optional.h"
+
+#include "src/core/lib/transport/metadata_batch.h"
+#include "src/core/load_balancing/lb_policy.h"
+
+namespace grpc_core {
+
+class LbMetadata : public LoadBalancingPolicy::MetadataInterface {
+ public:
+ explicit LbMetadata(grpc_metadata_batch* batch) : batch_(batch) {}
+
+ absl::optional Lookup(absl::string_view key,
+ std::string* buffer) const override;
+
+ std::vector> TestOnlyCopyToVector() const;
+
+ private:
+ grpc_metadata_batch* batch_;
+};
+
+class MetadataMutationHandler {
+ public:
+ static void Apply(LoadBalancingPolicy::MetadataMutations& metadata_mutations,
+ grpc_metadata_batch* metadata);
+};
+
+} // namespace grpc_core
+
+#endif // GRPC_SRC_CORE_CLIENT_CHANNEL_LB_METADATA_H
diff --git a/src/core/client_channel/load_balanced_call_destination.cc b/src/core/client_channel/load_balanced_call_destination.cc
index 352113fe0f28ad..c13369a09fe7d6 100644
--- a/src/core/client_channel/load_balanced_call_destination.cc
+++ b/src/core/client_channel/load_balanced_call_destination.cc
@@ -18,6 +18,7 @@
#include "src/core/client_channel/client_channel.h"
#include "src/core/client_channel/client_channel_internal.h"
+#include "src/core/client_channel/lb_metadata.h"
#include "src/core/client_channel/subchannel.h"
#include "src/core/lib/channel/status_util.h"
#include "src/core/lib/config/core_configuration.h"
@@ -28,74 +29,6 @@ namespace grpc_core {
namespace {
-class LbMetadata : public LoadBalancingPolicy::MetadataInterface {
- public:
- explicit LbMetadata(grpc_metadata_batch* batch) : batch_(batch) {}
-
- void Add(absl::string_view key, absl::string_view value) override {
- if (batch_ == nullptr) return;
- // Gross, egregious hack to support legacy grpclb behavior.
- // TODO(ctiller): Use a promise context for this once that plumbing is done.
- if (key == GrpcLbClientStatsMetadata::key()) {
- batch_->Set(
- GrpcLbClientStatsMetadata(),
- const_cast(
- reinterpret_cast(value.data())));
- return;
- }
- batch_->Append(key, Slice::FromStaticString(value),
- [key](absl::string_view error, const Slice& value) {
- LOG(ERROR) << error << " key:" << key
- << " value:" << value.as_string_view();
- });
- }
-
- std::vector> TestOnlyCopyToVector()
- override {
- if (batch_ == nullptr) return {};
- Encoder encoder;
- batch_->Encode(&encoder);
- return encoder.Take();
- }
-
- absl::optional Lookup(absl::string_view key,
- std::string* buffer) const override {
- if (batch_ == nullptr) return absl::nullopt;
- return batch_->GetStringValue(key, buffer);
- }
-
- private:
- class Encoder {
- public:
- void Encode(const Slice& key, const Slice& value) {
- out_.emplace_back(std::string(key.as_string_view()),
- std::string(value.as_string_view()));
- }
-
- template
- void Encode(Which, const typename Which::ValueType& value) {
- auto value_slice = Which::Encode(value);
- out_.emplace_back(std::string(Which::key()),
- std::string(value_slice.as_string_view()));
- }
-
- void Encode(GrpcTimeoutMetadata,
- const typename GrpcTimeoutMetadata::ValueType&) {}
- void Encode(HttpPathMetadata, const Slice&) {}
- void Encode(HttpMethodMetadata,
- const typename HttpMethodMetadata::ValueType&) {}
-
- std::vector> Take() {
- return std::move(out_);
- }
-
- private:
- std::vector> out_;
- };
-
- grpc_metadata_batch* batch_;
-};
-
void MaybeCreateCallAttemptTracer(bool is_transparent_retry) {
auto* call_tracer = MaybeGetContext();
if (call_tracer == nullptr) return;
@@ -208,6 +141,9 @@ LoopCtl>> PickSubchannel(
complete_pick->subchannel_call_tracker->Start();
SetContext(complete_pick->subchannel_call_tracker.release());
}
+ // Apply metadata mutations, if any.
+ MetadataMutationHandler::Apply(complete_pick->metadata_mutations,
+ &client_initial_metadata);
// Return the connected subchannel.
return call_destination;
},
diff --git a/src/core/load_balancing/grpclb/grpclb.cc b/src/core/load_balancing/grpclb/grpclb.cc
index 2258e7ebaaf509..6d928c0dfa28f8 100644
--- a/src/core/load_balancing/grpclb/grpclb.cc
+++ b/src/core/load_balancing/grpclb/grpclb.cc
@@ -311,14 +311,17 @@ class GrpcLb final : public LoadBalancingPolicy {
class SubchannelWrapper final : public DelegatingSubchannel {
public:
SubchannelWrapper(RefCountedPtr subchannel,
- RefCountedPtr lb_policy, std::string lb_token,
+ RefCountedPtr lb_policy,
+ grpc_event_engine::experimental::Slice lb_token,
RefCountedPtr client_stats)
: DelegatingSubchannel(std::move(subchannel)),
lb_policy_(std::move(lb_policy)),
lb_token_(std::move(lb_token)),
client_stats_(std::move(client_stats)) {}
- const std::string& lb_token() const { return lb_token_; }
+ const grpc_event_engine::experimental::Slice& lb_token() const {
+ return lb_token_;
+ }
GrpcLbClientStats* client_stats() const { return client_stats_.get(); }
private:
@@ -340,14 +343,14 @@ class GrpcLb final : public LoadBalancingPolicy {
}
RefCountedPtr lb_policy_;
- std::string lb_token_;
+ grpc_event_engine::experimental::Slice lb_token_;
RefCountedPtr client_stats_;
};
class TokenAndClientStatsArg final
: public RefCounted {
public:
- TokenAndClientStatsArg(std::string lb_token,
+ TokenAndClientStatsArg(grpc_event_engine::experimental::Slice lb_token,
RefCountedPtr client_stats)
: lb_token_(std::move(lb_token)),
client_stats_(std::move(client_stats)) {}
@@ -358,18 +361,21 @@ class GrpcLb final : public LoadBalancingPolicy {
static int ChannelArgsCompare(const TokenAndClientStatsArg* a,
const TokenAndClientStatsArg* b) {
- int r = a->lb_token_.compare(b->lb_token_);
+ int r =
+ a->lb_token_.as_string_view().compare(b->lb_token_.as_string_view());
if (r != 0) return r;
return QsortCompare(a->client_stats_.get(), b->client_stats_.get());
}
- const std::string& lb_token() const { return lb_token_; }
+ const grpc_event_engine::experimental::Slice& lb_token() const {
+ return lb_token_;
+ }
RefCountedPtr client_stats() const {
return client_stats_;
}
private:
- std::string lb_token_;
+ grpc_event_engine::experimental::Slice lb_token_;
RefCountedPtr client_stats_;
};
@@ -665,7 +671,8 @@ class GrpcLb::Serverlist::AddressIterator final
// LB token processing.
const size_t lb_token_length = strnlen(
server.load_balance_token, GPR_ARRAY_SIZE(server.load_balance_token));
- std::string lb_token(server.load_balance_token, lb_token_length);
+ auto lb_token = grpc_event_engine::experimental::Slice::FromCopiedBuffer(
+ server.load_balance_token, lb_token_length);
if (lb_token.empty()) {
auto addr_uri = grpc_sockaddr_to_uri(&addr);
LOG(INFO) << "Missing LB token for backend address '"
@@ -773,9 +780,10 @@ GrpcLb::PickResult GrpcLb::Picker::Pick(PickArgs args) {
// a string and rely on the client_load_reporting filter to know
// how to interpret it.
// NOLINTBEGIN(bugprone-string-constructor)
- args.initial_metadata->Add(
+ complete_pick->metadata_mutations.Add(
GrpcLbClientStatsMetadata::key(),
- absl::string_view(reinterpret_cast(client_stats), 0));
+ grpc_event_engine::experimental::Slice(grpc_slice_from_static_buffer(
+ reinterpret_cast(client_stats), 0)));
// NOLINTEND(bugprone-string-constructor)
// Update calls-started.
client_stats->AddCallStarted();
@@ -785,10 +793,8 @@ GrpcLb::PickResult GrpcLb::Picker::Pick(PickArgs args) {
// may get refreshed between when we return this pick and when the
// initial metadata goes out on the wire.
if (!subchannel_wrapper->lb_token().empty()) {
- char* lb_token = static_cast(
- args.call_state->Alloc(subchannel_wrapper->lb_token().size() + 1));
- strcpy(lb_token, subchannel_wrapper->lb_token().c_str());
- args.initial_metadata->Add(LbTokenMetadata::key(), lb_token);
+ complete_pick->metadata_mutations.Add(
+ LbTokenMetadata::key(), subchannel_wrapper->lb_token().Ref());
}
// Unwrap subchannel to pass up to the channel.
complete_pick->subchannel = subchannel_wrapper->wrapped_subchannel();
@@ -811,13 +817,11 @@ RefCountedPtr GrpcLb::Helper::CreateSubchannel(
absl::StrFormat("[grpclb %p] no TokenAndClientStatsArg for address %s",
parent(), addr_str.value_or("N/A").c_str()));
}
- std::string lb_token = arg->lb_token();
- RefCountedPtr client_stats = arg->client_stats();
return MakeRefCounted(
parent()->channel_control_helper()->CreateSubchannel(
address, per_address_args, args),
parent()->RefAsSubclass(DEBUG_LOCATION, "SubchannelWrapper"),
- std::move(lb_token), std::move(client_stats));
+ arg->lb_token().Ref(), arg->client_stats());
}
void GrpcLb::Helper::UpdateState(grpc_connectivity_state state,
@@ -1542,7 +1546,8 @@ class GrpcLb::NullLbTokenEndpointIterator final
private:
std::shared_ptr parent_it_;
RefCountedPtr empty_token_ =
- MakeRefCounted("", nullptr);
+ MakeRefCounted(
+ grpc_event_engine::experimental::Slice(), nullptr);
};
absl::Status GrpcLb::UpdateLocked(UpdateArgs args) {
diff --git a/src/core/load_balancing/lb_policy.h b/src/core/load_balancing/lb_policy.h
index cfdab1ba1a6249..600ef9f4498d2d 100644
--- a/src/core/load_balancing/lb_policy.h
+++ b/src/core/load_balancing/lb_policy.h
@@ -23,9 +23,9 @@
#include
#include
#include
-#include
#include "absl/base/thread_annotations.h"
+#include "absl/container/inlined_vector.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
@@ -33,6 +33,7 @@
#include "absl/types/variant.h"
#include
+#include
#include
#include
#include
@@ -116,29 +117,35 @@ class LoadBalancingPolicy : public InternallyRefCounted {
public:
virtual ~MetadataInterface() = default;
- //////////////////////////////////////////////////////////////////////////
- // TODO(ctiller): DO NOT MAKE THIS A PUBLIC API YET
- // This needs some API design to ensure we can add/remove/replace metadata
- // keys... we're deliberately not doing so to save some time whilst
- // cleaning up the internal metadata representation, but we should add
- // something back before making this a public API.
- //////////////////////////////////////////////////////////////////////////
-
- /// Adds a key/value pair.
- /// Does NOT take ownership of \a key or \a value.
- /// Implementations must ensure that the key and value remain alive
- /// until the call ends. If desired, they may be allocated via
- /// CallState::Alloc().
- virtual void Add(absl::string_view key, absl::string_view value) = 0;
-
- /// Produce a vector of metadata key/value strings for tests.
- virtual std::vector>
- TestOnlyCopyToVector() = 0;
-
virtual absl::optional Lookup(
absl::string_view key, std::string* buffer) const = 0;
};
+ /// A list of metadata mutations to be returned along with a PickResult.
+ class MetadataMutations {
+ public:
+ /// Adds a key/value pair. If the key is already present, the new
+ /// value will be appended with a comma delimiter.
+ void Add(absl::string_view key, absl::string_view value) {
+ Add(key, grpc_event_engine::experimental::Slice::FromCopiedString(value));
+ }
+ void Add(absl::string_view key,
+ grpc_event_engine::experimental::Slice value) {
+ additions_.push_back({key, std::move(value)});
+ }
+
+ private:
+ friend class MetadataMutationHandler;
+
+ // Avoid allocation if up to 3 additions per LB pick. Most expected
+ // use cases should be no more than 2, so this gives us a bit of slack.
+ // But it should be cheap to increase this value if we start seeing use
+ // cases with more than 3 additions.
+ absl::InlinedVector<
+ std::pair, 3>
+ additions_;
+ };
+
/// Arguments used when picking a subchannel for a call.
struct PickArgs {
/// The path of the call. Indicates the RPC service and method name.
@@ -210,11 +217,16 @@ class LoadBalancingPolicy : public InternallyRefCounted {
/// be used.
std::unique_ptr subchannel_call_tracker;
+ /// Metadata mutations to be applied to the call.
+ MetadataMutations metadata_mutations;
+
explicit Complete(
RefCountedPtr sc,
- std::unique_ptr tracker = nullptr)
+ std::unique_ptr tracker = nullptr,
+ MetadataMutations md = MetadataMutations())
: subchannel(std::move(sc)),
- subchannel_call_tracker(std::move(tracker)) {}
+ subchannel_call_tracker(std::move(tracker)),
+ metadata_mutations(std::move(md)) {}
};
/// Pick cannot be completed until something changes on the control
diff --git a/src/core/load_balancing/rls/rls.cc b/src/core/load_balancing/rls/rls.cc
index 7d518d2c869657..0af7cad20a0ff9 100644
--- a/src/core/load_balancing/rls/rls.cc
+++ b/src/core/load_balancing/rls/rls.cc
@@ -108,6 +108,7 @@
#include "src/core/util/json/json_args.h"
#include "src/core/util/json/json_object_loader.h"
#include "src/core/util/json/json_writer.h"
+#include "src/core/util/upb_utils.h"
#include "src/proto/grpc/lookup/v1/rls.upb.h"
using ::grpc_event_engine::experimental::EventEngine;
@@ -315,12 +316,12 @@ class RlsLb final : public LoadBalancingPolicy {
struct ResponseInfo {
absl::Status status;
std::vector targets;
- std::string header_data;
+ grpc_event_engine::experimental::Slice header_data;
std::string ToString() const {
return absl::StrFormat("{status=%s, targets=[%s], header_data=\"%s\"}",
status.ToString(), absl::StrJoin(targets, ","),
- header_data);
+ header_data.as_string_view());
}
};
@@ -464,7 +465,7 @@ class RlsLb final : public LoadBalancingPolicy {
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&RlsLb::mu_) {
return data_expiration_time_;
}
- const std::string& header_data() const
+ const grpc_event_engine::experimental::Slice& header_data() const
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&RlsLb::mu_) {
return header_data_;
}
@@ -543,7 +544,8 @@ class RlsLb final : public LoadBalancingPolicy {
// RLS response states
std::vector> child_policy_wrappers_
ABSL_GUARDED_BY(&RlsLb::mu_);
- std::string header_data_ ABSL_GUARDED_BY(&RlsLb::mu_);
+ grpc_event_engine::experimental::Slice header_data_
+ ABSL_GUARDED_BY(&RlsLb::mu_);
Timestamp data_expiration_time_ ABSL_GUARDED_BY(&RlsLb::mu_) =
Timestamp::InfPast();
Timestamp stale_time_ ABSL_GUARDED_BY(&RlsLb::mu_) = Timestamp::InfPast();
@@ -702,7 +704,7 @@ class RlsLb final : public LoadBalancingPolicy {
RefCountedPtr rls_channel,
std::unique_ptr backoff_state,
grpc_lookup_v1_RouteLookupRequest_Reason reason,
- std::string stale_header_data);
+ grpc_event_engine::experimental::Slice stale_header_data);
~RlsRequest() override;
// Shuts down the request. If the request is still in flight, it is
@@ -730,7 +732,7 @@ class RlsLb final : public LoadBalancingPolicy {
RefCountedPtr rls_channel_;
std::unique_ptr backoff_state_;
grpc_lookup_v1_RouteLookupRequest_Reason reason_;
- std::string stale_header_data_;
+ grpc_event_engine::experimental::Slice stale_header_data_;
// RLS call state.
Timestamp deadline_;
@@ -1259,19 +1261,17 @@ LoadBalancingPolicy::PickResult RlsLb::Cache::Entry::Pick(PickArgs args) {
child_policy_wrappers_.size(),
ConnectivityStateName(child_policy_wrapper->connectivity_state()));
}
- // Add header data.
- // Note that even if the target we're using is in TRANSIENT_FAILURE,
- // the pick might still succeed (e.g., if the child is ring_hash), so
- // we need to pass the right header info down in all cases.
- if (!header_data_.empty()) {
- char* copied_header_data =
- static_cast(args.call_state->Alloc(header_data_.length() + 1));
- strcpy(copied_header_data, header_data_.c_str());
- args.initial_metadata->Add(kRlsHeaderKey, copied_header_data);
- }
auto pick_result = child_policy_wrapper->Pick(args);
lb_policy_->MaybeExportPickCount(kMetricTargetPicks,
child_policy_wrapper->target(), pick_result);
+ // Add header data.
+ if (!header_data_.empty()) {
+ auto* complete_pick =
+ absl::get_if(&pick_result.result);
+ if (complete_pick != nullptr) {
+ complete_pick->metadata_mutations.Add(kRlsHeaderKey, header_data_.Ref());
+ }
+ }
return pick_result;
}
@@ -1682,11 +1682,11 @@ void RlsLb::RlsChannel::StartRlsCall(const RequestKey& key,
std::unique_ptr backoff_state;
grpc_lookup_v1_RouteLookupRequest_Reason reason =
grpc_lookup_v1_RouteLookupRequest_REASON_MISS;
- std::string stale_header_data;
+ grpc_event_engine::experimental::Slice stale_header_data;
if (stale_entry != nullptr) {
backoff_state = stale_entry->TakeBackoffState();
reason = grpc_lookup_v1_RouteLookupRequest_REASON_STALE;
- stale_header_data = stale_entry->header_data();
+ stale_header_data = stale_entry->header_data().Ref();
}
lb_policy_->request_map_.emplace(
key, MakeOrphanable(
@@ -1708,11 +1708,12 @@ void RlsLb::RlsChannel::ResetBackoff() {
// RlsLb::RlsRequest
//
-RlsLb::RlsRequest::RlsRequest(RefCountedPtr lb_policy, RequestKey key,
- RefCountedPtr rls_channel,
- std::unique_ptr backoff_state,
- grpc_lookup_v1_RouteLookupRequest_Reason reason,
- std::string stale_header_data)
+RlsLb::RlsRequest::RlsRequest(
+ RefCountedPtr lb_policy, RequestKey key,
+ RefCountedPtr rls_channel,
+ std::unique_ptr backoff_state,
+ grpc_lookup_v1_RouteLookupRequest_Reason reason,
+ grpc_event_engine::experimental::Slice stale_header_data)
: InternallyRefCounted(
GRPC_TRACE_FLAG_ENABLED(rls_lb) ? "RlsRequest" : nullptr),
lb_policy_(std::move(lb_policy)),
@@ -1890,8 +1891,7 @@ grpc_byte_buffer* RlsLb::RlsRequest::MakeRequestProto() {
grpc_lookup_v1_RouteLookupRequest_set_reason(req, reason_);
if (!stale_header_data_.empty()) {
grpc_lookup_v1_RouteLookupRequest_set_stale_header_data(
- req, upb_StringView_FromDataAndSize(stale_header_data_.data(),
- stale_header_data_.size()));
+ req, StdStringToUpbString(stale_header_data_.as_string_view()));
}
size_t len;
char* buf =
@@ -1934,7 +1934,8 @@ RlsLb::ResponseInfo RlsLb::RlsRequest::ParseResponseProto() {
upb_StringView header_data_strview =
grpc_lookup_v1_RouteLookupResponse_header_data(response);
response_info.header_data =
- std::string(header_data_strview.data, header_data_strview.size);
+ grpc_event_engine::experimental::Slice::FromCopiedBuffer(
+ header_data_strview.data, header_data_strview.size);
return response_info;
}
diff --git a/src/core/xds/grpc/upb_utils.h b/src/core/util/upb_utils.h
similarity index 87%
rename from src/core/xds/grpc/upb_utils.h
rename to src/core/util/upb_utils.h
index e2123f39cbc42f..a5b1ac897fc9c3 100644
--- a/src/core/xds/grpc/upb_utils.h
+++ b/src/core/util/upb_utils.h
@@ -14,16 +14,14 @@
// limitations under the License.
//
-#ifndef GRPC_SRC_CORE_XDS_GRPC_UPB_UTILS_H
-#define GRPC_SRC_CORE_XDS_GRPC_UPB_UTILS_H
+#ifndef GRPC_SRC_CORE_UTIL_UPB_UTILS_H
+#define GRPC_SRC_CORE_UTIL_UPB_UTILS_H
#include
#include "absl/strings/string_view.h"
#include "upb/base/string_view.h"
-#include
-
namespace grpc_core {
// Works for both std::string and absl::string_view.
@@ -42,4 +40,4 @@ inline std::string UpbStringToStdString(const upb_StringView& str) {
} // namespace grpc_core
-#endif // GRPC_SRC_CORE_XDS_GRPC_UPB_UTILS_H
+#endif // GRPC_SRC_CORE_UTIL_UPB_UTILS_H
diff --git a/src/core/xds/grpc/xds_client_grpc.cc b/src/core/xds/grpc/xds_client_grpc.cc
index 58771564a293b6..a1899f47673f42 100644
--- a/src/core/xds/grpc/xds_client_grpc.cc
+++ b/src/core/xds/grpc/xds_client_grpc.cc
@@ -55,7 +55,7 @@
#include "src/core/lib/slice/slice_internal.h"
#include "src/core/lib/transport/error_utils.h"
#include "src/core/telemetry/metrics.h"
-#include "src/core/xds/grpc/upb_utils.h"
+#include "src/core/util/upb_utils.h"
#include "src/core/xds/grpc/xds_bootstrap_grpc.h"
#include "src/core/xds/grpc/xds_transport_grpc.h"
#include "src/core/xds/xds_client/xds_api.h"
diff --git a/src/core/xds/grpc/xds_cluster.cc b/src/core/xds/grpc/xds_cluster.cc
index 2d2f4e6c7282b1..7b232fa0ef8ab4 100644
--- a/src/core/xds/grpc/xds_cluster.cc
+++ b/src/core/xds/grpc/xds_cluster.cc
@@ -66,7 +66,7 @@
#include "src/core/lib/matchers/matchers.h"
#include "src/core/load_balancing/lb_policy_registry.h"
#include "src/core/util/json/json_writer.h"
-#include "src/core/xds/grpc/upb_utils.h"
+#include "src/core/util/upb_utils.h"
#include "src/core/xds/grpc/xds_common_types.h"
#include "src/core/xds/grpc/xds_lb_policy_registry.h"
#include "src/core/xds/xds_client/xds_client.h"
diff --git a/src/core/xds/grpc/xds_common_types.cc b/src/core/xds/grpc/xds_common_types.cc
index ff0a630a992731..2e8fafda5883a5 100644
--- a/src/core/xds/grpc/xds_common_types.cc
+++ b/src/core/xds/grpc/xds_common_types.cc
@@ -45,7 +45,7 @@
#include
#include "src/core/util/json/json_reader.h"
-#include "src/core/xds/grpc/upb_utils.h"
+#include "src/core/util/upb_utils.h"
#include "src/core/xds/grpc/xds_bootstrap_grpc.h"
#include "src/core/xds/xds_client/xds_client.h"
diff --git a/src/core/xds/grpc/xds_endpoint.cc b/src/core/xds/grpc/xds_endpoint.cc
index 46fb1f81887781..dc761931d66e6d 100644
--- a/src/core/xds/grpc/xds_endpoint.cc
+++ b/src/core/xds/grpc/xds_endpoint.cc
@@ -51,7 +51,7 @@
#include "src/core/lib/gprpp/validation_errors.h"
#include "src/core/lib/iomgr/resolved_address.h"
#include "src/core/util/string.h"
-#include "src/core/xds/grpc/upb_utils.h"
+#include "src/core/util/upb_utils.h"
#include "src/core/xds/grpc/xds_health_status.h"
#include "src/core/xds/xds_client/xds_resource_type.h"
diff --git a/src/core/xds/grpc/xds_http_rbac_filter.cc b/src/core/xds/grpc/xds_http_rbac_filter.cc
index 6ba09042c88aa3..3fadf5d2ebf90e 100644
--- a/src/core/xds/grpc/xds_http_rbac_filter.cc
+++ b/src/core/xds/grpc/xds_http_rbac_filter.cc
@@ -50,7 +50,7 @@
#include "src/core/util/json/json.h"
#include "src/core/util/json/json_writer.h"
#include "src/core/util/string.h"
-#include "src/core/xds/grpc/upb_utils.h"
+#include "src/core/util/upb_utils.h"
#include "src/core/xds/grpc/xds_audit_logger_registry.h"
#include "src/core/xds/grpc/xds_bootstrap_grpc.h"
#include "src/core/xds/xds_client/xds_client.h"
diff --git a/src/core/xds/grpc/xds_http_stateful_session_filter.cc b/src/core/xds/grpc/xds_http_stateful_session_filter.cc
index 7b0b05219cd1fd..0c4203dd14067a 100644
--- a/src/core/xds/grpc/xds_http_stateful_session_filter.cc
+++ b/src/core/xds/grpc/xds_http_stateful_session_filter.cc
@@ -39,7 +39,7 @@
#include "src/core/lib/gprpp/validation_errors.h"
#include "src/core/util/json/json.h"
#include "src/core/util/json/json_writer.h"
-#include "src/core/xds/grpc/upb_utils.h"
+#include "src/core/util/upb_utils.h"
#include "src/core/xds/grpc/xds_common_types.h"
#include "src/core/xds/grpc/xds_http_filters.h"
diff --git a/src/core/xds/grpc/xds_listener.cc b/src/core/xds/grpc/xds_listener.cc
index 7ea5822bf633ac..02379bfe5a7fb8 100644
--- a/src/core/xds/grpc/xds_listener.cc
+++ b/src/core/xds/grpc/xds_listener.cc
@@ -55,7 +55,7 @@
#include "src/core/lib/gprpp/validation_errors.h"
#include "src/core/lib/iomgr/sockaddr.h"
#include "src/core/lib/matchers/matchers.h"
-#include "src/core/xds/grpc/upb_utils.h"
+#include "src/core/util/upb_utils.h"
#include "src/core/xds/grpc/xds_common_types.h"
#include "src/core/xds/xds_client/xds_resource_type.h"
diff --git a/src/core/xds/grpc/xds_route_config.cc b/src/core/xds/grpc/xds_route_config.cc
index d2933d8413a46d..c2f8a406207633 100644
--- a/src/core/xds/grpc/xds_route_config.cc
+++ b/src/core/xds/grpc/xds_route_config.cc
@@ -70,7 +70,7 @@
#include "src/core/util/json/json.h"
#include "src/core/util/json/json_writer.h"
#include "src/core/util/string.h"
-#include "src/core/xds/grpc/upb_utils.h"
+#include "src/core/util/upb_utils.h"
#include "src/core/xds/grpc/xds_cluster_specifier_plugin.h"
#include "src/core/xds/grpc/xds_common_types.h"
#include "src/core/xds/grpc/xds_http_filters.h"
diff --git a/src/core/xds/xds_client/xds_api.cc b/src/core/xds/xds_client/xds_api.cc
index 6964d9b993135d..8ae5e438abf7d5 100644
--- a/src/core/xds/xds_client/xds_api.cc
+++ b/src/core/xds/xds_client/xds_api.cc
@@ -48,7 +48,7 @@
#include
#include "src/core/util/json/json.h"
-#include "src/core/xds/grpc/upb_utils.h"
+#include "src/core/util/upb_utils.h"
#include "src/core/xds/xds_client/xds_client.h"
// IWYU pragma: no_include "upb/msg_internal.h"
diff --git a/src/core/xds/xds_client/xds_client.cc b/src/core/xds/xds_client/xds_client.cc
index 1f3c9b1926f4ac..8a9456cdad2f4a 100644
--- a/src/core/xds/xds_client/xds_client.cc
+++ b/src/core/xds/xds_client/xds_client.cc
@@ -53,7 +53,7 @@
#include "src/core/lib/gprpp/sync.h"
#include "src/core/lib/iomgr/exec_ctx.h"
#include "src/core/lib/uri/uri_parser.h"
-#include "src/core/xds/grpc/upb_utils.h"
+#include "src/core/util/upb_utils.h"
#include "src/core/xds/xds_client/xds_api.h"
#include "src/core/xds/xds_client/xds_bootstrap.h"
#include "src/core/xds/xds_client/xds_client_stats.h"
diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py
index 4d4b22f2225dba..70c1bbfa63ddcb 100644
--- a/src/python/grpcio/grpc_core_dependencies.py
+++ b/src/python/grpcio/grpc_core_dependencies.py
@@ -27,6 +27,7 @@
'src/core/client_channel/direct_channel.cc',
'src/core/client_channel/dynamic_filters.cc',
'src/core/client_channel/global_subchannel_pool.cc',
+ 'src/core/client_channel/lb_metadata.cc',
'src/core/client_channel/load_balanced_call_destination.cc',
'src/core/client_channel/local_subchannel_pool.cc',
'src/core/client_channel/retry_filter.cc',
diff --git a/test/core/load_balancing/lb_policy_test_lib.h b/test/core/load_balancing/lb_policy_test_lib.h
index aa7ea8c9ae8b17..fcd85c8928eeec 100644
--- a/test/core/load_balancing/lb_policy_test_lib.h
+++ b/test/core/load_balancing/lb_policy_test_lib.h
@@ -620,20 +620,7 @@ class LoadBalancingPolicyTest : public ::testing::Test {
explicit FakeMetadata(std::map metadata)
: metadata_(std::move(metadata)) {}
- const std::map& metadata() const {
- return metadata_;
- }
-
private:
- void Add(absl::string_view key, absl::string_view value) override {
- metadata_[std::string(key)] = std::string(value);
- }
-
- std::vector> TestOnlyCopyToVector()
- override {
- return {}; // Not used.
- }
-
absl::optional Lookup(
absl::string_view key, std::string* /*buffer*/) const override {
auto it = metadata_.find(std::string(key));
diff --git a/test/core/test_util/BUILD b/test/core/test_util/BUILD
index d2e935f75f4caf..e0962df86f3703 100644
--- a/test/core/test_util/BUILD
+++ b/test/core/test_util/BUILD
@@ -333,10 +333,12 @@ grpc_cc_library(
"//:uri_parser",
"//src/core:channel_args",
"//src/core:delegating_helper",
+ "//src/core:down_cast",
"//src/core:error",
"//src/core:grpc_backend_metric_data",
"//src/core:json",
"//src/core:json_util",
+ "//src/core:lb_metadata",
"//src/core:lb_policy",
"//src/core:lb_policy_factory",
"//src/core:lb_policy_registry",
diff --git a/test/core/test_util/test_lb_policies.cc b/test/core/test_util/test_lb_policies.cc
index aea9e9194140a4..2d674333b12b84 100644
--- a/test/core/test_util/test_lb_policies.cc
+++ b/test/core/test_util/test_lb_policies.cc
@@ -30,9 +30,11 @@
#include
#include
+#include "src/core/client_channel/lb_metadata.h"
#include "src/core/lib/address_utils/parse_address.h"
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/config/core_configuration.h"
+#include "src/core/lib/gprpp/down_cast.h"
#include "src/core/lib/gprpp/orphanable.h"
#include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/lib/gprpp/status_helper.h"
@@ -129,7 +131,8 @@ class TestPickArgsLb : public ForwardingLoadBalancingPolicy {
// Report args seen.
PickArgsSeen args_seen;
args_seen.path = std::string(args.path);
- args_seen.metadata = args.initial_metadata->TestOnlyCopyToVector();
+ args_seen.metadata =
+ DownCast(args.initial_metadata)->TestOnlyCopyToVector();
cb_(args_seen);
// Do pick.
return delegate_picker_->Pick(args);
@@ -269,7 +272,8 @@ class InterceptRecvTrailingMetadataLoadBalancingPolicy
args_seen.status = args.status;
args_seen.backend_metric_data =
args.backend_metric_accessor->GetBackendMetricData();
- args_seen.metadata = args.trailing_metadata->TestOnlyCopyToVector();
+ args_seen.metadata =
+ DownCast(args.trailing_metadata)->TestOnlyCopyToVector();
cb_(args_seen);
}
diff --git a/test/core/transport/metadata_map_test.cc b/test/core/transport/metadata_map_test.cc
index 12e42c7167a352..cc977cd3c4e9a7 100644
--- a/test/core/transport/metadata_map_test.cc
+++ b/test/core/transport/metadata_map_test.cc
@@ -81,7 +81,7 @@ TEST(MetadataMapTest, SimpleOps) {
// EXPECT_EQ it later.
class FakeEncoder {
public:
- std::string output() { return output_; }
+ const std::string& output() { return output_; }
void Encode(const Slice& key, const Slice& value) {
output_ += absl::StrCat("UNKNOWN METADATUM: key=", key.as_string_view(),
@@ -128,6 +128,26 @@ TEST(MetadataMapTest, NonEncodableTrait) {
EXPECT_EQ(map.DebugString(), "GrpcStreamNetworkState: not sent on wire");
}
+TEST(MetadataMapTest, NonTraitKeyWithMultipleValues) {
+ FakeEncoder encoder;
+ TimeoutOnlyMetadataMap map;
+ const absl::string_view kKey = "key";
+ map.Append(kKey, Slice::FromStaticString("value1"),
+ [](absl::string_view error, const Slice& value) {
+ LOG(ERROR) << error << " value:" << value.as_string_view();
+ });
+ map.Append(kKey, Slice::FromStaticString("value2"),
+ [](absl::string_view error, const Slice& value) {
+ LOG(ERROR) << error << " value:" << value.as_string_view();
+ });
+ map.Encode(&encoder);
+ EXPECT_EQ(encoder.output(),
+ "UNKNOWN METADATUM: key=key value=value1\n"
+ "UNKNOWN METADATUM: key=key value=value2\n");
+ std::string buffer;
+ EXPECT_EQ(map.GetStringValue(kKey, &buffer), "value1,value2");
+}
+
TEST(DebugStringBuilderTest, OneAddAfterRedaction) {
metadata_detail::DebugStringBuilder b;
b.AddAfterRedaction(ContentTypeMetadata::key(), "AddValue01");
diff --git a/test/core/xds/xds_common_types_test.cc b/test/core/xds/xds_common_types_test.cc
index b934a977cb6221..6202407862f5ab 100644
--- a/test/core/xds/xds_common_types_test.cc
+++ b/test/core/xds/xds_common_types_test.cc
@@ -45,7 +45,7 @@
#include "src/core/lib/gprpp/validation_errors.h"
#include "src/core/lib/matchers/matchers.h"
#include "src/core/util/json/json_writer.h"
-#include "src/core/xds/grpc/upb_utils.h"
+#include "src/core/util/upb_utils.h"
#include "src/core/xds/grpc/xds_bootstrap_grpc.h"
#include "src/core/xds/xds_client/xds_bootstrap.h"
#include "src/core/xds/xds_client/xds_client.h"
diff --git a/test/cpp/interop/rpc_behavior_lb_policy.cc b/test/cpp/interop/rpc_behavior_lb_policy.cc
index b065af99ad9699..1e6ab254fd2a77 100644
--- a/test/cpp/interop/rpc_behavior_lb_policy.cc
+++ b/test/cpp/interop/rpc_behavior_lb_policy.cc
@@ -111,12 +111,17 @@ class RpcBehaviorLbPolicy : public LoadBalancingPolicy {
rpc_behavior_(rpc_behavior) {}
PickResult Pick(PickArgs args) override {
- char* rpc_behavior_copy = static_cast(
- args.call_state->Alloc(rpc_behavior_.length() + 1));
- strcpy(rpc_behavior_copy, rpc_behavior_.c_str());
- args.initial_metadata->Add(kRpcBehaviorMetadataKey, rpc_behavior_copy);
// Do pick.
- return delegate_picker_->Pick(args);
+ auto pick_result = delegate_picker_->Pick(args);
+ // Add metadata.
+ auto* complete_pick =
+ absl::get_if(&pick_result.result);
+ if (complete_pick != nullptr) {
+ complete_pick->metadata_mutations.Add(kRpcBehaviorMetadataKey,
+ rpc_behavior_);
+ }
+ // Return result.
+ return pick_result;
}
private:
diff --git a/tools/doxygen/Doxyfile.c++.internal b/tools/doxygen/Doxyfile.c++.internal
index cf6eb657b4f445..7065c3262d76b0 100644
--- a/tools/doxygen/Doxyfile.c++.internal
+++ b/tools/doxygen/Doxyfile.c++.internal
@@ -1115,6 +1115,8 @@ src/core/client_channel/dynamic_filters.cc \
src/core/client_channel/dynamic_filters.h \
src/core/client_channel/global_subchannel_pool.cc \
src/core/client_channel/global_subchannel_pool.h \
+src/core/client_channel/lb_metadata.cc \
+src/core/client_channel/lb_metadata.h \
src/core/client_channel/load_balanced_call_destination.cc \
src/core/client_channel/load_balanced_call_destination.h \
src/core/client_channel/local_subchannel_pool.cc \
@@ -2951,6 +2953,7 @@ src/core/util/time.cc \
src/core/util/time_precise.cc \
src/core/util/time_precise.h \
src/core/util/tmpfile.h \
+src/core/util/upb_utils.h \
src/core/util/useful.h \
src/core/util/windows/cpu.cc \
src/core/util/windows/log.cc \
@@ -2963,7 +2966,6 @@ src/core/xds/grpc/certificate_provider_store.cc \
src/core/xds/grpc/certificate_provider_store.h \
src/core/xds/grpc/file_watcher_certificate_provider_factory.cc \
src/core/xds/grpc/file_watcher_certificate_provider_factory.h \
-src/core/xds/grpc/upb_utils.h \
src/core/xds/grpc/xds_audit_logger_registry.cc \
src/core/xds/grpc/xds_audit_logger_registry.h \
src/core/xds/grpc/xds_bootstrap_grpc.cc \
diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal
index 6b3601d8359357..de0d4e57c86a59 100644
--- a/tools/doxygen/Doxyfile.core.internal
+++ b/tools/doxygen/Doxyfile.core.internal
@@ -915,6 +915,8 @@ src/core/client_channel/dynamic_filters.cc \
src/core/client_channel/dynamic_filters.h \
src/core/client_channel/global_subchannel_pool.cc \
src/core/client_channel/global_subchannel_pool.h \
+src/core/client_channel/lb_metadata.cc \
+src/core/client_channel/lb_metadata.h \
src/core/client_channel/load_balanced_call_destination.cc \
src/core/client_channel/load_balanced_call_destination.h \
src/core/client_channel/local_subchannel_pool.cc \
@@ -2730,6 +2732,7 @@ src/core/util/time.cc \
src/core/util/time_precise.cc \
src/core/util/time_precise.h \
src/core/util/tmpfile.h \
+src/core/util/upb_utils.h \
src/core/util/useful.h \
src/core/util/windows/cpu.cc \
src/core/util/windows/log.cc \
@@ -2742,7 +2745,6 @@ src/core/xds/grpc/certificate_provider_store.cc \
src/core/xds/grpc/certificate_provider_store.h \
src/core/xds/grpc/file_watcher_certificate_provider_factory.cc \
src/core/xds/grpc/file_watcher_certificate_provider_factory.h \
-src/core/xds/grpc/upb_utils.h \
src/core/xds/grpc/xds_audit_logger_registry.cc \
src/core/xds/grpc/xds_audit_logger_registry.h \
src/core/xds/grpc/xds_bootstrap_grpc.cc \
diff --git a/tools/run_tests/sanity/core_banned_functions.py b/tools/run_tests/sanity/core_banned_functions.py
index 7a4270ecf0df4e..24b38f3142169c 100755
--- a/tools/run_tests/sanity/core_banned_functions.py
+++ b/tools/run_tests/sanity/core_banned_functions.py
@@ -24,7 +24,10 @@
# map of banned function signature to allowlist
BANNED_EXCEPT = {
- "grpc_slice_from_static_buffer(": ["src/core/lib/slice/slice.cc"],
+ "grpc_slice_from_static_buffer(": [
+ "src/core/lib/slice/slice.cc",
+ "src/core/load_balancing/grpclb/grpclb.cc",
+ ],
"grpc_resource_quota_ref(": ["src/core/lib/resource_quota/api.cc"],
"grpc_resource_quota_unref(": [
"src/core/lib/resource_quota/api.cc",