Imported Upstream version 1.33.1
[platform/upstream/grpc.git] / src / core / ext / filters / client_channel / service_config.cc
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
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
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
66 ServiceConfig::~ServiceConfig() {
67   for (auto& p : parsed_method_configs_map_) {
68     grpc_slice_unref_internal(p.first);
69   }
70 }
71
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
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
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*
206 ServiceConfig::GetMethodParsedConfigVector(const grpc_slice& path) const {
207   if (parsed_method_configs_map_.empty()) return nullptr;
208   // Try looking up the full path in the map.
209   auto it = parsed_method_configs_map_.find(path);
210   if (it != parsed_method_configs_map_.end()) return it->second;
211   // If we didn't find a match for the path, try looking for a wildcard
212   // entry (i.e., change "/service/method" to "/service/").
213   UniquePtr<char> path_str(grpc_slice_to_c_string(path));
214   char* sep = strrchr(path_str.get(), '/');
215   if (sep == nullptr) return nullptr;  // Shouldn't ever happen.
216   sep[1] = '\0';
217   grpc_slice wildcard_path = grpc_slice_from_static_string(path_str.get());
218   it = parsed_method_configs_map_.find(wildcard_path);
219   if (it != parsed_method_configs_map_.end()) return it->second;
220   // Try default method config, if set.
221   return default_method_config_vector_;
222 }
223
224 }  // namespace grpc_core