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