• 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(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