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