• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 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/lib/transport/metadata_batch.h"
16 
17 #include <grpc/support/port_platform.h>
18 #include <string.h>
19 
20 #include <algorithm>
21 #include <string>
22 
23 #include "absl/base/no_destructor.h"
24 #include "absl/container/flat_hash_set.h"
25 #include "absl/strings/escaping.h"
26 #include "absl/strings/match.h"
27 #include "absl/strings/str_cat.h"
28 #include "absl/strings/str_format.h"
29 #include "src/core/lib/transport/timeout_encoding.h"
30 
31 namespace grpc_core {
32 namespace metadata_detail {
33 
Add(absl::string_view key,absl::string_view value)34 void DebugStringBuilder::Add(absl::string_view key, absl::string_view value) {
35   if (!out_.empty()) out_.append(", ");
36   absl::StrAppend(&out_, absl::CEscape(key), ": ", absl::CEscape(value));
37 }
38 
AddAfterRedaction(absl::string_view key,absl::string_view value)39 void DebugStringBuilder::AddAfterRedaction(absl::string_view key,
40                                            absl::string_view value) {
41   if (IsAllowListed(key)) {
42     Add(key, value);
43   } else {
44     // If the type of metadata is not in the allow list, we want to prevent it
45     // from getting logged. Custom metadata types may have sensitive information
46     // that should never be logged. Programatically, we have no way to know
47     // which data is sensitive and which is not. So we redact all values which
48     // are not in the allow list.
49     Add(key,
50         absl::StrCat(value.size(), " bytes redacted for security reasons."));
51   }
52 }
53 
IsAllowListed(const absl::string_view key) const54 bool DebugStringBuilder::IsAllowListed(const absl::string_view key) const {
55   // We have intentionally not allowed for any way to add to the allow list at
56   // run time, (using a flag or some other setting) because such workarounds
57   // may lead to security issues.
58   static const absl::NoDestructor<absl::flat_hash_set<std::string>> allow_list(
59       [] {
60         absl::flat_hash_set<std::string> allow_list;
61         // go/keep-sorted start
62         allow_list.insert(std::string(ContentTypeMetadata::key()));
63         allow_list.insert(std::string(EndpointLoadMetricsBinMetadata::key()));
64         allow_list.insert(std::string(GrpcAcceptEncodingMetadata::key()));
65         allow_list.insert(std::string(GrpcEncodingMetadata::key()));
66         allow_list.insert(std::string(GrpcInternalEncodingRequest::key()));
67         allow_list.insert(std::string(GrpcLbClientStatsMetadata::key()));
68         allow_list.insert(std::string(GrpcMessageMetadata::key()));
69         allow_list.insert(std::string(GrpcPreviousRpcAttemptsMetadata::key()));
70         allow_list.insert(std::string(GrpcRetryPushbackMsMetadata::key()));
71         allow_list.insert(std::string(GrpcServerStatsBinMetadata::key()));
72         allow_list.insert(std::string(GrpcStatusMetadata::key()));
73         allow_list.insert(std::string(GrpcTagsBinMetadata::key()));
74         allow_list.insert(std::string(GrpcTimeoutMetadata::key()));
75         allow_list.insert(std::string(GrpcTraceBinMetadata::key()));
76         allow_list.insert(std::string(HostMetadata::key()));
77         allow_list.insert(std::string(HttpAuthorityMetadata::key()));
78         allow_list.insert(std::string(HttpMethodMetadata::key()));
79         allow_list.insert(std::string(HttpPathMetadata::key()));
80         allow_list.insert(std::string(HttpSchemeMetadata::key()));
81         allow_list.insert(std::string(HttpStatusMetadata::key()));
82         allow_list.insert(std::string(LbCostBinMetadata::key()));
83         allow_list.insert(std::string(LbTokenMetadata::key()));
84         allow_list.insert(std::string(TeMetadata::key()));
85         allow_list.insert(std::string(UserAgentMetadata::key()));
86         allow_list.insert(std::string(W3CTraceParentMetadata::key()));
87         allow_list.insert(std::string(XEnvoyPeerMetadata::key()));
88         // go/keep-sorted end
89         // go/keep-sorted start
90         allow_list.insert(std::string(GrpcCallWasCancelled::DebugKey()));
91         allow_list.insert(std::string(GrpcRegisteredMethod::DebugKey()));
92         allow_list.insert(std::string(GrpcStatusContext::DebugKey()));
93         allow_list.insert(std::string(GrpcStatusFromWire::DebugKey()));
94         allow_list.insert(std::string(GrpcStreamNetworkState::DebugKey()));
95         allow_list.insert(std::string(GrpcTarPit::DebugKey()));
96         allow_list.insert(std::string(GrpcTrailersOnly::DebugKey()));
97         allow_list.insert(std::string(PeerString::DebugKey()));
98         allow_list.insert(std::string(WaitForReady::DebugKey()));
99         // go/keep-sorted end
100         return allow_list;
101       }());
102   return allow_list->contains(key);
103 }
104 
Append(absl::string_view key,Slice value)105 void UnknownMap::Append(absl::string_view key, Slice value) {
106   unknown_.emplace_back(Slice::FromCopiedString(key), value.Ref());
107 }
108 
Remove(absl::string_view key)109 void UnknownMap::Remove(absl::string_view key) {
110   unknown_.erase(std::remove_if(unknown_.begin(), unknown_.end(),
111                                 [key](const std::pair<Slice, Slice>& p) {
112                                   return p.first.as_string_view() == key;
113                                 }),
114                  unknown_.end());
115 }
116 
GetStringValue(absl::string_view key,std::string * backing) const117 absl::optional<absl::string_view> UnknownMap::GetStringValue(
118     absl::string_view key, std::string* backing) const {
119   absl::optional<absl::string_view> out;
120   for (const auto& p : unknown_) {
121     if (p.first.as_string_view() == key) {
122       if (!out.has_value()) {
123         out = p.second.as_string_view();
124       } else {
125         out = *backing = absl::StrCat(*out, ",", p.second.as_string_view());
126       }
127     }
128   }
129   return out;
130 }
131 
132 }  // namespace metadata_detail
133 
ParseMemento(Slice value,bool,MetadataParseErrorFn)134 ContentTypeMetadata::MementoType ContentTypeMetadata::ParseMemento(
135     Slice value, bool, MetadataParseErrorFn /*on_error*/) {
136   auto out = kInvalid;
137   auto value_string = value.as_string_view();
138   if (value_string == "application/grpc") {
139     out = kApplicationGrpc;
140   } else if (absl::StartsWith(value_string, "application/grpc;")) {
141     out = kApplicationGrpc;
142   } else if (absl::StartsWith(value_string, "application/grpc+")) {
143     out = kApplicationGrpc;
144   } else if (value_string.empty()) {
145     out = kEmpty;
146   } else {
147     // We are intentionally not invoking on_error here since the spec is not
148     // clear on what the behavior should be here, so to avoid breaking anyone,
149     // we should continue to accept this.
150   }
151   return out;
152 }
153 
Encode(ValueType x)154 StaticSlice ContentTypeMetadata::Encode(ValueType x) {
155   switch (x) {
156     case kEmpty:
157       return StaticSlice::FromStaticString("");
158     case kApplicationGrpc:
159       return StaticSlice::FromStaticString("application/grpc");
160     case kInvalid:
161       return StaticSlice::FromStaticString("application/grpc+unknown");
162   }
163   GPR_UNREACHABLE_CODE(
164       return StaticSlice::FromStaticString("unrepresentable value"));
165 }
166 
DisplayValue(ValueType content_type)167 const char* ContentTypeMetadata::DisplayValue(ValueType content_type) {
168   switch (content_type) {
169     case ValueType::kApplicationGrpc:
170       return "application/grpc";
171     case ValueType::kEmpty:
172       return "";
173     default:
174       return "<discarded-invalid-value>";
175   }
176 }
177 
ParseMemento(Slice value,bool,MetadataParseErrorFn on_error)178 GrpcTimeoutMetadata::MementoType GrpcTimeoutMetadata::ParseMemento(
179     Slice value, bool, MetadataParseErrorFn on_error) {
180   auto timeout = ParseTimeout(value);
181   if (!timeout.has_value()) {
182     on_error("invalid value", value);
183     return Duration::Infinity();
184   }
185   return *timeout;
186 }
187 
MementoToValue(MementoType timeout)188 GrpcTimeoutMetadata::ValueType GrpcTimeoutMetadata::MementoToValue(
189     MementoType timeout) {
190   if (timeout == Duration::Infinity()) {
191     return Timestamp::InfFuture();
192   }
193   return Timestamp::Now() + timeout;
194 }
195 
Encode(ValueType x)196 Slice GrpcTimeoutMetadata::Encode(ValueType x) {
197   return Timeout::FromDuration(x - Timestamp::Now()).Encode();
198 }
199 
ParseMemento(Slice value,bool,MetadataParseErrorFn on_error)200 TeMetadata::MementoType TeMetadata::ParseMemento(
201     Slice value, bool, MetadataParseErrorFn on_error) {
202   auto out = kInvalid;
203   if (value == "trailers") {
204     out = kTrailers;
205   } else {
206     on_error("invalid value", value);
207   }
208   return out;
209 }
210 
DisplayValue(ValueType te)211 const char* TeMetadata::DisplayValue(ValueType te) {
212   switch (te) {
213     case ValueType::kTrailers:
214       return "trailers";
215     default:
216       return "<discarded-invalid-value>";
217   }
218 }
219 
Parse(absl::string_view value,MetadataParseErrorFn on_error)220 HttpSchemeMetadata::ValueType HttpSchemeMetadata::Parse(
221     absl::string_view value, MetadataParseErrorFn on_error) {
222   if (value == "http") {
223     return kHttp;
224   } else if (value == "https") {
225     return kHttps;
226   }
227   on_error("invalid value", Slice::FromCopiedBuffer(value));
228   return kInvalid;
229 }
230 
Encode(ValueType x)231 StaticSlice HttpSchemeMetadata::Encode(ValueType x) {
232   switch (x) {
233     case kHttp:
234       return StaticSlice::FromStaticString("http");
235     case kHttps:
236       return StaticSlice::FromStaticString("https");
237     default:
238       abort();
239   }
240 }
241 
EncodedSizeOfKey(HttpSchemeMetadata,HttpSchemeMetadata::ValueType x)242 size_t EncodedSizeOfKey(HttpSchemeMetadata, HttpSchemeMetadata::ValueType x) {
243   switch (x) {
244     case HttpSchemeMetadata::kHttp:
245       return 4;
246     case HttpSchemeMetadata::kHttps:
247       return 5;
248     default:
249       return 0;
250   }
251 }
252 
DisplayValue(ValueType content_type)253 const char* HttpSchemeMetadata::DisplayValue(ValueType content_type) {
254   switch (content_type) {
255     case kHttp:
256       return "http";
257     case kHttps:
258       return "https";
259     default:
260       return "<discarded-invalid-value>";
261   }
262 }
263 
ParseMemento(Slice value,bool,MetadataParseErrorFn on_error)264 HttpMethodMetadata::MementoType HttpMethodMetadata::ParseMemento(
265     Slice value, bool, MetadataParseErrorFn on_error) {
266   auto out = kInvalid;
267   auto value_string = value.as_string_view();
268   if (value_string == "POST") {
269     out = kPost;
270   } else if (value_string == "PUT") {
271     out = kPut;
272   } else if (value_string == "GET") {
273     out = kGet;
274   } else {
275     on_error("invalid value", value);
276   }
277   return out;
278 }
279 
Encode(ValueType x)280 StaticSlice HttpMethodMetadata::Encode(ValueType x) {
281   switch (x) {
282     case kPost:
283       return StaticSlice::FromStaticString("POST");
284     case kPut:
285       return StaticSlice::FromStaticString("PUT");
286     case kGet:
287       return StaticSlice::FromStaticString("GET");
288     default:
289       // TODO(ctiller): this should be an abort, we should split up the debug
290       // string generation from the encode string generation so that debug
291       // strings can always succeed and encode strings can crash.
292       return StaticSlice::FromStaticString("<<INVALID METHOD>>");
293   }
294 }
295 
DisplayValue(ValueType content_type)296 const char* HttpMethodMetadata::DisplayValue(ValueType content_type) {
297   switch (content_type) {
298     case kPost:
299       return "POST";
300     case kGet:
301       return "GET";
302     case kPut:
303       return "PUT";
304     default:
305       return "<discarded-invalid-value>";
306   }
307 }
308 
309 CompressionAlgorithmBasedMetadata::MementoType
ParseMemento(Slice value,bool,MetadataParseErrorFn on_error)310 CompressionAlgorithmBasedMetadata::ParseMemento(Slice value, bool,
311                                                 MetadataParseErrorFn on_error) {
312   auto algorithm = ParseCompressionAlgorithm(value.as_string_view());
313   if (!algorithm.has_value()) {
314     on_error("invalid value", value);
315     return GRPC_COMPRESS_NONE;
316   }
317   return *algorithm;
318 }
319 
ParseMemento(Slice value,bool,MetadataParseErrorFn on_error)320 Duration GrpcRetryPushbackMsMetadata::ParseMemento(
321     Slice value, bool, MetadataParseErrorFn on_error) {
322   int64_t out;
323   if (!absl::SimpleAtoi(value.as_string_view(), &out)) {
324     on_error("not an integer", value);
325     return Duration::NegativeInfinity();
326   }
327   return Duration::Milliseconds(out);
328 }
329 
Encode(const ValueType & x)330 Slice LbCostBinMetadata::Encode(const ValueType& x) {
331   auto slice =
332       MutableSlice::CreateUninitialized(sizeof(double) + x.name.length());
333   memcpy(slice.data(), &x.cost, sizeof(double));
334   memcpy(slice.data() + sizeof(double), x.name.data(), x.name.length());
335   return Slice(std::move(slice));
336 }
337 
DisplayValue(ValueType x)338 std::string LbCostBinMetadata::DisplayValue(ValueType x) {
339   return absl::StrCat(x.name, ":", x.cost);
340 }
341 
ParseMemento(Slice value,bool,MetadataParseErrorFn on_error)342 LbCostBinMetadata::MementoType LbCostBinMetadata::ParseMemento(
343     Slice value, bool, MetadataParseErrorFn on_error) {
344   if (value.length() < sizeof(double)) {
345     on_error("too short", value);
346     return {0, ""};
347   }
348   MementoType out;
349   memcpy(&out.cost, value.data(), sizeof(double));
350   out.name =
351       std::string(reinterpret_cast<const char*>(value.data()) + sizeof(double),
352                   value.length() - sizeof(double));
353   return out;
354 }
355 
DisplayValue(ValueType x)356 std::string GrpcStreamNetworkState::DisplayValue(ValueType x) {
357   switch (x) {
358     case kNotSentOnWire:
359       return "not sent on wire";
360     case kNotSeenByServer:
361       return "not seen by server";
362   }
363   GPR_UNREACHABLE_CODE(return "unknown value");
364 }
365 
DisplayValue(void * x)366 std::string GrpcRegisteredMethod::DisplayValue(void* x) {
367   return absl::StrFormat("%p", x);
368 }
369 
DisplayValue(const ValueType & x)370 std::string PeerString::DisplayValue(const ValueType& x) {
371   return std::string(x.as_string_view());
372 }
373 
DisplayValue(const std::string & x)374 const std::string& GrpcStatusContext::DisplayValue(const std::string& x) {
375   return x;
376 }
377 
DisplayValue(ValueType x)378 std::string WaitForReady::DisplayValue(ValueType x) {
379   return absl::StrCat(x.value ? "true" : "false",
380                       x.explicitly_set ? " (explicit)" : "");
381 }
382 
383 }  // namespace grpc_core
384