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 #ifndef GRPC_SRC_CORE_LIB_TRANSPORT_PARSED_METADATA_H
16 #define GRPC_SRC_CORE_LIB_TRANSPORT_PARSED_METADATA_H
17 
18 #include <grpc/support/port_platform.h>
19 
20 #include <string.h>
21 
22 #include <cstdint>
23 #include <string>
24 #include <type_traits>
25 #include <utility>
26 
27 #include "absl/functional/function_ref.h"
28 #include "absl/meta/type_traits.h"
29 #include "absl/strings/escaping.h"
30 #include "absl/strings/match.h"
31 #include "absl/strings/str_cat.h"
32 #include "absl/strings/string_view.h"
33 
34 #include <grpc/slice.h>
35 
36 #include "src/core/lib/gprpp/time.h"
37 #include "src/core/lib/slice/slice.h"
38 
39 namespace grpc_core {
40 
41 using MetadataParseErrorFn =
42     absl::FunctionRef<void(absl::string_view error, const Slice& value)>;
43 
44 namespace metadata_detail {
45 
46 // Helper to determine whether a traits metadata is inlinable inside a memento,
47 // or (if not) we'll need to take the memory allocation path.
48 template <typename Which>
49 struct HasSimpleMemento {
50   static constexpr bool value =
51       (std::is_trivial<typename Which::MementoType>::value &&
52        sizeof(typename Which::MementoType) <= sizeof(grpc_slice)) ||
53       std::is_same<typename Which::MementoType, Duration>::value;
54 };
55 
56 // Storage type for a single metadata entry.
57 union Buffer {
58   uint8_t trivial[sizeof(grpc_slice)];
59   void* pointer;
60   grpc_slice slice;
61 };
62 
63 // Given a key and a value, concatenate together to make a debug string.
64 // Split out to avoid template bloat.
65 std::string MakeDebugString(absl::string_view key, absl::string_view value);
66 
67 // Wrapper around MakeDebugString.
68 // For the value part, use two functions - one to extract a typed field from
69 // Buffer, and a second (sourced from the trait) to generate a displayable debug
70 // string from the field value. We try to maximize indirection/code sharing here
71 // as this is not critical path code and we'd like to avoid some code bloat -
72 // better to scale by number of types than then number of metadata traits!
73 template <typename Field, typename CompatibleWithField, typename Display>
MakeDebugStringPipeline(absl::string_view key,const Buffer & value,Field (* field_from_buffer)(const Buffer &),Display (* display_from_field)(CompatibleWithField))74 GPR_ATTRIBUTE_NOINLINE std::string MakeDebugStringPipeline(
75     absl::string_view key, const Buffer& value,
76     Field (*field_from_buffer)(const Buffer&),
77     Display (*display_from_field)(CompatibleWithField)) {
78   return MakeDebugString(
79       key, absl::StrCat(display_from_field(field_from_buffer(value))));
80 }
81 
82 // Extract a trivial field value from a Buffer - for MakeDebugStringPipeline.
83 template <typename Field>
FieldFromTrivial(const Buffer & value)84 Field FieldFromTrivial(const Buffer& value) {
85   Field field;
86   memcpy(&field, value.trivial, sizeof(Field));
87   return field;
88 }
89 
90 // Extract a pointer field value from a Buffer - for MakeDebugStringPipeline.
91 template <typename Field>
FieldFromPointer(const Buffer & value)92 Field FieldFromPointer(const Buffer& value) {
93   return *static_cast<const Field*>(value.pointer);
94 }
95 
96 // Extract a Slice from a Buffer.
97 Slice SliceFromBuffer(const Buffer& buffer);
98 
99 // Unref the grpc_slice part of a Buffer (assumes it is in fact a grpc_slice).
100 void DestroySliceValue(const Buffer& value);
101 
102 // Destroy a trivial memento (empty function).
103 void DestroyTrivialMemento(const Buffer& value);
104 
105 // Set a slice value in a container
106 template <Slice (*MementoToValue)(Slice)>
SetSliceValue(Slice * set,const Buffer & value)107 void SetSliceValue(Slice* set, const Buffer& value) {
108   *set = MementoToValue(SliceFromBuffer(value));
109 }
110 
111 }  // namespace metadata_detail
112 
113 // A parsed metadata value.
114 // This type captures a type erased MementoType from one trait of
115 // MetadataContainer, and provides utilities to manipulate that and to set it on
116 // a MetadataContainer.
117 template <typename MetadataContainer>
118 class ParsedMetadata {
119  public:
120   // Construct metadata from a trait Which of MetadataContainer.
121   // Two versions: the first is for simple inlinable mementos, and the second
122   // forces an allocation.
123   template <typename Which>
ParsedMetadata(Which,absl::enable_if_t<metadata_detail::HasSimpleMemento<Which>::value,typename Which::MementoType> value,uint32_t transport_size)124   ParsedMetadata(
125       Which,
126       absl::enable_if_t<metadata_detail::HasSimpleMemento<Which>::value,
127                         typename Which::MementoType>
128           value,
129       uint32_t transport_size)
130       : vtable_(ParsedMetadata::template TrivialTraitVTable<Which>()),
131         transport_size_(transport_size) {
132     memcpy(value_.trivial, &value, sizeof(value));
133   }
134   template <typename Which>
ParsedMetadata(Which,absl::enable_if_t<!metadata_detail::HasSimpleMemento<Which>::value &&!std::is_convertible<typename Which::MementoType,Slice>::value,typename Which::MementoType> value,uint32_t transport_size)135   ParsedMetadata(
136       Which,
137       absl::enable_if_t<
138           !metadata_detail::HasSimpleMemento<Which>::value &&
139               !std::is_convertible<typename Which::MementoType, Slice>::value,
140           typename Which::MementoType>
141           value,
142       uint32_t transport_size)
143       : vtable_(ParsedMetadata::template NonTrivialTraitVTable<Which>()),
144         transport_size_(transport_size) {
145     value_.pointer = new typename Which::MementoType(std::move(value));
146   }
147   // Construct metadata from a Slice typed value.
148   template <typename Which>
ParsedMetadata(Which,Slice value,uint32_t transport_size)149   ParsedMetadata(Which, Slice value, uint32_t transport_size)
150       : vtable_(ParsedMetadata::template SliceTraitVTable<Which>()),
151         transport_size_(transport_size) {
152     value_.slice = value.TakeCSlice();
153   }
154   // Construct metadata from a string key, slice value pair.
155   // FromSlicePair() is used to adjust the overload set so that we don't
156   // inadvertently match against any of the previous overloads.
157   // TODO(ctiller): re-evaluate the overload functions here so and maybe
158   // introduce some factory functions?
159   struct FromSlicePair {};
ParsedMetadata(FromSlicePair,Slice key,Slice value,uint32_t transport_size)160   ParsedMetadata(FromSlicePair, Slice key, Slice value, uint32_t transport_size)
161       : vtable_(ParsedMetadata::KeyValueVTable(key.as_string_view())),
162         transport_size_(transport_size) {
163     value_.pointer =
164         new std::pair<Slice, Slice>(std::move(key), std::move(value));
165   }
ParsedMetadata()166   ParsedMetadata() : vtable_(EmptyVTable()), transport_size_(0) {}
~ParsedMetadata()167   ~ParsedMetadata() { vtable_->destroy(value_); }
168 
169   // Non copyable, but movable.
170   ParsedMetadata(const ParsedMetadata&) = delete;
171   ParsedMetadata& operator=(const ParsedMetadata&) = delete;
ParsedMetadata(ParsedMetadata && other)172   ParsedMetadata(ParsedMetadata&& other) noexcept
173       : vtable_(other.vtable_),
174         value_(other.value_),
175         transport_size_(other.transport_size_) {
176     other.vtable_ = EmptyVTable();
177   }
178   ParsedMetadata& operator=(ParsedMetadata&& other) noexcept {
179     vtable_ = other.vtable_;
180     value_ = other.value_;
181     transport_size_ = other.transport_size_;
182     other.vtable_ = EmptyVTable();
183     return *this;
184   }
185 
186   // Set this parsed value on a container.
SetOnContainer(MetadataContainer * container)187   void SetOnContainer(MetadataContainer* container) const {
188     vtable_->set(value_, container);
189   }
190 
191   // Is this a binary header or not?
is_binary_header()192   bool is_binary_header() const { return vtable_->is_binary_header; }
193   // HTTP2 defined storage size of this metadatum.
transport_size()194   uint32_t transport_size() const { return transport_size_; }
195   // Create a new parsed metadata with the same key but a different value.
WithNewValue(Slice value,uint32_t value_wire_size,MetadataParseErrorFn on_error)196   ParsedMetadata WithNewValue(Slice value, uint32_t value_wire_size,
197                               MetadataParseErrorFn on_error) const {
198     ParsedMetadata result;
199     result.vtable_ = vtable_;
200     result.value_ = value_;
201     result.transport_size_ =
202         TransportSize(static_cast<uint32_t>(key().length()), value_wire_size);
203     vtable_->with_new_value(&value, on_error, &result);
204     return result;
205   }
DebugString()206   std::string DebugString() const { return vtable_->debug_string(value_); }
key()207   absl::string_view key() const {
208     if (vtable_->key == nullptr) return vtable_->key_value;
209     return vtable_->key(value_);
210   }
211 
212   // TODO(ctiller): move to transport
TransportSize(uint32_t key_size,uint32_t value_size)213   static uint32_t TransportSize(uint32_t key_size, uint32_t value_size) {
214     // TODO(ctiller): use hpack constant?
215     return key_size + value_size + 32;
216   }
217 
218  private:
219   using Buffer = metadata_detail::Buffer;
220 
221   struct VTable {
222     const bool is_binary_header;
223     void (*const destroy)(const Buffer& value);
224     void (*const set)(const Buffer& value, MetadataContainer* container);
225     // result is a bitwise copy of the originating ParsedMetadata.
226     void (*const with_new_value)(Slice* new_value,
227                                  MetadataParseErrorFn on_error,
228                                  ParsedMetadata* result);
229     std::string (*const debug_string)(const Buffer& value);
230     // The key - if key is null, use key_value, otherwise call key.
231     absl::string_view key_value;
232     absl::string_view (*const key)(const Buffer& value);
233   };
234 
235   static const VTable* EmptyVTable();
236   static const VTable* KeyValueVTable(absl::string_view key);
237   template <typename Which>
238   static const VTable* TrivialTraitVTable();
239   template <typename Which>
240   static const VTable* NonTrivialTraitVTable();
241   template <typename Which>
242   static const VTable* SliceTraitVTable();
243 
244   template <Slice (*ParseMemento)(Slice, MetadataParseErrorFn)>
WithNewValueSetSlice(Slice * slice,MetadataParseErrorFn on_error,ParsedMetadata * result)245   GPR_ATTRIBUTE_NOINLINE static void WithNewValueSetSlice(
246       Slice* slice, MetadataParseErrorFn on_error, ParsedMetadata* result) {
247     result->value_.slice =
248         ParseMemento(std::move(*slice), on_error).TakeCSlice();
249   }
250 
251   template <typename T, T (*ParseMemento)(Slice, MetadataParseErrorFn)>
WithNewValueSetTrivial(Slice * slice,MetadataParseErrorFn on_error,ParsedMetadata * result)252   GPR_ATTRIBUTE_NOINLINE static void WithNewValueSetTrivial(
253       Slice* slice, MetadataParseErrorFn on_error, ParsedMetadata* result) {
254     T memento = ParseMemento(std::move(*slice), on_error);
255     memcpy(result->value_.trivial, &memento, sizeof(memento));
256   }
257 
258   const VTable* vtable_;
259   Buffer value_;
260   uint32_t transport_size_;
261 };
262 
263 namespace metadata_detail {}  // namespace metadata_detail
264 
265 template <typename MetadataContainer>
266 const typename ParsedMetadata<MetadataContainer>::VTable*
EmptyVTable()267 ParsedMetadata<MetadataContainer>::EmptyVTable() {
268   static const VTable vtable = {
269       false,
270       // destroy
271       metadata_detail::DestroyTrivialMemento,
272       // set
273       [](const Buffer&, MetadataContainer*) {},
274       // with_new_value
275       [](Slice*, MetadataParseErrorFn, ParsedMetadata*) {},
276       // debug_string
277       [](const Buffer&) -> std::string { return "empty"; },
278       // key
279       "",
280       nullptr,
281   };
282   return &vtable;
283 }
284 
285 template <typename MetadataContainer>
286 template <typename Which>
287 const typename ParsedMetadata<MetadataContainer>::VTable*
TrivialTraitVTable()288 ParsedMetadata<MetadataContainer>::TrivialTraitVTable() {
289   static const VTable vtable = {
290       absl::EndsWith(Which::key(), "-bin"),
291       // destroy
292       metadata_detail::DestroyTrivialMemento,
293       // set
294       [](const Buffer& value, MetadataContainer* map) {
295         map->Set(
296             Which(),
297             Which::MementoToValue(
298                 metadata_detail::FieldFromTrivial<typename Which::MementoType>(
299                     value)));
300       },
301       // with_new_value
302       WithNewValueSetTrivial<typename Which::MementoType, Which::ParseMemento>,
303       // debug_string
304       [](const Buffer& value) {
305         return metadata_detail::MakeDebugStringPipeline(
306             Which::key(), value,
307             metadata_detail::FieldFromTrivial<typename Which::MementoType>,
308             Which::DisplayMemento);
309       },
310       // key
311       Which::key(),
312       nullptr,
313   };
314   return &vtable;
315 }
316 
317 template <typename MetadataContainer>
318 template <typename Which>
319 const typename ParsedMetadata<MetadataContainer>::VTable*
NonTrivialTraitVTable()320 ParsedMetadata<MetadataContainer>::NonTrivialTraitVTable() {
321   static const VTable vtable = {
322       absl::EndsWith(Which::key(), "-bin"),
323       // destroy
324       [](const Buffer& value) {
325         delete static_cast<typename Which::MementoType*>(value.pointer);
326       },
327       // set
328       [](const Buffer& value, MetadataContainer* map) {
329         auto* p = static_cast<typename Which::MementoType*>(value.pointer);
330         map->Set(Which(), Which::MementoToValue(*p));
331       },
332       // with_new_value
333       [](Slice* value, MetadataParseErrorFn on_error, ParsedMetadata* result) {
334         result->value_.pointer = new typename Which::MementoType(
335             Which::ParseMemento(std::move(*value), on_error));
336       },
337       // debug_string
338       [](const Buffer& value) {
339         return metadata_detail::MakeDebugStringPipeline(
340             Which::key(), value,
341             metadata_detail::FieldFromPointer<typename Which::MementoType>,
342             Which::DisplayMemento);
343       },
344       // key
345       Which::key(),
346       nullptr,
347   };
348   return &vtable;
349 }
350 
351 template <typename MetadataContainer>
352 template <typename Which>
353 const typename ParsedMetadata<MetadataContainer>::VTable*
SliceTraitVTable()354 ParsedMetadata<MetadataContainer>::SliceTraitVTable() {
355   static const VTable vtable = {
356       absl::EndsWith(Which::key(), "-bin"),
357       // destroy
358       metadata_detail::DestroySliceValue,
359       // set
360       [](const Buffer& value, MetadataContainer* map) {
361         metadata_detail::SetSliceValue<Which::MementoToValue>(
362             map->GetOrCreatePointer(Which()), value);
363       },
364       // with_new_value
365       WithNewValueSetSlice<Which::ParseMemento>,
366       // debug_string
367       [](const Buffer& value) {
368         return metadata_detail::MakeDebugStringPipeline(
369             Which::key(), value, metadata_detail::SliceFromBuffer,
370             Which::DisplayMemento);
371       },
372       // key
373       Which::key(),
374       nullptr,
375   };
376   return &vtable;
377 }
378 
379 template <typename MetadataContainer>
380 const typename ParsedMetadata<MetadataContainer>::VTable*
KeyValueVTable(absl::string_view key)381 ParsedMetadata<MetadataContainer>::KeyValueVTable(absl::string_view key) {
382   using KV = std::pair<Slice, Slice>;
383   static const auto destroy = [](const Buffer& value) {
384     delete static_cast<KV*>(value.pointer);
385   };
386   static const auto set = [](const Buffer& value, MetadataContainer* map) {
387     auto* p = static_cast<KV*>(value.pointer);
388     map->unknown_.Append(p->first.as_string_view(), p->second.Ref());
389   };
390   static const auto with_new_value = [](Slice* value, MetadataParseErrorFn,
391                                         ParsedMetadata* result) {
392     auto* p = new KV{
393         static_cast<KV*>(result->value_.pointer)->first.Ref(),
394         std::move(*value),
395     };
396     result->value_.pointer = p;
397   };
398   static const auto debug_string = [](const Buffer& value) {
399     auto* p = static_cast<KV*>(value.pointer);
400     return absl::StrCat(p->first.as_string_view(), ": ",
401                         p->second.as_string_view());
402   };
403   static const auto binary_debug_string = [](const Buffer& value) {
404     auto* p = static_cast<KV*>(value.pointer);
405     return absl::StrCat(p->first.as_string_view(), ": \"",
406                         absl::CEscape(p->second.as_string_view()), "\"");
407   };
408   static const auto key_fn = [](const Buffer& value) {
409     return static_cast<KV*>(value.pointer)->first.as_string_view();
410   };
411   static const VTable vtable[2] = {
412       {false, destroy, set, with_new_value, debug_string, "", key_fn},
413       {true, destroy, set, with_new_value, binary_debug_string, "", key_fn},
414   };
415   return &vtable[absl::EndsWith(key, "-bin")];
416 }
417 
418 }  // namespace grpc_core
419 
420 #endif  // GRPC_SRC_CORE_LIB_TRANSPORT_PARSED_METADATA_H
421