• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2018 gRPC authors.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //     http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 
17 #include <grpc/support/port_platform.h>
18 
19 #include "src/core/ext/xds/xds_api.h"
20 
21 #include <stdint.h>
22 #include <stdlib.h>
23 
24 #include <set>
25 #include <string>
26 #include <vector>
27 
28 #include "absl/strings/str_cat.h"
29 #include "absl/strings/strip.h"
30 #include "envoy/config/core/v3/base.upb.h"
31 #include "envoy/config/endpoint/v3/load_report.upb.h"
32 #include "envoy/service/discovery/v3/discovery.upb.h"
33 #include "envoy/service/discovery/v3/discovery.upbdefs.h"
34 #include "envoy/service/load_stats/v3/lrs.upb.h"
35 #include "envoy/service/load_stats/v3/lrs.upbdefs.h"
36 #include "envoy/service/status/v3/csds.upb.h"
37 #include "google/protobuf/any.upb.h"
38 #include "google/protobuf/duration.upb.h"
39 #include "google/protobuf/struct.upb.h"
40 #include "google/protobuf/timestamp.upb.h"
41 #include "google/rpc/status.upb.h"
42 #include "upb/base/string_view.h"
43 #include "upb/mem/arena.hpp"
44 #include "upb/reflection/def.h"
45 #include "upb/text/encode.h"
46 
47 #include <grpc/status.h>
48 #include <grpc/support/log.h>
49 #include <grpc/support/time.h>
50 
51 #include "src/core/ext/xds/upb_utils.h"
52 #include "src/core/ext/xds/xds_client.h"
53 #include "src/core/lib/json/json.h"
54 
55 // IWYU pragma: no_include "upb/msg_internal.h"
56 
57 namespace grpc_core {
58 
XdsApi(XdsClient * client,TraceFlag * tracer,const XdsBootstrap::Node * node,upb::DefPool * def_pool,std::string user_agent_name,std::string user_agent_version)59 XdsApi::XdsApi(XdsClient* client, TraceFlag* tracer,
60                const XdsBootstrap::Node* node, upb::DefPool* def_pool,
61                std::string user_agent_name, std::string user_agent_version)
62     : client_(client),
63       tracer_(tracer),
64       node_(node),
65       def_pool_(def_pool),
66       user_agent_name_(std::move(user_agent_name)),
67       user_agent_version_(std::move(user_agent_version)) {}
68 
69 namespace {
70 
71 struct XdsApiContext {
72   XdsClient* client;
73   TraceFlag* tracer;
74   upb_DefPool* def_pool;
75   upb_Arena* arena;
76 };
77 
78 void PopulateMetadataValue(google_protobuf_Value* value_pb, const Json& value,
79                            upb_Arena* arena);
80 
PopulateListValue(google_protobuf_ListValue * list_value,const Json::Array & values,upb_Arena * arena)81 void PopulateListValue(google_protobuf_ListValue* list_value,
82                        const Json::Array& values, upb_Arena* arena) {
83   for (const auto& value : values) {
84     auto* value_pb = google_protobuf_ListValue_add_values(list_value, arena);
85     PopulateMetadataValue(value_pb, value, arena);
86   }
87 }
88 
PopulateMetadata(google_protobuf_Struct * metadata_pb,const Json::Object & metadata,upb_Arena * arena)89 void PopulateMetadata(google_protobuf_Struct* metadata_pb,
90                       const Json::Object& metadata, upb_Arena* arena) {
91   for (const auto& p : metadata) {
92     google_protobuf_Value* value = google_protobuf_Value_new(arena);
93     PopulateMetadataValue(value, p.second, arena);
94     google_protobuf_Struct_fields_set(
95         metadata_pb, StdStringToUpbString(p.first), value, arena);
96   }
97 }
98 
PopulateMetadataValue(google_protobuf_Value * value_pb,const Json & value,upb_Arena * arena)99 void PopulateMetadataValue(google_protobuf_Value* value_pb, const Json& value,
100                            upb_Arena* arena) {
101   switch (value.type()) {
102     case Json::Type::kNull:
103       google_protobuf_Value_set_null_value(value_pb, 0);
104       break;
105     case Json::Type::kNumber:
106       google_protobuf_Value_set_number_value(
107           value_pb, strtod(value.string().c_str(), nullptr));
108       break;
109     case Json::Type::kString:
110       google_protobuf_Value_set_string_value(
111           value_pb, StdStringToUpbString(value.string()));
112       break;
113     case Json::Type::kBoolean:
114       google_protobuf_Value_set_bool_value(value_pb, value.boolean());
115       break;
116     case Json::Type::kObject: {
117       google_protobuf_Struct* struct_value =
118           google_protobuf_Value_mutable_struct_value(value_pb, arena);
119       PopulateMetadata(struct_value, value.object(), arena);
120       break;
121     }
122     case Json::Type::kArray: {
123       google_protobuf_ListValue* list_value =
124           google_protobuf_Value_mutable_list_value(value_pb, arena);
125       PopulateListValue(list_value, value.array(), arena);
126       break;
127     }
128   }
129 }
130 
MaybeLogDiscoveryRequest(const XdsApiContext & context,const envoy_service_discovery_v3_DiscoveryRequest * request)131 void MaybeLogDiscoveryRequest(
132     const XdsApiContext& context,
133     const envoy_service_discovery_v3_DiscoveryRequest* request) {
134   if (GRPC_TRACE_FLAG_ENABLED(*context.tracer) &&
135       gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
136     const upb_MessageDef* msg_type =
137         envoy_service_discovery_v3_DiscoveryRequest_getmsgdef(context.def_pool);
138     char buf[10240];
139     upb_TextEncode(reinterpret_cast<const upb_Message*>(request), msg_type,
140                    nullptr, 0, buf, sizeof(buf));
141     gpr_log(GPR_DEBUG, "[xds_client %p] constructed ADS request: %s",
142             context.client, buf);
143   }
144 }
145 
SerializeDiscoveryRequest(const XdsApiContext & context,envoy_service_discovery_v3_DiscoveryRequest * request)146 std::string SerializeDiscoveryRequest(
147     const XdsApiContext& context,
148     envoy_service_discovery_v3_DiscoveryRequest* request) {
149   size_t output_length;
150   char* output = envoy_service_discovery_v3_DiscoveryRequest_serialize(
151       request, context.arena, &output_length);
152   return std::string(output, output_length);
153 }
154 
155 }  // namespace
156 
PopulateNode(envoy_config_core_v3_Node * node_msg,upb_Arena * arena)157 void XdsApi::PopulateNode(envoy_config_core_v3_Node* node_msg,
158                           upb_Arena* arena) {
159   if (node_ != nullptr) {
160     if (!node_->id().empty()) {
161       envoy_config_core_v3_Node_set_id(node_msg,
162                                        StdStringToUpbString(node_->id()));
163     }
164     if (!node_->cluster().empty()) {
165       envoy_config_core_v3_Node_set_cluster(
166           node_msg, StdStringToUpbString(node_->cluster()));
167     }
168     if (!node_->metadata().empty()) {
169       google_protobuf_Struct* metadata =
170           envoy_config_core_v3_Node_mutable_metadata(node_msg, arena);
171       PopulateMetadata(metadata, node_->metadata(), arena);
172     }
173     if (!node_->locality_region().empty() || !node_->locality_zone().empty() ||
174         !node_->locality_sub_zone().empty()) {
175       envoy_config_core_v3_Locality* locality =
176           envoy_config_core_v3_Node_mutable_locality(node_msg, arena);
177       if (!node_->locality_region().empty()) {
178         envoy_config_core_v3_Locality_set_region(
179             locality, StdStringToUpbString(node_->locality_region()));
180       }
181       if (!node_->locality_zone().empty()) {
182         envoy_config_core_v3_Locality_set_zone(
183             locality, StdStringToUpbString(node_->locality_zone()));
184       }
185       if (!node_->locality_sub_zone().empty()) {
186         envoy_config_core_v3_Locality_set_sub_zone(
187             locality, StdStringToUpbString(node_->locality_sub_zone()));
188       }
189     }
190   }
191   envoy_config_core_v3_Node_set_user_agent_name(
192       node_msg, StdStringToUpbString(user_agent_name_));
193   envoy_config_core_v3_Node_set_user_agent_version(
194       node_msg, StdStringToUpbString(user_agent_version_));
195   envoy_config_core_v3_Node_add_client_features(
196       node_msg,
197       upb_StringView_FromString("envoy.lb.does_not_support_overprovisioning"),
198       arena);
199 }
200 
CreateAdsRequest(absl::string_view type_url,absl::string_view version,absl::string_view nonce,const std::vector<std::string> & resource_names,absl::Status status,bool populate_node)201 std::string XdsApi::CreateAdsRequest(
202     absl::string_view type_url, absl::string_view version,
203     absl::string_view nonce, const std::vector<std::string>& resource_names,
204     absl::Status status, bool populate_node) {
205   upb::Arena arena;
206   const XdsApiContext context = {client_, tracer_, def_pool_->ptr(),
207                                  arena.ptr()};
208   // Create a request.
209   envoy_service_discovery_v3_DiscoveryRequest* request =
210       envoy_service_discovery_v3_DiscoveryRequest_new(arena.ptr());
211   // Set type_url.
212   std::string type_url_str = absl::StrCat("type.googleapis.com/", type_url);
213   envoy_service_discovery_v3_DiscoveryRequest_set_type_url(
214       request, StdStringToUpbString(type_url_str));
215   // Set version_info.
216   if (!version.empty()) {
217     envoy_service_discovery_v3_DiscoveryRequest_set_version_info(
218         request, StdStringToUpbString(version));
219   }
220   // Set nonce.
221   if (!nonce.empty()) {
222     envoy_service_discovery_v3_DiscoveryRequest_set_response_nonce(
223         request, StdStringToUpbString(nonce));
224   }
225   // Set error_detail if it's a NACK.
226   std::string error_string_storage;
227   if (!status.ok()) {
228     google_rpc_Status* error_detail =
229         envoy_service_discovery_v3_DiscoveryRequest_mutable_error_detail(
230             request, arena.ptr());
231     // Hard-code INVALID_ARGUMENT as the status code.
232     // TODO(roth): If at some point we decide we care about this value,
233     // we could attach a status code to the individual errors where we
234     // generate them in the parsing code, and then use that here.
235     google_rpc_Status_set_code(error_detail, GRPC_STATUS_INVALID_ARGUMENT);
236     // Error description comes from the status that was passed in.
237     error_string_storage = std::string(status.message());
238     upb_StringView error_description =
239         StdStringToUpbString(error_string_storage);
240     google_rpc_Status_set_message(error_detail, error_description);
241   }
242   // Populate node.
243   if (populate_node) {
244     envoy_config_core_v3_Node* node_msg =
245         envoy_service_discovery_v3_DiscoveryRequest_mutable_node(request,
246                                                                  arena.ptr());
247     PopulateNode(node_msg, arena.ptr());
248     envoy_config_core_v3_Node_add_client_features(
249         node_msg, upb_StringView_FromString("xds.config.resource-in-sotw"),
250         context.arena);
251   }
252   // Add resource_names.
253   for (const std::string& resource_name : resource_names) {
254     envoy_service_discovery_v3_DiscoveryRequest_add_resource_names(
255         request, StdStringToUpbString(resource_name), arena.ptr());
256   }
257   MaybeLogDiscoveryRequest(context, request);
258   return SerializeDiscoveryRequest(context, request);
259 }
260 
261 namespace {
262 
MaybeLogDiscoveryResponse(const XdsApiContext & context,const envoy_service_discovery_v3_DiscoveryResponse * response)263 void MaybeLogDiscoveryResponse(
264     const XdsApiContext& context,
265     const envoy_service_discovery_v3_DiscoveryResponse* response) {
266   if (GRPC_TRACE_FLAG_ENABLED(*context.tracer) &&
267       gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
268     const upb_MessageDef* msg_type =
269         envoy_service_discovery_v3_DiscoveryResponse_getmsgdef(
270             context.def_pool);
271     char buf[10240];
272     upb_TextEncode(reinterpret_cast<const upb_Message*>(response), msg_type,
273                    nullptr, 0, buf, sizeof(buf));
274     gpr_log(GPR_DEBUG, "[xds_client %p] received response: %s", context.client,
275             buf);
276   }
277 }
278 
279 }  // namespace
280 
ParseAdsResponse(absl::string_view encoded_response,AdsResponseParserInterface * parser)281 absl::Status XdsApi::ParseAdsResponse(absl::string_view encoded_response,
282                                       AdsResponseParserInterface* parser) {
283   upb::Arena arena;
284   const XdsApiContext context = {client_, tracer_, def_pool_->ptr(),
285                                  arena.ptr()};
286   // Decode the response.
287   const envoy_service_discovery_v3_DiscoveryResponse* response =
288       envoy_service_discovery_v3_DiscoveryResponse_parse(
289           encoded_response.data(), encoded_response.size(), arena.ptr());
290   // If decoding fails, report a fatal error and return.
291   if (response == nullptr) {
292     return absl::InvalidArgumentError("Can't decode DiscoveryResponse.");
293   }
294   MaybeLogDiscoveryResponse(context, response);
295   // Report the type_url, version, nonce, and number of resources to the parser.
296   AdsResponseParserInterface::AdsResponseFields fields;
297   fields.type_url = std::string(absl::StripPrefix(
298       UpbStringToAbsl(
299           envoy_service_discovery_v3_DiscoveryResponse_type_url(response)),
300       "type.googleapis.com/"));
301   fields.version = UpbStringToStdString(
302       envoy_service_discovery_v3_DiscoveryResponse_version_info(response));
303   fields.nonce = UpbStringToStdString(
304       envoy_service_discovery_v3_DiscoveryResponse_nonce(response));
305   size_t num_resources;
306   const google_protobuf_Any* const* resources =
307       envoy_service_discovery_v3_DiscoveryResponse_resources(response,
308                                                              &num_resources);
309   fields.num_resources = num_resources;
310   absl::Status status = parser->ProcessAdsResponseFields(std::move(fields));
311   if (!status.ok()) return status;
312   // Process each resource.
313   for (size_t i = 0; i < num_resources; ++i) {
314     absl::string_view type_url = absl::StripPrefix(
315         UpbStringToAbsl(google_protobuf_Any_type_url(resources[i])),
316         "type.googleapis.com/");
317     absl::string_view serialized_resource =
318         UpbStringToAbsl(google_protobuf_Any_value(resources[i]));
319     // Unwrap Resource messages, if so wrapped.
320     absl::string_view resource_name;
321     if (type_url == "envoy.service.discovery.v3.Resource") {
322       const auto* resource_wrapper = envoy_service_discovery_v3_Resource_parse(
323           serialized_resource.data(), serialized_resource.size(), arena.ptr());
324       if (resource_wrapper == nullptr) {
325         parser->ResourceWrapperParsingFailed(
326             i, "Can't decode Resource proto wrapper");
327         continue;
328       }
329       const auto* resource =
330           envoy_service_discovery_v3_Resource_resource(resource_wrapper);
331       if (resource == nullptr) {
332         parser->ResourceWrapperParsingFailed(
333             i, "No resource present in Resource proto wrapper");
334         continue;
335       }
336       type_url = absl::StripPrefix(
337           UpbStringToAbsl(google_protobuf_Any_type_url(resource)),
338           "type.googleapis.com/");
339       serialized_resource =
340           UpbStringToAbsl(google_protobuf_Any_value(resource));
341       resource_name = UpbStringToAbsl(
342           envoy_service_discovery_v3_Resource_name(resource_wrapper));
343     }
344     parser->ParseResource(context.arena, i, type_url, resource_name,
345                           serialized_resource);
346   }
347   return absl::OkStatus();
348 }
349 
350 namespace {
351 
MaybeLogLrsRequest(const XdsApiContext & context,const envoy_service_load_stats_v3_LoadStatsRequest * request)352 void MaybeLogLrsRequest(
353     const XdsApiContext& context,
354     const envoy_service_load_stats_v3_LoadStatsRequest* request) {
355   if (GRPC_TRACE_FLAG_ENABLED(*context.tracer) &&
356       gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
357     const upb_MessageDef* msg_type =
358         envoy_service_load_stats_v3_LoadStatsRequest_getmsgdef(
359             context.def_pool);
360     char buf[10240];
361     upb_TextEncode(reinterpret_cast<const upb_Message*>(request), msg_type,
362                    nullptr, 0, buf, sizeof(buf));
363     gpr_log(GPR_DEBUG, "[xds_client %p] constructed LRS request: %s",
364             context.client, buf);
365   }
366 }
367 
SerializeLrsRequest(const XdsApiContext & context,const envoy_service_load_stats_v3_LoadStatsRequest * request)368 std::string SerializeLrsRequest(
369     const XdsApiContext& context,
370     const envoy_service_load_stats_v3_LoadStatsRequest* request) {
371   size_t output_length;
372   char* output = envoy_service_load_stats_v3_LoadStatsRequest_serialize(
373       request, context.arena, &output_length);
374   return std::string(output, output_length);
375 }
376 
377 }  // namespace
378 
CreateLrsInitialRequest()379 std::string XdsApi::CreateLrsInitialRequest() {
380   upb::Arena arena;
381   const XdsApiContext context = {client_, tracer_, def_pool_->ptr(),
382                                  arena.ptr()};
383   // Create a request.
384   envoy_service_load_stats_v3_LoadStatsRequest* request =
385       envoy_service_load_stats_v3_LoadStatsRequest_new(arena.ptr());
386   // Populate node.
387   envoy_config_core_v3_Node* node_msg =
388       envoy_service_load_stats_v3_LoadStatsRequest_mutable_node(request,
389                                                                 arena.ptr());
390   PopulateNode(node_msg, arena.ptr());
391   envoy_config_core_v3_Node_add_client_features(
392       node_msg,
393       upb_StringView_FromString("envoy.lrs.supports_send_all_clusters"),
394       arena.ptr());
395   MaybeLogLrsRequest(context, request);
396   return SerializeLrsRequest(context, request);
397 }
398 
399 namespace {
400 
LocalityStatsPopulate(const XdsApiContext & context,envoy_config_endpoint_v3_UpstreamLocalityStats * output,const XdsLocalityName & locality_name,const XdsClusterLocalityStats::Snapshot & snapshot)401 void LocalityStatsPopulate(
402     const XdsApiContext& context,
403     envoy_config_endpoint_v3_UpstreamLocalityStats* output,
404     const XdsLocalityName& locality_name,
405     const XdsClusterLocalityStats::Snapshot& snapshot) {
406   // Set locality.
407   envoy_config_core_v3_Locality* locality =
408       envoy_config_endpoint_v3_UpstreamLocalityStats_mutable_locality(
409           output, context.arena);
410   if (!locality_name.region().empty()) {
411     envoy_config_core_v3_Locality_set_region(
412         locality, StdStringToUpbString(locality_name.region()));
413   }
414   if (!locality_name.zone().empty()) {
415     envoy_config_core_v3_Locality_set_zone(
416         locality, StdStringToUpbString(locality_name.zone()));
417   }
418   if (!locality_name.sub_zone().empty()) {
419     envoy_config_core_v3_Locality_set_sub_zone(
420         locality, StdStringToUpbString(locality_name.sub_zone()));
421   }
422   // Set total counts.
423   envoy_config_endpoint_v3_UpstreamLocalityStats_set_total_successful_requests(
424       output, snapshot.total_successful_requests);
425   envoy_config_endpoint_v3_UpstreamLocalityStats_set_total_requests_in_progress(
426       output, snapshot.total_requests_in_progress);
427   envoy_config_endpoint_v3_UpstreamLocalityStats_set_total_error_requests(
428       output, snapshot.total_error_requests);
429   envoy_config_endpoint_v3_UpstreamLocalityStats_set_total_issued_requests(
430       output, snapshot.total_issued_requests);
431   // Add backend metrics.
432   for (const auto& p : snapshot.backend_metrics) {
433     const std::string& metric_name = p.first;
434     const XdsClusterLocalityStats::BackendMetric& metric_value = p.second;
435     envoy_config_endpoint_v3_EndpointLoadMetricStats* load_metric =
436         envoy_config_endpoint_v3_UpstreamLocalityStats_add_load_metric_stats(
437             output, context.arena);
438     envoy_config_endpoint_v3_EndpointLoadMetricStats_set_metric_name(
439         load_metric, StdStringToUpbString(metric_name));
440     envoy_config_endpoint_v3_EndpointLoadMetricStats_set_num_requests_finished_with_metric(
441         load_metric, metric_value.num_requests_finished_with_metric);
442     envoy_config_endpoint_v3_EndpointLoadMetricStats_set_total_metric_value(
443         load_metric, metric_value.total_metric_value);
444   }
445 }
446 
447 }  // namespace
448 
CreateLrsRequest(ClusterLoadReportMap cluster_load_report_map)449 std::string XdsApi::CreateLrsRequest(
450     ClusterLoadReportMap cluster_load_report_map) {
451   upb::Arena arena;
452   const XdsApiContext context = {client_, tracer_, def_pool_->ptr(),
453                                  arena.ptr()};
454   // Create a request.
455   envoy_service_load_stats_v3_LoadStatsRequest* request =
456       envoy_service_load_stats_v3_LoadStatsRequest_new(arena.ptr());
457   for (auto& p : cluster_load_report_map) {
458     const std::string& cluster_name = p.first.first;
459     const std::string& eds_service_name = p.first.second;
460     const ClusterLoadReport& load_report = p.second;
461     // Add cluster stats.
462     envoy_config_endpoint_v3_ClusterStats* cluster_stats =
463         envoy_service_load_stats_v3_LoadStatsRequest_add_cluster_stats(
464             request, arena.ptr());
465     // Set the cluster name.
466     envoy_config_endpoint_v3_ClusterStats_set_cluster_name(
467         cluster_stats, StdStringToUpbString(cluster_name));
468     // Set EDS service name, if non-empty.
469     if (!eds_service_name.empty()) {
470       envoy_config_endpoint_v3_ClusterStats_set_cluster_service_name(
471           cluster_stats, StdStringToUpbString(eds_service_name));
472     }
473     // Add locality stats.
474     for (const auto& p : load_report.locality_stats) {
475       const XdsLocalityName& locality_name = *p.first;
476       const auto& snapshot = p.second;
477       envoy_config_endpoint_v3_UpstreamLocalityStats* locality_stats =
478           envoy_config_endpoint_v3_ClusterStats_add_upstream_locality_stats(
479               cluster_stats, arena.ptr());
480       LocalityStatsPopulate(context, locality_stats, locality_name, snapshot);
481     }
482     // Add dropped requests.
483     uint64_t total_dropped_requests = 0;
484     for (const auto& p : load_report.dropped_requests.categorized_drops) {
485       const std::string& category = p.first;
486       const uint64_t count = p.second;
487       envoy_config_endpoint_v3_ClusterStats_DroppedRequests* dropped_requests =
488           envoy_config_endpoint_v3_ClusterStats_add_dropped_requests(
489               cluster_stats, arena.ptr());
490       envoy_config_endpoint_v3_ClusterStats_DroppedRequests_set_category(
491           dropped_requests, StdStringToUpbString(category));
492       envoy_config_endpoint_v3_ClusterStats_DroppedRequests_set_dropped_count(
493           dropped_requests, count);
494       total_dropped_requests += count;
495     }
496     total_dropped_requests += load_report.dropped_requests.uncategorized_drops;
497     // Set total dropped requests.
498     envoy_config_endpoint_v3_ClusterStats_set_total_dropped_requests(
499         cluster_stats, total_dropped_requests);
500     // Set real load report interval.
501     gpr_timespec timespec = load_report.load_report_interval.as_timespec();
502     google_protobuf_Duration* load_report_interval =
503         envoy_config_endpoint_v3_ClusterStats_mutable_load_report_interval(
504             cluster_stats, arena.ptr());
505     google_protobuf_Duration_set_seconds(load_report_interval, timespec.tv_sec);
506     google_protobuf_Duration_set_nanos(load_report_interval, timespec.tv_nsec);
507   }
508   MaybeLogLrsRequest(context, request);
509   return SerializeLrsRequest(context, request);
510 }
511 
512 namespace {
513 
MaybeLogLrsResponse(const XdsApiContext & context,const envoy_service_load_stats_v3_LoadStatsResponse * response)514 void MaybeLogLrsResponse(
515     const XdsApiContext& context,
516     const envoy_service_load_stats_v3_LoadStatsResponse* response) {
517   if (GRPC_TRACE_FLAG_ENABLED(*context.tracer) &&
518       gpr_should_log(GPR_LOG_SEVERITY_DEBUG)) {
519     const upb_MessageDef* msg_type =
520         envoy_service_load_stats_v3_LoadStatsResponse_getmsgdef(
521             context.def_pool);
522     char buf[10240];
523     upb_TextEncode(reinterpret_cast<const upb_Message*>(response), msg_type,
524                    nullptr, 0, buf, sizeof(buf));
525     gpr_log(GPR_DEBUG, "[xds_client %p] received LRS response: %s",
526             context.client, buf);
527   }
528 }
529 
530 }  // namespace
531 
ParseLrsResponse(absl::string_view encoded_response,bool * send_all_clusters,std::set<std::string> * cluster_names,Duration * load_reporting_interval)532 absl::Status XdsApi::ParseLrsResponse(absl::string_view encoded_response,
533                                       bool* send_all_clusters,
534                                       std::set<std::string>* cluster_names,
535                                       Duration* load_reporting_interval) {
536   upb::Arena arena;
537   // Decode the response.
538   const envoy_service_load_stats_v3_LoadStatsResponse* decoded_response =
539       envoy_service_load_stats_v3_LoadStatsResponse_parse(
540           encoded_response.data(), encoded_response.size(), arena.ptr());
541   // Parse the response.
542   if (decoded_response == nullptr) {
543     return absl::UnavailableError("Can't decode response.");
544   }
545   const XdsApiContext context = {client_, tracer_, def_pool_->ptr(),
546                                  arena.ptr()};
547   MaybeLogLrsResponse(context, decoded_response);
548   // Check send_all_clusters.
549   if (envoy_service_load_stats_v3_LoadStatsResponse_send_all_clusters(
550           decoded_response)) {
551     *send_all_clusters = true;
552   } else {
553     // Store the cluster names.
554     size_t size;
555     const upb_StringView* clusters =
556         envoy_service_load_stats_v3_LoadStatsResponse_clusters(decoded_response,
557                                                                &size);
558     for (size_t i = 0; i < size; ++i) {
559       cluster_names->emplace(UpbStringToStdString(clusters[i]));
560     }
561   }
562   // Get the load report interval.
563   const google_protobuf_Duration* load_reporting_interval_duration =
564       envoy_service_load_stats_v3_LoadStatsResponse_load_reporting_interval(
565           decoded_response);
566   *load_reporting_interval = Duration::FromSecondsAndNanoseconds(
567       google_protobuf_Duration_seconds(load_reporting_interval_duration),
568       google_protobuf_Duration_nanos(load_reporting_interval_duration));
569   return absl::OkStatus();
570 }
571 
572 }  // namespace grpc_core
573