• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2016 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 #ifndef GRPC_CORE_LIB_TRANSPORT_SERVICE_CONFIG_H
18 #define GRPC_CORE_LIB_TRANSPORT_SERVICE_CONFIG_H
19 
20 #include <grpc/support/port_platform.h>
21 
22 #include <grpc/impl/codegen/grpc_types.h>
23 #include <grpc/support/string_util.h>
24 
25 #include "src/core/lib/gprpp/inlined_vector.h"
26 #include "src/core/lib/gprpp/ref_counted_ptr.h"
27 #include "src/core/lib/json/json.h"
28 #include "src/core/lib/slice/slice_hash_table.h"
29 
30 // The main purpose of the code here is to parse the service config in
31 // JSON form, which will look like this:
32 //
33 // {
34 //   "loadBalancingPolicy": "string",  // optional
35 //   "methodConfig": [  // array of one or more method_config objects
36 //     {
37 //       "name": [  // array of one or more name objects
38 //         {
39 //           "service": "string",  // required
40 //           "method": "string",  // optional
41 //         }
42 //       ],
43 //       // remaining fields are optional.
44 //       // see
45 //       https://developers.google.com/protocol-buffers/docs/proto3#json
46 //       // for format details.
47 //       "waitForReady": bool,
48 //       "timeout": "duration_string",
49 //       "maxRequestMessageBytes": "int64_string",
50 //       "maxResponseMessageBytes": "int64_string",
51 //     }
52 //   ]
53 // }
54 
55 namespace grpc_core {
56 
57 class ServiceConfig {
58  public:
59   /// Creates a new service config from parsing \a json_string.
60   /// Returns null on parse error.
61   static UniquePtr<ServiceConfig> Create(const char* json);
62 
63   ~ServiceConfig();
64 
65   /// Invokes \a process_json() for each global parameter in the service
66   /// config.  \a arg is passed as the second argument to \a process_json().
67   template <typename T>
68   using ProcessJson = void (*)(const grpc_json*, T*);
69   template <typename T>
70   void ParseGlobalParams(ProcessJson<T> process_json, T* arg) const;
71 
72   /// Gets the LB policy name from \a service_config.
73   /// Returns NULL if no LB policy name was specified.
74   /// Caller does NOT take ownership.
75   const char* GetLoadBalancingPolicyName() const;
76 
77   /// Creates a method config table based on the data in \a json.
78   /// The table's keys are request paths.  The table's value type is
79   /// returned by \a create_value(), based on data parsed from the JSON tree.
80   /// Returns null on error.
81   template <typename T>
82   using CreateValue = RefCountedPtr<T> (*)(const grpc_json* method_config_json);
83   template <typename T>
84   RefCountedPtr<SliceHashTable<RefCountedPtr<T>>> CreateMethodConfigTable(
85       CreateValue<T> create_value);
86 
87   /// A helper function for looking up values in the table returned by
88   /// \a CreateMethodConfigTable().
89   /// Gets the method config for the specified \a path, which should be of
90   /// the form "/service/method".
91   /// Returns null if the method has no config.
92   /// Caller does NOT own a reference to the result.
93   template <typename T>
94   static RefCountedPtr<T> MethodConfigTableLookup(
95       const SliceHashTable<RefCountedPtr<T>>& table, grpc_slice path);
96 
97  private:
98   // So New() can call our private ctor.
99   template <typename T, typename... Args>
100   friend T* New(Args&&... args);
101 
102   // Takes ownership of \a json_tree.
103   ServiceConfig(UniquePtr<char> json_string, grpc_json* json_tree);
104 
105   // Returns the number of names specified in the method config \a json.
106   static int CountNamesInMethodConfig(grpc_json* json);
107 
108   // Returns a path string for the JSON name object specified by \a json.
109   // Returns null on error.
110   static UniquePtr<char> ParseJsonMethodName(grpc_json* json);
111 
112   // Parses the method config from \a json.  Adds an entry to \a entries for
113   // each name found, incrementing \a idx for each entry added.
114   // Returns false on error.
115   template <typename T>
116   static bool ParseJsonMethodConfig(
117       grpc_json* json, CreateValue<T> create_value,
118       typename SliceHashTable<RefCountedPtr<T>>::Entry* entries, size_t* idx);
119 
120   UniquePtr<char> json_string_;  // Underlying storage for json_tree.
121   grpc_json* json_tree_;
122 };
123 
124 //
125 // implementation -- no user-serviceable parts below
126 //
127 
128 template <typename T>
ParseGlobalParams(ProcessJson<T> process_json,T * arg)129 void ServiceConfig::ParseGlobalParams(ProcessJson<T> process_json,
130                                       T* arg) const {
131   if (json_tree_->type != GRPC_JSON_OBJECT || json_tree_->key != nullptr) {
132     return;
133   }
134   for (grpc_json* field = json_tree_->child; field != nullptr;
135        field = field->next) {
136     if (field->key == nullptr) return;
137     if (strcmp(field->key, "methodConfig") == 0) continue;
138     process_json(field, arg);
139   }
140 }
141 
142 template <typename T>
ParseJsonMethodConfig(grpc_json * json,CreateValue<T> create_value,typename SliceHashTable<RefCountedPtr<T>>::Entry * entries,size_t * idx)143 bool ServiceConfig::ParseJsonMethodConfig(
144     grpc_json* json, CreateValue<T> create_value,
145     typename SliceHashTable<RefCountedPtr<T>>::Entry* entries, size_t* idx) {
146   // Construct value.
147   RefCountedPtr<T> method_config = create_value(json);
148   if (method_config == nullptr) return false;
149   // Construct list of paths.
150   InlinedVector<UniquePtr<char>, 10> paths;
151   for (grpc_json* child = json->child; child != nullptr; child = child->next) {
152     if (child->key == nullptr) continue;
153     if (strcmp(child->key, "name") == 0) {
154       if (child->type != GRPC_JSON_ARRAY) return false;
155       for (grpc_json* name = child->child; name != nullptr; name = name->next) {
156         UniquePtr<char> path = ParseJsonMethodName(name);
157         if (path == nullptr) return false;
158         paths.push_back(std::move(path));
159       }
160     }
161   }
162   if (paths.size() == 0) return false;  // No names specified.
163   // Add entry for each path.
164   for (size_t i = 0; i < paths.size(); ++i) {
165     entries[*idx].key = grpc_slice_from_copied_string(paths[i].get());
166     entries[*idx].value = method_config;  // Takes a new ref.
167     ++*idx;
168   }
169   // Success.
170   return true;
171 }
172 
173 template <typename T>
174 RefCountedPtr<SliceHashTable<RefCountedPtr<T>>>
CreateMethodConfigTable(CreateValue<T> create_value)175 ServiceConfig::CreateMethodConfigTable(CreateValue<T> create_value) {
176   // Traverse parsed JSON tree.
177   if (json_tree_->type != GRPC_JSON_OBJECT || json_tree_->key != nullptr) {
178     return nullptr;
179   }
180   size_t num_entries = 0;
181   typename SliceHashTable<RefCountedPtr<T>>::Entry* entries = nullptr;
182   for (grpc_json* field = json_tree_->child; field != nullptr;
183        field = field->next) {
184     if (field->key == nullptr) return nullptr;
185     if (strcmp(field->key, "methodConfig") == 0) {
186       if (entries != nullptr) return nullptr;  // Duplicate.
187       if (field->type != GRPC_JSON_ARRAY) return nullptr;
188       // Find number of entries.
189       for (grpc_json* method = field->child; method != nullptr;
190            method = method->next) {
191         int count = CountNamesInMethodConfig(method);
192         if (count <= 0) return nullptr;
193         num_entries += static_cast<size_t>(count);
194       }
195       // Populate method config table entries.
196       entries = static_cast<typename SliceHashTable<RefCountedPtr<T>>::Entry*>(
197           gpr_zalloc(num_entries *
198                      sizeof(typename SliceHashTable<RefCountedPtr<T>>::Entry)));
199       size_t idx = 0;
200       for (grpc_json* method = field->child; method != nullptr;
201            method = method->next) {
202         if (!ParseJsonMethodConfig(method, create_value, entries, &idx)) {
203           for (size_t i = 0; i < idx; ++i) {
204             grpc_slice_unref_internal(entries[i].key);
205             entries[i].value.reset();
206           }
207           gpr_free(entries);
208           return nullptr;
209         }
210       }
211       GPR_ASSERT(idx == num_entries);
212     }
213   }
214   // Instantiate method config table.
215   RefCountedPtr<SliceHashTable<RefCountedPtr<T>>> method_config_table;
216   if (entries != nullptr) {
217     method_config_table =
218         SliceHashTable<RefCountedPtr<T>>::Create(num_entries, entries, nullptr);
219     gpr_free(entries);
220   }
221   return method_config_table;
222 }
223 
224 template <typename T>
MethodConfigTableLookup(const SliceHashTable<RefCountedPtr<T>> & table,grpc_slice path)225 RefCountedPtr<T> ServiceConfig::MethodConfigTableLookup(
226     const SliceHashTable<RefCountedPtr<T>>& table, grpc_slice path) {
227   const RefCountedPtr<T>* value = table.Get(path);
228   // If we didn't find a match for the path, try looking for a wildcard
229   // entry (i.e., change "/service/method" to "/service/*").
230   if (value == nullptr) {
231     char* path_str = grpc_slice_to_c_string(path);
232     const char* sep = strrchr(path_str, '/') + 1;
233     const size_t len = (size_t)(sep - path_str);
234     char* buf = (char*)gpr_malloc(len + 2);  // '*' and NUL
235     memcpy(buf, path_str, len);
236     buf[len] = '*';
237     buf[len + 1] = '\0';
238     grpc_slice wildcard_path = grpc_slice_from_copied_string(buf);
239     gpr_free(buf);
240     value = table.Get(wildcard_path);
241     grpc_slice_unref_internal(wildcard_path);
242     gpr_free(path_str);
243   }
244   return RefCountedPtr<T>(*value);
245 }
246 
247 }  // namespace grpc_core
248 
249 #endif /* GRPC_CORE_LIB_TRANSPORT_SERVICE_CONFIG_H */
250