1 //
2 // Copyright 2015 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/ext/filters/client_channel/service_config.h"
20
21 #include <string>
22
23 #include "absl/strings/str_cat.h"
24
25 #include <grpc/support/log.h>
26
27 #include "src/core/ext/filters/client_channel/service_config_parser.h"
28 #include "src/core/lib/json/json.h"
29 #include "src/core/lib/slice/slice_internal.h"
30
31 namespace grpc_core {
32
Create(const grpc_channel_args * args,absl::string_view json_string,grpc_error ** error)33 RefCountedPtr<ServiceConfig> ServiceConfig::Create(
34 const grpc_channel_args* args, absl::string_view json_string,
35 grpc_error** error) {
36 GPR_DEBUG_ASSERT(error != nullptr);
37 Json json = Json::Parse(json_string, error);
38 if (*error != GRPC_ERROR_NONE) return nullptr;
39 return MakeRefCounted<ServiceConfig>(args, std::string(json_string),
40 std::move(json), error);
41 }
42
ServiceConfig(const grpc_channel_args * args,std::string json_string,Json json,grpc_error ** error)43 ServiceConfig::ServiceConfig(const grpc_channel_args* args,
44 std::string json_string, Json json,
45 grpc_error** error)
46 : json_string_(std::move(json_string)), json_(std::move(json)) {
47 GPR_DEBUG_ASSERT(error != nullptr);
48 if (json_.type() != Json::Type::OBJECT) {
49 *error =
50 GRPC_ERROR_CREATE_FROM_STATIC_STRING("JSON value is not an object");
51 return;
52 }
53 std::vector<grpc_error*> error_list;
54 grpc_error* global_error = GRPC_ERROR_NONE;
55 parsed_global_configs_ =
56 ServiceConfigParser::ParseGlobalParameters(args, json_, &global_error);
57 if (global_error != GRPC_ERROR_NONE) error_list.push_back(global_error);
58 grpc_error* local_error = ParsePerMethodParams(args);
59 if (local_error != GRPC_ERROR_NONE) error_list.push_back(local_error);
60 if (!error_list.empty()) {
61 *error = GRPC_ERROR_CREATE_FROM_VECTOR("Service config parsing error",
62 &error_list);
63 }
64 }
65
~ServiceConfig()66 ServiceConfig::~ServiceConfig() {
67 for (auto& p : parsed_method_configs_map_) {
68 grpc_slice_unref_internal(p.first);
69 }
70 }
71
ParseJsonMethodConfig(const grpc_channel_args * args,const Json & json)72 grpc_error* ServiceConfig::ParseJsonMethodConfig(const grpc_channel_args* args,
73 const Json& json) {
74 std::vector<grpc_error*> error_list;
75 // Parse method config with each registered parser.
76 auto parsed_configs =
77 absl::make_unique<ServiceConfigParser::ParsedConfigVector>();
78 grpc_error* parser_error = GRPC_ERROR_NONE;
79 *parsed_configs =
80 ServiceConfigParser::ParsePerMethodParameters(args, json, &parser_error);
81 if (parser_error != GRPC_ERROR_NONE) {
82 error_list.push_back(parser_error);
83 }
84 parsed_method_config_vectors_storage_.push_back(std::move(parsed_configs));
85 const auto* vector_ptr = parsed_method_config_vectors_storage_.back().get();
86 // Add an entry for each path.
87 bool found_name = false;
88 auto it = json.object_value().find("name");
89 if (it != json.object_value().end()) {
90 if (it->second.type() != Json::Type::ARRAY) {
91 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
92 "field:name error:not of type Array"));
93 return GRPC_ERROR_CREATE_FROM_VECTOR("methodConfig", &error_list);
94 }
95 const Json::Array& name_array = it->second.array_value();
96 for (const Json& name : name_array) {
97 grpc_error* parse_error = GRPC_ERROR_NONE;
98 std::string path = ParseJsonMethodName(name, &parse_error);
99 if (parse_error != GRPC_ERROR_NONE) {
100 error_list.push_back(parse_error);
101 } else {
102 found_name = true;
103 if (path.empty()) {
104 if (default_method_config_vector_ != nullptr) {
105 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
106 "field:name error:multiple default method configs"));
107 }
108 default_method_config_vector_ = vector_ptr;
109 } else {
110 grpc_slice key = grpc_slice_from_copied_string(path.c_str());
111 // If the key is not already present in the map, this will
112 // store a ref to the key in the map.
113 auto& value = parsed_method_configs_map_[key];
114 if (value != nullptr) {
115 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
116 "field:name error:multiple method configs with same name"));
117 // The map entry already existed, so we need to unref the
118 // key we just created.
119 grpc_slice_unref_internal(key);
120 } else {
121 value = vector_ptr;
122 }
123 }
124 }
125 }
126 }
127 if (!found_name) {
128 parsed_method_config_vectors_storage_.pop_back();
129 }
130 return GRPC_ERROR_CREATE_FROM_VECTOR("methodConfig", &error_list);
131 }
132
ParsePerMethodParams(const grpc_channel_args * args)133 grpc_error* ServiceConfig::ParsePerMethodParams(const grpc_channel_args* args) {
134 std::vector<grpc_error*> error_list;
135 auto it = json_.object_value().find("methodConfig");
136 if (it != json_.object_value().end()) {
137 if (it->second.type() != Json::Type::ARRAY) {
138 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
139 "field:methodConfig error:not of type Array"));
140 }
141 for (const Json& method_config : it->second.array_value()) {
142 if (method_config.type() != Json::Type::OBJECT) {
143 error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
144 "field:methodConfig error:not of type Object"));
145 continue;
146 }
147 grpc_error* error = ParseJsonMethodConfig(args, method_config);
148 if (error != GRPC_ERROR_NONE) {
149 error_list.push_back(error);
150 }
151 }
152 }
153 return GRPC_ERROR_CREATE_FROM_VECTOR("Method Params", &error_list);
154 }
155
ParseJsonMethodName(const Json & json,grpc_error ** error)156 std::string ServiceConfig::ParseJsonMethodName(const Json& json,
157 grpc_error** error) {
158 if (json.type() != Json::Type::OBJECT) {
159 *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
160 "field:name error:type is not object");
161 return "";
162 }
163 // Find service name.
164 const std::string* service_name = nullptr;
165 auto it = json.object_value().find("service");
166 if (it != json.object_value().end() &&
167 it->second.type() != Json::Type::JSON_NULL) {
168 if (it->second.type() != Json::Type::STRING) {
169 *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
170 "field:name error: field:service error:not of type string");
171 return "";
172 }
173 if (!it->second.string_value().empty()) {
174 service_name = &it->second.string_value();
175 }
176 }
177 const std::string* method_name = nullptr;
178 // Find method name.
179 it = json.object_value().find("method");
180 if (it != json.object_value().end() &&
181 it->second.type() != Json::Type::JSON_NULL) {
182 if (it->second.type() != Json::Type::STRING) {
183 *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
184 "field:name error: field:method error:not of type string");
185 return "";
186 }
187 if (!it->second.string_value().empty()) {
188 method_name = &it->second.string_value();
189 }
190 }
191 // If neither service nor method are specified, it's the default.
192 // Method name may not be specified without service name.
193 if (service_name == nullptr) {
194 if (method_name != nullptr) {
195 *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
196 "field:name error:method name populated without service name");
197 }
198 return "";
199 }
200 // Construct path.
201 return absl::StrCat("/", *service_name, "/",
202 method_name == nullptr ? "" : *method_name);
203 }
204
205 const ServiceConfigParser::ParsedConfigVector*
GetMethodParsedConfigVector(const grpc_slice & path) const206 ServiceConfig::GetMethodParsedConfigVector(const grpc_slice& path) const {
207 if (parsed_method_configs_map_.empty()) {
208 return default_method_config_vector_;
209 }
210 // Try looking up the full path in the map.
211 auto it = parsed_method_configs_map_.find(path);
212 if (it != parsed_method_configs_map_.end()) return it->second;
213 // If we didn't find a match for the path, try looking for a wildcard
214 // entry (i.e., change "/service/method" to "/service/").
215 UniquePtr<char> path_str(grpc_slice_to_c_string(path));
216 char* sep = strrchr(path_str.get(), '/');
217 if (sep == nullptr) return nullptr; // Shouldn't ever happen.
218 sep[1] = '\0';
219 grpc_slice wildcard_path = grpc_slice_from_static_string(path_str.get());
220 it = parsed_method_configs_map_.find(wildcard_path);
221 if (it != parsed_method_configs_map_.end()) return it->second;
222 // Try default method config, if set.
223 return default_method_config_vector_;
224 }
225
226 } // namespace grpc_core
227