1 //
2 // Copyright 2022 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/lib/service_config/service_config_impl.h"
20 
21 #include <string.h>
22 
23 #include <string>
24 #include <utility>
25 
26 #include "absl/status/status.h"
27 #include "absl/status/statusor.h"
28 #include "absl/strings/str_cat.h"
29 #include "absl/types/optional.h"
30 
31 #include "src/core/lib/config/core_configuration.h"
32 #include "src/core/lib/gprpp/memory.h"
33 #include "src/core/lib/gprpp/validation_errors.h"
34 #include "src/core/lib/json/json.h"
35 #include "src/core/lib/json/json_args.h"
36 #include "src/core/lib/json/json_object_loader.h"
37 #include "src/core/lib/json/json_reader.h"
38 #include "src/core/lib/json/json_writer.h"
39 #include "src/core/lib/service_config/service_config_parser.h"
40 #include "src/core/lib/slice/slice.h"
41 #include "src/core/lib/slice/slice_internal.h"
42 
43 namespace grpc_core {
44 
45 namespace {
46 
47 struct MethodConfig {
48   struct Name {
49     absl::optional<std::string> service;
50     absl::optional<std::string> method;
51 
JsonLoadergrpc_core::__anon87a462280111::MethodConfig::Name52     static const JsonLoaderInterface* JsonLoader(const JsonArgs&) {
53       static const auto* loader = JsonObjectLoader<Name>()
54                                       .OptionalField("service", &Name::service)
55                                       .OptionalField("method", &Name::method)
56                                       .Finish();
57       return loader;
58     }
59 
JsonPostLoadgrpc_core::__anon87a462280111::MethodConfig::Name60     void JsonPostLoad(const Json&, const JsonArgs&, ValidationErrors* errors) {
61       if (!service.has_value() && method.has_value()) {
62         errors->AddError("method name populated without service name");
63       }
64     }
65 
Pathgrpc_core::__anon87a462280111::MethodConfig::Name66     std::string Path() const {
67       if (!service.has_value() || service->empty()) return "";
68       return absl::StrCat("/", *service, "/",
69                           method.has_value() ? *method : "");
70     }
71   };
72 
73   std::vector<Name> names;
74 
JsonLoadergrpc_core::__anon87a462280111::MethodConfig75   static const JsonLoaderInterface* JsonLoader(const JsonArgs&) {
76     static const auto* loader = JsonObjectLoader<MethodConfig>()
77                                     .OptionalField("name", &MethodConfig::names)
78                                     .Finish();
79     return loader;
80   }
81 };
82 
83 }  // namespace
84 
Create(const ChannelArgs & args,absl::string_view json_string)85 absl::StatusOr<RefCountedPtr<ServiceConfig>> ServiceConfigImpl::Create(
86     const ChannelArgs& args, absl::string_view json_string) {
87   auto json = JsonParse(json_string);
88   if (!json.ok()) return json.status();
89   ValidationErrors errors;
90   auto service_config = Create(args, *json, json_string, &errors);
91   if (!errors.ok()) {
92     return errors.status(absl::StatusCode::kInvalidArgument,
93                          "errors validating service config");
94   }
95   return service_config;
96 }
97 
Create(const ChannelArgs & args,const Json & json,ValidationErrors * errors)98 RefCountedPtr<ServiceConfig> ServiceConfigImpl::Create(
99     const ChannelArgs& args, const Json& json, ValidationErrors* errors) {
100   return Create(args, json, JsonDump(json), errors);
101 }
102 
Create(const ChannelArgs & args,const Json & json,absl::string_view json_string,ValidationErrors * errors)103 RefCountedPtr<ServiceConfig> ServiceConfigImpl::Create(
104     const ChannelArgs& args, const Json& json, absl::string_view json_string,
105     ValidationErrors* errors) {
106   if (json.type() != Json::Type::kObject) {
107     errors->AddError("is not an object");
108     return nullptr;
109   }
110   auto service_config = MakeRefCounted<ServiceConfigImpl>();
111   service_config->json_string_ = std::string(json_string);
112   // Parse global parameters.
113   service_config->parsed_global_configs_ =
114       CoreConfiguration::Get().service_config_parser().ParseGlobalParameters(
115           args, json, errors);
116   // Parse per-method parameters.
117   auto method_configs = LoadJsonObjectField<std::vector<Json::Object>>(
118       json.object(), JsonArgs(), "methodConfig", errors,
119       /*required=*/false);
120   if (method_configs.has_value()) {
121     service_config->parsed_method_config_vectors_storage_.reserve(
122         method_configs->size());
123     for (size_t i = 0; i < method_configs->size(); ++i) {
124       const Json method_config_json =
125           Json::FromObject(std::move((*method_configs)[i]));
126       ValidationErrors::ScopedField field(
127           errors, absl::StrCat(".methodConfig[", i, "]"));
128       // Have each parser read this method config.
129       auto parsed_configs =
130           CoreConfiguration::Get()
131               .service_config_parser()
132               .ParsePerMethodParameters(args, method_config_json, errors);
133       // Store the parsed configs.
134       service_config->parsed_method_config_vectors_storage_.push_back(
135           std::move(parsed_configs));
136       const ServiceConfigParser::ParsedConfigVector* vector_ptr =
137           &service_config->parsed_method_config_vectors_storage_.back();
138       // Parse the names.
139       auto method_config =
140           LoadFromJson<MethodConfig>(method_config_json, JsonArgs(), errors);
141       for (size_t j = 0; j < method_config.names.size(); ++j) {
142         ValidationErrors::ScopedField field(errors,
143                                             absl::StrCat(".name[", j, "]"));
144         std::string path = method_config.names[j].Path();
145         if (path.empty()) {
146           if (service_config->default_method_config_vector_ != nullptr) {
147             errors->AddError("duplicate default method config");
148           }
149           service_config->default_method_config_vector_ = vector_ptr;
150         } else {
151           grpc_slice key = grpc_slice_from_cpp_string(std::move(path));
152           // If the key is not already present in the map, this will
153           // store a ref to the key in the map.
154           auto& value = service_config->parsed_method_configs_map_[key];
155           if (value != nullptr) {
156             errors->AddError(absl::StrCat("multiple method configs for path ",
157                                           StringViewFromSlice(key)));
158             // The map entry already existed, so we need to unref the
159             // key we just created.
160             CSliceUnref(key);
161           } else {
162             value = vector_ptr;
163           }
164         }
165       }
166     }
167   }
168   return service_config;
169 }
170 
~ServiceConfigImpl()171 ServiceConfigImpl::~ServiceConfigImpl() {
172   for (auto& p : parsed_method_configs_map_) {
173     CSliceUnref(p.first);
174   }
175 }
176 
177 const ServiceConfigParser::ParsedConfigVector*
GetMethodParsedConfigVector(const grpc_slice & path) const178 ServiceConfigImpl::GetMethodParsedConfigVector(const grpc_slice& path) const {
179   if (parsed_method_configs_map_.empty()) {
180     return default_method_config_vector_;
181   }
182   // Try looking up the full path in the map.
183   auto it = parsed_method_configs_map_.find(path);
184   if (it != parsed_method_configs_map_.end()) return it->second;
185   // If we didn't find a match for the path, try looking for a wildcard
186   // entry (i.e., change "/service/method" to "/service/").
187   UniquePtr<char> path_str(grpc_slice_to_c_string(path));
188   char* sep = strrchr(path_str.get(), '/');
189   if (sep == nullptr) return nullptr;  // Shouldn't ever happen.
190   sep[1] = '\0';
191   grpc_slice wildcard_path = grpc_slice_from_static_string(path_str.get());
192   it = parsed_method_configs_map_.find(wildcard_path);
193   if (it != parsed_method_configs_map_.end()) return it->second;
194   // Try default method config, if set.
195   return default_method_config_vector_;
196 }
197 
198 }  // namespace grpc_core
199