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