LCOV - code coverage report
Current view: top level - source/common/config - xds_resource.cc (source / functions) Hit Total Coverage
Test: coverage.dat Lines: 3 162 1.9 %
Date: 2024-01-05 06:35:25 Functions: 1 12 8.3 %

          Line data    Source code
       1             : #include "source/common/config/xds_resource.h"
       2             : 
       3             : #include <algorithm>
       4             : 
       5             : #include "source/common/common/fmt.h"
       6             : #include "source/common/http/utility.h"
       7             : 
       8             : #include "absl/strings/str_cat.h"
       9             : #include "absl/strings/str_split.h"
      10             : 
      11             : // TODO(htuch): This file has a bunch of ad hoc URI encoding/decoding based on Envoy's HTTP util
      12             : // functions. Once https://github.com/envoyproxy/envoy/issues/6588 lands, we can replace with GURL.
      13             : 
      14             : namespace Envoy {
      15             : namespace Config {
      16             : 
      17             : using PercentEncoding = Http::Utility::PercentEncoding;
      18             : 
      19             : namespace {
      20             : 
      21             : // We need to percent-encode authority, id, path and query params. Resource types should not have
      22             : // reserved characters.
      23             : 
      24           0 : std::string encodeAuthority(const std::string& authority) {
      25           0 :   return PercentEncoding::encode(authority, "%/?#");
      26           0 : }
      27             : 
      28           0 : std::string encodeIdPath(const std::string& id) {
      29           0 :   const std::string path = PercentEncoding::encode(id, "%:?#[]");
      30           0 :   return path.empty() ? "" : absl::StrCat("/", path);
      31           0 : }
      32             : 
      33             : std::string encodeContextParams(const xds::core::v3::ContextParams& context_params,
      34           0 :                                 bool sort_context_params) {
      35           0 :   std::vector<std::string> query_param_components;
      36           0 :   for (const auto& context_param : context_params.params()) {
      37           0 :     query_param_components.emplace_back(
      38           0 :         absl::StrCat(PercentEncoding::encode(context_param.first, "%#[]&="), "=",
      39           0 :                      PercentEncoding::encode(context_param.second, "%#[]&=")));
      40           0 :   }
      41           0 :   if (sort_context_params) {
      42           0 :     std::sort(query_param_components.begin(), query_param_components.end());
      43           0 :   }
      44           0 :   return query_param_components.empty() ? "" : "?" + absl::StrJoin(query_param_components, "&");
      45           0 : }
      46             : 
      47             : std::string encodeDirectives(
      48           0 :     const Protobuf::RepeatedPtrField<xds::core::v3::ResourceLocator::Directive>& directives) {
      49           0 :   std::vector<std::string> fragment_components;
      50           0 :   const std::string DirectiveEscapeChars = "%#[],";
      51           0 :   for (const auto& directive : directives) {
      52           0 :     switch (directive.directive_case()) {
      53           0 :     case xds::core::v3::ResourceLocator::Directive::DirectiveCase::kAlt:
      54           0 :       fragment_components.emplace_back(absl::StrCat(
      55           0 :           "alt=", PercentEncoding::encode(XdsResourceIdentifier::encodeUrl(directive.alt()),
      56           0 :                                           DirectiveEscapeChars)));
      57           0 :       break;
      58           0 :     case xds::core::v3::ResourceLocator::Directive::DirectiveCase::kEntry:
      59           0 :       fragment_components.emplace_back(
      60           0 :           absl::StrCat("entry=", PercentEncoding::encode(directive.entry(), DirectiveEscapeChars)));
      61           0 :       break;
      62           0 :     case xds::core::v3::ResourceLocator::Directive::DirectiveCase::DIRECTIVE_NOT_SET:
      63           0 :       PANIC_DUE_TO_PROTO_UNSET;
      64           0 :     }
      65           0 :   }
      66           0 :   return fragment_components.empty() ? "" : "#" + absl::StrJoin(fragment_components, ",");
      67           0 : }
      68             : 
      69             : } // namespace
      70             : 
      71             : std::string XdsResourceIdentifier::encodeUrn(const xds::core::v3::ResourceName& resource_name,
      72           0 :                                              const EncodeOptions& options) {
      73           0 :   const std::string authority = encodeAuthority(resource_name.authority());
      74           0 :   const std::string id_path = encodeIdPath(resource_name.id());
      75           0 :   const std::string query_params =
      76           0 :       encodeContextParams(resource_name.context(), options.sort_context_params_);
      77           0 :   return absl::StrCat("xdstp://", authority, "/", resource_name.resource_type(), id_path,
      78           0 :                       query_params);
      79           0 : }
      80             : 
      81             : std::string XdsResourceIdentifier::encodeUrl(const xds::core::v3::ResourceLocator& resource_locator,
      82           0 :                                              const EncodeOptions& options) {
      83           0 :   const std::string id_path = encodeIdPath(resource_locator.id());
      84           0 :   const std::string fragment = encodeDirectives(resource_locator.directives());
      85           0 :   std::string scheme = "xdstp:";
      86           0 :   switch (resource_locator.scheme()) {
      87           0 :     PANIC_ON_PROTO_ENUM_SENTINEL_VALUES;
      88           0 :   case xds::core::v3::ResourceLocator::HTTP:
      89           0 :     scheme = "http:";
      90           0 :     FALLTHRU;
      91           0 :   case xds::core::v3::ResourceLocator::XDSTP: {
      92           0 :     const std::string authority = encodeAuthority(resource_locator.authority());
      93           0 :     const std::string query_params =
      94           0 :         encodeContextParams(resource_locator.exact_context(), options.sort_context_params_);
      95           0 :     return absl::StrCat(scheme, "//", authority, "/", resource_locator.resource_type(), id_path,
      96           0 :                         query_params, fragment);
      97           0 :   }
      98           0 :   case xds::core::v3::ResourceLocator::FILE: {
      99           0 :     return absl::StrCat("file://", id_path, fragment);
     100           0 :   }
     101           0 :   }
     102           0 :   return "";
     103           0 : }
     104             : 
     105             : namespace {
     106             : 
     107           0 : absl::Status decodePath(absl::string_view path, std::string* resource_type, std::string& id) {
     108             :   // This is guaranteed by Http::Utility::extractHostPathFromUrn.
     109           0 :   ASSERT(absl::StartsWith(path, "/"));
     110           0 :   const std::vector<absl::string_view> path_components = absl::StrSplit(path.substr(1), '/');
     111           0 :   auto id_it = path_components.cbegin();
     112           0 :   if (resource_type != nullptr) {
     113           0 :     *resource_type = std::string(path_components[0]);
     114           0 :     if (resource_type->empty()) {
     115           0 :       return absl::InvalidArgumentError(fmt::format("Resource type missing from {}", path));
     116           0 :     }
     117           0 :     id_it = std::next(id_it);
     118           0 :   }
     119           0 :   id = PercentEncoding::decode(absl::StrJoin(id_it, path_components.cend(), "/"));
     120           0 :   return absl::OkStatus();
     121           0 : }
     122             : 
     123             : void decodeQueryParams(absl::string_view query_params,
     124           0 :                        xds::core::v3::ContextParams& context_params) {
     125           0 :   auto query_params_components = Http::Utility::QueryParamsMulti::parseQueryString(query_params);
     126           0 :   for (const auto& it : query_params_components.data()) {
     127           0 :     (*context_params.mutable_params())[PercentEncoding::decode(it.first)] =
     128           0 :         PercentEncoding::decode(it.second[0]);
     129           0 :   }
     130           0 : }
     131             : 
     132             : absl::Status
     133             : decodeFragment(absl::string_view fragment,
     134           0 :                Protobuf::RepeatedPtrField<xds::core::v3::ResourceLocator::Directive>& directives) {
     135           0 :   const std::vector<absl::string_view> fragment_components = absl::StrSplit(fragment, ',');
     136           0 :   for (const absl::string_view& fragment_component : fragment_components) {
     137           0 :     if (absl::StartsWith(fragment_component, "alt=")) {
     138           0 :       directives.Add()->mutable_alt()->MergeFrom(
     139           0 :           XdsResourceIdentifier::decodeUrl(PercentEncoding::decode(fragment_component.substr(4))));
     140           0 :     } else if (absl::StartsWith(fragment_component, "entry=")) {
     141           0 :       directives.Add()->set_entry(PercentEncoding::decode(fragment_component.substr(6)));
     142           0 :     } else {
     143           0 :       return absl::InvalidArgumentError(
     144           0 :           fmt::format("Unknown fragment component {}", fragment_component));
     145           0 :     }
     146           0 :   }
     147           0 :   return absl::OkStatus();
     148           0 : }
     149             : 
     150             : } // namespace
     151             : 
     152             : absl::StatusOr<xds::core::v3::ResourceName>
     153           0 : XdsResourceIdentifier::decodeUrn(absl::string_view resource_urn) {
     154           0 :   if (!hasXdsTpScheme(resource_urn)) {
     155           0 :     return absl::InvalidArgumentError(
     156           0 :         fmt::format("{} does not have an xdstp: scheme", resource_urn));
     157           0 :   }
     158           0 :   absl::string_view host, path;
     159           0 :   Http::Utility::extractHostPathFromUri(resource_urn, host, path);
     160           0 :   xds::core::v3::ResourceName decoded_resource_name;
     161           0 :   decoded_resource_name.set_authority(PercentEncoding::decode(host));
     162           0 :   const size_t query_params_start = path.find('?');
     163           0 :   if (query_params_start != absl::string_view::npos) {
     164           0 :     decodeQueryParams(path.substr(query_params_start), *decoded_resource_name.mutable_context());
     165           0 :     path = path.substr(0, query_params_start);
     166           0 :   }
     167           0 :   auto status = decodePath(path, decoded_resource_name.mutable_resource_type(),
     168           0 :                            *decoded_resource_name.mutable_id());
     169           0 :   if (!status.ok()) {
     170           0 :     return status;
     171           0 :   }
     172           0 :   return decoded_resource_name;
     173           0 : }
     174             : 
     175           0 : xds::core::v3::ResourceLocator XdsResourceIdentifier::decodeUrl(absl::string_view resource_url) {
     176           0 :   absl::string_view host, path;
     177           0 :   Http::Utility::extractHostPathFromUri(resource_url, host, path);
     178           0 :   xds::core::v3::ResourceLocator decoded_resource_locator;
     179           0 :   const size_t fragment_start = path.find('#');
     180           0 :   if (fragment_start != absl::string_view::npos) {
     181           0 :     THROW_IF_NOT_OK(decodeFragment(path.substr(fragment_start + 1),
     182           0 :                                    *decoded_resource_locator.mutable_directives()));
     183           0 :     path = path.substr(0, fragment_start);
     184           0 :   }
     185           0 :   if (hasXdsTpScheme(resource_url)) {
     186           0 :     decoded_resource_locator.set_scheme(xds::core::v3::ResourceLocator::XDSTP);
     187           0 :   } else if (absl::StartsWith(resource_url, "http:")) {
     188           0 :     decoded_resource_locator.set_scheme(xds::core::v3::ResourceLocator::HTTP);
     189           0 :   } else if (absl::StartsWith(resource_url, "file:")) {
     190           0 :     decoded_resource_locator.set_scheme(xds::core::v3::ResourceLocator::FILE);
     191             :     // File URLs only have a path and fragment.
     192           0 :     THROW_IF_NOT_OK(decodePath(path, nullptr, *decoded_resource_locator.mutable_id()));
     193           0 :     return decoded_resource_locator;
     194           0 :   } else {
     195           0 :     throwEnvoyExceptionOrPanic(
     196           0 :         fmt::format("{} does not have a xdstp:, http: or file: scheme", resource_url));
     197           0 :   }
     198           0 :   decoded_resource_locator.set_authority(PercentEncoding::decode(host));
     199           0 :   const size_t query_params_start = path.find('?');
     200           0 :   if (query_params_start != absl::string_view::npos) {
     201           0 :     decodeQueryParams(path.substr(query_params_start),
     202           0 :                       *decoded_resource_locator.mutable_exact_context());
     203           0 :     path = path.substr(0, query_params_start);
     204           0 :   }
     205           0 :   THROW_IF_NOT_OK(decodePath(path, decoded_resource_locator.mutable_resource_type(),
     206           0 :                              *decoded_resource_locator.mutable_id()));
     207           0 :   return decoded_resource_locator;
     208           0 : }
     209             : 
     210         418 : bool XdsResourceIdentifier::hasXdsTpScheme(absl::string_view resource_name) {
     211         418 :   return absl::StartsWith(resource_name, "xdstp:");
     212         418 : }
     213             : 
     214             : } // namespace Config
     215             : } // namespace Envoy

Generated by: LCOV version 1.15