summaryrefslogtreecommitdiff
path: root/grpc/src/core/ext/filters/client_channel/service_config.cc
blob: a2794f19f446bb2620919369dc3eb20820a2a8c3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
//
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

#include <grpc/support/port_platform.h>

#include "src/core/ext/filters/client_channel/service_config.h"

#include <string>

#include "absl/strings/str_cat.h"

#include <grpc/support/log.h>

#include "src/core/ext/filters/client_channel/service_config_parser.h"
#include "src/core/lib/json/json.h"
#include "src/core/lib/slice/slice_internal.h"

namespace grpc_core {

RefCountedPtr<ServiceConfig> ServiceConfig::Create(
    const grpc_channel_args* args, absl::string_view json_string,
    grpc_error** error) {
  GPR_DEBUG_ASSERT(error != nullptr);
  Json json = Json::Parse(json_string, error);
  if (*error != GRPC_ERROR_NONE) return nullptr;
  return MakeRefCounted<ServiceConfig>(args, std::string(json_string),
                                       std::move(json), error);
}

ServiceConfig::ServiceConfig(const grpc_channel_args* args,
                             std::string json_string, Json json,
                             grpc_error** error)
    : json_string_(std::move(json_string)), json_(std::move(json)) {
  GPR_DEBUG_ASSERT(error != nullptr);
  if (json_.type() != Json::Type::OBJECT) {
    *error =
        GRPC_ERROR_CREATE_FROM_STATIC_STRING("JSON value is not an object");
    return;
  }
  std::vector<grpc_error*> error_list;
  grpc_error* global_error = GRPC_ERROR_NONE;
  parsed_global_configs_ =
      ServiceConfigParser::ParseGlobalParameters(args, json_, &global_error);
  if (global_error != GRPC_ERROR_NONE) error_list.push_back(global_error);
  grpc_error* local_error = ParsePerMethodParams(args);
  if (local_error != GRPC_ERROR_NONE) error_list.push_back(local_error);
  if (!error_list.empty()) {
    *error = GRPC_ERROR_CREATE_FROM_VECTOR("Service config parsing error",
                                           &error_list);
  }
}

ServiceConfig::~ServiceConfig() {
  for (auto& p : parsed_method_configs_map_) {
    grpc_slice_unref_internal(p.first);
  }
}

grpc_error* ServiceConfig::ParseJsonMethodConfig(const grpc_channel_args* args,
                                                 const Json& json) {
  std::vector<grpc_error*> error_list;
  // Parse method config with each registered parser.
  auto parsed_configs =
      absl::make_unique<ServiceConfigParser::ParsedConfigVector>();
  grpc_error* parser_error = GRPC_ERROR_NONE;
  *parsed_configs =
      ServiceConfigParser::ParsePerMethodParameters(args, json, &parser_error);
  if (parser_error != GRPC_ERROR_NONE) {
    error_list.push_back(parser_error);
  }
  parsed_method_config_vectors_storage_.push_back(std::move(parsed_configs));
  const auto* vector_ptr = parsed_method_config_vectors_storage_.back().get();
  // Add an entry for each path.
  bool found_name = false;
  auto it = json.object_value().find("name");
  if (it != json.object_value().end()) {
    if (it->second.type() != Json::Type::ARRAY) {
      error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
          "field:name error:not of type Array"));
      return GRPC_ERROR_CREATE_FROM_VECTOR("methodConfig", &error_list);
    }
    const Json::Array& name_array = it->second.array_value();
    for (const Json& name : name_array) {
      grpc_error* parse_error = GRPC_ERROR_NONE;
      std::string path = ParseJsonMethodName(name, &parse_error);
      if (parse_error != GRPC_ERROR_NONE) {
        error_list.push_back(parse_error);
      } else {
        found_name = true;
        if (path.empty()) {
          if (default_method_config_vector_ != nullptr) {
            error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
                "field:name error:multiple default method configs"));
          }
          default_method_config_vector_ = vector_ptr;
        } else {
          grpc_slice key = grpc_slice_from_copied_string(path.c_str());
          // If the key is not already present in the map, this will
          // store a ref to the key in the map.
          auto& value = parsed_method_configs_map_[key];
          if (value != nullptr) {
            error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
                "field:name error:multiple method configs with same name"));
            // The map entry already existed, so we need to unref the
            // key we just created.
            grpc_slice_unref_internal(key);
          } else {
            value = vector_ptr;
          }
        }
      }
    }
  }
  if (!found_name) {
    parsed_method_config_vectors_storage_.pop_back();
  }
  return GRPC_ERROR_CREATE_FROM_VECTOR("methodConfig", &error_list);
}

grpc_error* ServiceConfig::ParsePerMethodParams(const grpc_channel_args* args) {
  std::vector<grpc_error*> error_list;
  auto it = json_.object_value().find("methodConfig");
  if (it != json_.object_value().end()) {
    if (it->second.type() != Json::Type::ARRAY) {
      error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
          "field:methodConfig error:not of type Array"));
    }
    for (const Json& method_config : it->second.array_value()) {
      if (method_config.type() != Json::Type::OBJECT) {
        error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
            "field:methodConfig error:not of type Object"));
        continue;
      }
      grpc_error* error = ParseJsonMethodConfig(args, method_config);
      if (error != GRPC_ERROR_NONE) {
        error_list.push_back(error);
      }
    }
  }
  return GRPC_ERROR_CREATE_FROM_VECTOR("Method Params", &error_list);
}

std::string ServiceConfig::ParseJsonMethodName(const Json& json,
                                               grpc_error** error) {
  if (json.type() != Json::Type::OBJECT) {
    *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
        "field:name error:type is not object");
    return "";
  }
  // Find service name.
  const std::string* service_name = nullptr;
  auto it = json.object_value().find("service");
  if (it != json.object_value().end() &&
      it->second.type() != Json::Type::JSON_NULL) {
    if (it->second.type() != Json::Type::STRING) {
      *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
          "field:name error: field:service error:not of type string");
      return "";
    }
    if (!it->second.string_value().empty()) {
      service_name = &it->second.string_value();
    }
  }
  const std::string* method_name = nullptr;
  // Find method name.
  it = json.object_value().find("method");
  if (it != json.object_value().end() &&
      it->second.type() != Json::Type::JSON_NULL) {
    if (it->second.type() != Json::Type::STRING) {
      *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
          "field:name error: field:method error:not of type string");
      return "";
    }
    if (!it->second.string_value().empty()) {
      method_name = &it->second.string_value();
    }
  }
  // If neither service nor method are specified, it's the default.
  // Method name may not be specified without service name.
  if (service_name == nullptr) {
    if (method_name != nullptr) {
      *error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
          "field:name error:method name populated without service name");
    }
    return "";
  }
  // Construct path.
  return absl::StrCat("/", *service_name, "/",
                      method_name == nullptr ? "" : *method_name);
}

const ServiceConfigParser::ParsedConfigVector*
ServiceConfig::GetMethodParsedConfigVector(const grpc_slice& path) const {
  if (parsed_method_configs_map_.empty()) {
    return default_method_config_vector_;
  }
  // Try looking up the full path in the map.
  auto it = parsed_method_configs_map_.find(path);
  if (it != parsed_method_configs_map_.end()) return it->second;
  // If we didn't find a match for the path, try looking for a wildcard
  // entry (i.e., change "/service/method" to "/service/").
  UniquePtr<char> path_str(grpc_slice_to_c_string(path));
  char* sep = strrchr(path_str.get(), '/');
  if (sep == nullptr) return nullptr;  // Shouldn't ever happen.
  sep[1] = '\0';
  grpc_slice wildcard_path = grpc_slice_from_static_string(path_str.get());
  it = parsed_method_configs_map_.find(wildcard_path);
  if (it != parsed_method_configs_map_.end()) return it->second;
  // Try default method config, if set.
  return default_method_config_vector_;
}

}  // namespace grpc_core