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