Coverage Report

Created: 2023-11-12 09:30

/proc/self/cwd/source/common/config/xds_resource.cc
Line
Count
Source (jump to first uncovered line)
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
26
void throwDecodeExceptionOrPanic(std::string message) {
108
26
  throwExceptionOrPanic(XdsResourceIdentifier::DecodeException, message);
109
26
}
110
111
0
void decodePath(absl::string_view path, std::string* resource_type, std::string& id) {
112
  // This is guaranteed by Http::Utility::extractHostPathFromUrn.
113
0
  ASSERT(absl::StartsWith(path, "/"));
114
0
  const std::vector<absl::string_view> path_components = absl::StrSplit(path.substr(1), '/');
115
0
  auto id_it = path_components.cbegin();
116
0
  if (resource_type != nullptr) {
117
0
    *resource_type = std::string(path_components[0]);
118
0
    if (resource_type->empty()) {
119
0
      throwDecodeExceptionOrPanic(fmt::format("Resource type missing from {}", path));
120
0
    }
121
0
    id_it = std::next(id_it);
122
0
  }
123
0
  id = PercentEncoding::decode(absl::StrJoin(id_it, path_components.cend(), "/"));
124
0
}
125
126
void decodeQueryParams(absl::string_view query_params,
127
0
                       xds::core::v3::ContextParams& context_params) {
128
0
  auto query_params_components = Http::Utility::QueryParamsMulti::parseQueryString(query_params);
129
0
  for (const auto& it : query_params_components.data()) {
130
0
    (*context_params.mutable_params())[PercentEncoding::decode(it.first)] =
131
0
        PercentEncoding::decode(it.second[0]);
132
0
  }
133
0
}
134
135
void decodeFragment(
136
    absl::string_view fragment,
137
26
    Protobuf::RepeatedPtrField<xds::core::v3::ResourceLocator::Directive>& directives) {
138
26
  const std::vector<absl::string_view> fragment_components = absl::StrSplit(fragment, ',');
139
26
  for (const absl::string_view& fragment_component : fragment_components) {
140
26
    if (absl::StartsWith(fragment_component, "alt=")) {
141
0
      directives.Add()->mutable_alt()->MergeFrom(
142
0
          XdsResourceIdentifier::decodeUrl(PercentEncoding::decode(fragment_component.substr(4))));
143
26
    } else if (absl::StartsWith(fragment_component, "entry=")) {
144
0
      directives.Add()->set_entry(PercentEncoding::decode(fragment_component.substr(6)));
145
26
    } else {
146
26
      throwDecodeExceptionOrPanic(fmt::format("Unknown fragment component {}", fragment_component));
147
26
      ;
148
26
    }
149
26
  }
150
26
}
151
152
} // namespace
153
154
0
xds::core::v3::ResourceName XdsResourceIdentifier::decodeUrn(absl::string_view resource_urn) {
155
0
  if (!hasXdsTpScheme(resource_urn)) {
156
0
    throwDecodeExceptionOrPanic(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
  decodePath(path, decoded_resource_name.mutable_resource_type(),
168
0
             *decoded_resource_name.mutable_id());
169
0
  return decoded_resource_name;
170
0
}
171
172
53
xds::core::v3::ResourceLocator XdsResourceIdentifier::decodeUrl(absl::string_view resource_url) {
173
53
  absl::string_view host, path;
174
53
  Http::Utility::extractHostPathFromUri(resource_url, host, path);
175
53
  xds::core::v3::ResourceLocator decoded_resource_locator;
176
53
  const size_t fragment_start = path.find('#');
177
53
  if (fragment_start != absl::string_view::npos) {
178
26
    decodeFragment(path.substr(fragment_start + 1), *decoded_resource_locator.mutable_directives());
179
26
    path = path.substr(0, fragment_start);
180
26
  }
181
53
  if (hasXdsTpScheme(resource_url)) {
182
0
    decoded_resource_locator.set_scheme(xds::core::v3::ResourceLocator::XDSTP);
183
53
  } else if (absl::StartsWith(resource_url, "http:")) {
184
0
    decoded_resource_locator.set_scheme(xds::core::v3::ResourceLocator::HTTP);
185
53
  } else if (absl::StartsWith(resource_url, "file:")) {
186
0
    decoded_resource_locator.set_scheme(xds::core::v3::ResourceLocator::FILE);
187
    // File URLs only have a path and fragment.
188
0
    decodePath(path, nullptr, *decoded_resource_locator.mutable_id());
189
0
    return decoded_resource_locator;
190
53
  } else {
191
53
    throwExceptionOrPanic(
192
53
        XdsResourceIdentifier::DecodeException,
193
53
        fmt::format("{} does not have a xdstp:, http: or file: scheme", resource_url));
194
53
  }
195
0
  decoded_resource_locator.set_authority(PercentEncoding::decode(host));
196
0
  const size_t query_params_start = path.find('?');
197
0
  if (query_params_start != absl::string_view::npos) {
198
0
    decodeQueryParams(path.substr(query_params_start),
199
0
                      *decoded_resource_locator.mutable_exact_context());
200
0
    path = path.substr(0, query_params_start);
201
0
  }
202
0
  decodePath(path, decoded_resource_locator.mutable_resource_type(),
203
0
             *decoded_resource_locator.mutable_id());
204
0
  return decoded_resource_locator;
205
53
}
206
207
691
bool XdsResourceIdentifier::hasXdsTpScheme(absl::string_view resource_name) {
208
691
  return absl::StartsWith(resource_name, "xdstp:");
209
691
}
210
211
} // namespace Config
212
} // namespace Envoy