1 // Copyright 2020 gRPC authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "src/core/util/json/json_object_loader.h"
16
17 #include <grpc/support/json.h>
18 #include <grpc/support/port_platform.h>
19
20 #include <utility>
21
22 #include "absl/strings/ascii.h"
23 #include "absl/strings/str_cat.h"
24 #include "absl/strings/strip.h"
25
26 namespace grpc_core {
27 namespace json_detail {
28
LoadInto(const Json & json,const JsonArgs &,void * dst,ValidationErrors * errors) const29 void LoadScalar::LoadInto(const Json& json, const JsonArgs& /*args*/, void* dst,
30 ValidationErrors* errors) const {
31 // We accept either kString or kNumber for numeric values, as per
32 // https://developers.google.com/protocol-buffers/docs/proto3#json.
33 if (json.type() != Json::Type::kString &&
34 (!IsNumber() || json.type() != Json::Type::kNumber)) {
35 errors->AddError(
36 absl::StrCat("is not a ", IsNumber() ? "number" : "string"));
37 return;
38 }
39 return LoadInto(json.string(), dst, errors);
40 }
41
IsNumber() const42 bool LoadString::IsNumber() const { return false; }
43
LoadInto(const std::string & value,void * dst,ValidationErrors *) const44 void LoadString::LoadInto(const std::string& value, void* dst,
45 ValidationErrors*) const {
46 *static_cast<std::string*>(dst) = value;
47 }
48
IsNumber() const49 bool LoadDuration::IsNumber() const { return false; }
50
LoadInto(const std::string & value,void * dst,ValidationErrors * errors) const51 void LoadDuration::LoadInto(const std::string& value, void* dst,
52 ValidationErrors* errors) const {
53 absl::string_view buf(value);
54 if (!absl::ConsumeSuffix(&buf, "s")) {
55 errors->AddError("Not a duration (no s suffix)");
56 return;
57 }
58 buf = absl::StripAsciiWhitespace(buf);
59 auto decimal_point = buf.find('.');
60 int32_t nanos = 0;
61 if (decimal_point != absl::string_view::npos) {
62 absl::string_view after_decimal = buf.substr(decimal_point + 1);
63 buf = buf.substr(0, decimal_point);
64 if (!absl::SimpleAtoi(after_decimal, &nanos)) {
65 errors->AddError("Not a duration (not a number of nanoseconds)");
66 return;
67 }
68 if (after_decimal.length() > 9) {
69 // We don't accept greater precision than nanos.
70 errors->AddError("Not a duration (too many digits after decimal)");
71 return;
72 }
73 for (size_t i = 0; i < (9 - after_decimal.length()); ++i) {
74 nanos *= 10;
75 }
76 }
77 int64_t seconds;
78 if (!absl::SimpleAtoi(buf, &seconds)) {
79 errors->AddError("Not a duration (not a number of seconds)");
80 return;
81 }
82 // Acceptable range for seconds documented at
83 // https://developers.google.com/protocol-buffers/docs/reference/google.protobuf#google.protobuf.Duration
84 if (seconds < 0 || seconds > 315576000000) {
85 errors->AddError("seconds must be in the range [0, 315576000000]");
86 }
87 *static_cast<Duration*>(dst) =
88 Duration::FromSecondsAndNanoseconds(seconds, nanos);
89 }
90
IsNumber() const91 bool LoadNumber::IsNumber() const { return true; }
92
LoadInto(const Json & json,const JsonArgs &,void * dst,ValidationErrors * errors) const93 void LoadBool::LoadInto(const Json& json, const JsonArgs&, void* dst,
94 ValidationErrors* errors) const {
95 if (json.type() != Json::Type::kBoolean) {
96 errors->AddError("is not a boolean");
97 return;
98 }
99 *static_cast<bool*>(dst) = json.boolean();
100 }
101
LoadInto(const Json & json,const JsonArgs &,void * dst,ValidationErrors * errors) const102 void LoadUnprocessedJsonObject::LoadInto(const Json& json, const JsonArgs&,
103 void* dst,
104 ValidationErrors* errors) const {
105 if (json.type() != Json::Type::kObject) {
106 errors->AddError("is not an object");
107 return;
108 }
109 *static_cast<Json::Object*>(dst) = json.object();
110 }
111
LoadInto(const Json & json,const JsonArgs &,void * dst,ValidationErrors * errors) const112 void LoadUnprocessedJsonArray::LoadInto(const Json& json, const JsonArgs&,
113 void* dst,
114 ValidationErrors* errors) const {
115 if (json.type() != Json::Type::kArray) {
116 errors->AddError("is not an array");
117 return;
118 }
119 *static_cast<Json::Array*>(dst) = json.array();
120 }
121
LoadInto(const Json & json,const JsonArgs & args,void * dst,ValidationErrors * errors) const122 void LoadVector::LoadInto(const Json& json, const JsonArgs& args, void* dst,
123 ValidationErrors* errors) const {
124 if (json.type() != Json::Type::kArray) {
125 errors->AddError("is not an array");
126 return;
127 }
128 const auto& array = json.array();
129 const LoaderInterface* element_loader = ElementLoader();
130 for (size_t i = 0; i < array.size(); ++i) {
131 ValidationErrors::ScopedField field(errors, absl::StrCat("[", i, "]"));
132 void* element = EmplaceBack(dst);
133 element_loader->LoadInto(array[i], args, element, errors);
134 }
135 }
136
LoadInto(const Json & json,const JsonArgs & args,void * dst,ValidationErrors * errors) const137 void AutoLoader<std::vector<bool>>::LoadInto(const Json& json,
138 const JsonArgs& args, void* dst,
139 ValidationErrors* errors) const {
140 if (json.type() != Json::Type::kArray) {
141 errors->AddError("is not an array");
142 return;
143 }
144 const auto& array = json.array();
145 const LoaderInterface* element_loader = LoaderForType<bool>();
146 std::vector<bool>* vec = static_cast<std::vector<bool>*>(dst);
147 for (size_t i = 0; i < array.size(); ++i) {
148 ValidationErrors::ScopedField field(errors, absl::StrCat("[", i, "]"));
149 bool elem = false;
150 element_loader->LoadInto(array[i], args, &elem, errors);
151 vec->push_back(elem);
152 }
153 }
154
LoadInto(const Json & json,const JsonArgs & args,void * dst,ValidationErrors * errors) const155 void LoadMap::LoadInto(const Json& json, const JsonArgs& args, void* dst,
156 ValidationErrors* errors) const {
157 if (json.type() != Json::Type::kObject) {
158 errors->AddError("is not an object");
159 return;
160 }
161 const LoaderInterface* element_loader = ElementLoader();
162 for (const auto& pair : json.object()) {
163 ValidationErrors::ScopedField field(errors,
164 absl::StrCat("[\"", pair.first, "\"]"));
165 void* element = Insert(pair.first, dst);
166 element_loader->LoadInto(pair.second, args, element, errors);
167 }
168 }
169
LoadInto(const Json & json,const JsonArgs & args,void * dst,ValidationErrors * errors) const170 void LoadWrapped::LoadInto(const Json& json, const JsonArgs& args, void* dst,
171 ValidationErrors* errors) const {
172 void* element = Emplace(dst);
173 size_t starting_error_size = errors->size();
174 ElementLoader()->LoadInto(json, args, element, errors);
175 if (errors->size() > starting_error_size) Reset(dst);
176 }
177
LoadObject(const Json & json,const JsonArgs & args,const Element * elements,size_t num_elements,void * dst,ValidationErrors * errors)178 bool LoadObject(const Json& json, const JsonArgs& args, const Element* elements,
179 size_t num_elements, void* dst, ValidationErrors* errors) {
180 if (json.type() != Json::Type::kObject) {
181 errors->AddError("is not an object");
182 return false;
183 }
184 for (size_t i = 0; i < num_elements; ++i) {
185 const Element& element = elements[i];
186 if (element.enable_key != nullptr && !args.IsEnabled(element.enable_key)) {
187 continue;
188 }
189 ValidationErrors::ScopedField field(errors,
190 absl::StrCat(".", element.name));
191 const auto& it = json.object().find(element.name);
192 if (it == json.object().end() || it->second.type() == Json::Type::kNull) {
193 if (element.optional) continue;
194 errors->AddError("field not present");
195 continue;
196 }
197 char* field_dst = static_cast<char*>(dst) + element.member_offset;
198 element.loader->LoadInto(it->second, args, field_dst, errors);
199 }
200 return true;
201 }
202
GetJsonObjectField(const Json::Object & json,absl::string_view field,ValidationErrors * errors,bool required)203 const Json* GetJsonObjectField(const Json::Object& json,
204 absl::string_view field,
205 ValidationErrors* errors, bool required) {
206 auto it = json.find(std::string(field));
207 if (it == json.end()) {
208 if (required) errors->AddError("field not present");
209 return nullptr;
210 }
211 return &it->second;
212 }
213
214 } // namespace json_detail
215 } // namespace grpc_core
216