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