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