• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_handle * error)33 RefCountedPtr<ServiceConfig> ServiceConfig::Create(
34     const grpc_channel_args* args, absl::string_view json_string,
35     grpc_error_handle* 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_handle * error)43 ServiceConfig::ServiceConfig(const grpc_channel_args* args,
44                              std::string json_string, Json json,
45                              grpc_error_handle* 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_handle> error_list;
54   grpc_error_handle 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_handle 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_handle ServiceConfig::ParseJsonMethodConfig(
73     const grpc_channel_args* args, const Json& json) {
74   std::vector<grpc_error_handle> error_list;
75   // Parse method config with each registered parser.
76   auto parsed_configs =
77       absl::make_unique<ServiceConfigParser::ParsedConfigVector>();
78   grpc_error_handle 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_handle 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_handle ServiceConfig::ParsePerMethodParams(
134     const grpc_channel_args* args) {
135   std::vector<grpc_error_handle> error_list;
136   auto it = json_.object_value().find("methodConfig");
137   if (it != json_.object_value().end()) {
138     if (it->second.type() != Json::Type::ARRAY) {
139       error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
140           "field:methodConfig error:not of type Array"));
141     }
142     for (const Json& method_config : it->second.array_value()) {
143       if (method_config.type() != Json::Type::OBJECT) {
144         error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
145             "field:methodConfig error:not of type Object"));
146         continue;
147       }
148       grpc_error_handle error = ParseJsonMethodConfig(args, method_config);
149       if (error != GRPC_ERROR_NONE) {
150         error_list.push_back(error);
151       }
152     }
153   }
154   return GRPC_ERROR_CREATE_FROM_VECTOR("Method Params", &error_list);
155 }
156 
ParseJsonMethodName(const Json & json,grpc_error_handle * error)157 std::string ServiceConfig::ParseJsonMethodName(const Json& json,
158                                                grpc_error_handle* error) {
159   if (json.type() != Json::Type::OBJECT) {
160     *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
161         "field:name error:type is not object");
162     return "";
163   }
164   // Find service name.
165   const std::string* service_name = nullptr;
166   auto it = json.object_value().find("service");
167   if (it != json.object_value().end() &&
168       it->second.type() != Json::Type::JSON_NULL) {
169     if (it->second.type() != Json::Type::STRING) {
170       *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
171           "field:name error: field:service error:not of type string");
172       return "";
173     }
174     if (!it->second.string_value().empty()) {
175       service_name = &it->second.string_value();
176     }
177   }
178   const std::string* method_name = nullptr;
179   // Find method name.
180   it = json.object_value().find("method");
181   if (it != json.object_value().end() &&
182       it->second.type() != Json::Type::JSON_NULL) {
183     if (it->second.type() != Json::Type::STRING) {
184       *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
185           "field:name error: field:method error:not of type string");
186       return "";
187     }
188     if (!it->second.string_value().empty()) {
189       method_name = &it->second.string_value();
190     }
191   }
192   // If neither service nor method are specified, it's the default.
193   // Method name may not be specified without service name.
194   if (service_name == nullptr) {
195     if (method_name != nullptr) {
196       *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
197           "field:name error:method name populated without service name");
198     }
199     return "";
200   }
201   // Construct path.
202   return absl::StrCat("/", *service_name, "/",
203                       method_name == nullptr ? "" : *method_name);
204 }
205 
206 const ServiceConfigParser::ParsedConfigVector*
GetMethodParsedConfigVector(const grpc_slice & path) const207 ServiceConfig::GetMethodParsedConfigVector(const grpc_slice& path) const {
208   if (parsed_method_configs_map_.empty()) {
209     return default_method_config_vector_;
210   }
211   // Try looking up the full path in the map.
212   auto it = parsed_method_configs_map_.find(path);
213   if (it != parsed_method_configs_map_.end()) return it->second;
214   // If we didn't find a match for the path, try looking for a wildcard
215   // entry (i.e., change "/service/method" to "/service/").
216   UniquePtr<char> path_str(grpc_slice_to_c_string(path));
217   char* sep = strrchr(path_str.get(), '/');
218   if (sep == nullptr) return nullptr;  // Shouldn't ever happen.
219   sep[1] = '\0';
220   grpc_slice wildcard_path = grpc_slice_from_static_string(path_str.get());
221   it = parsed_method_configs_map_.find(wildcard_path);
222   if (it != parsed_method_configs_map_.end()) return it->second;
223   // Try default method config, if set.
224   return default_method_config_vector_;
225 }
226 
227 }  // namespace grpc_core
228