// // // 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. // // #ifndef GRPC_SRC_CORE_LIB_TRANSPORT_METADATA_BATCH_H #define GRPC_SRC_CORE_LIB_TRANSPORT_METADATA_BATCH_H #include #include #include #include #include #include #include "absl/container/inlined_vector.h" #include "absl/functional/function_ref.h" #include "absl/meta/type_traits.h" #include "absl/strings/numbers.h" #include "absl/strings/string_view.h" #include "absl/types/optional.h" #include #include #include #include "src/core/lib/compression/compression_internal.h" #include "src/core/lib/gprpp/chunked_vector.h" #include "src/core/lib/gprpp/packed_table.h" #include "src/core/lib/gprpp/time.h" #include "src/core/lib/resource_quota/arena.h" #include "src/core/lib/slice/slice.h" #include "src/core/lib/transport/custom_metadata.h" #include "src/core/lib/transport/metadata_compression_traits.h" #include "src/core/lib/transport/parsed_metadata.h" #include "src/core/lib/transport/simple_slice_based_metadata.h" namespace grpc_core { /////////////////////////////////////////////////////////////////////////////// // Metadata traits // Given a metadata key and a value, return the encoded size. // Defaults to calling the key's Encode() method and then calculating the size // of that, but can be overridden for specific keys if there's a better way of // doing this. // May return 0 if the size is unknown/unknowable. template size_t EncodedSizeOfKey(Key, const typename Key::ValueType& value) { return Key::Encode(value).size(); } // grpc-timeout metadata trait. // ValueType is defined as Timestamp - an absolute timestamp (i.e. a // deadline!), that is converted to a duration by transports before being // sent. // TODO(ctiller): Move this elsewhere. During the transition we need to be able // to name this in MetadataMap, but ultimately once the transition is done we // should not need to. struct GrpcTimeoutMetadata { static constexpr bool kRepeatable = false; using ValueType = Timestamp; using MementoType = Duration; using CompressionTraits = TimeoutCompressor; static absl::string_view key() { return "grpc-timeout"; } static MementoType ParseMemento(Slice value, MetadataParseErrorFn on_error); static ValueType MementoToValue(MementoType timeout); static Slice Encode(ValueType x); static std::string DisplayValue(ValueType x) { return x.ToString(); } static std::string DisplayMemento(MementoType x) { return x.ToString(); } }; // TE metadata trait. struct TeMetadata { static constexpr bool kRepeatable = false; // HTTP2 says that TE can either be empty or "trailers". // Empty means this trait is not included, "trailers" means kTrailers, and // kInvalid is used to remember an invalid value. enum ValueType : uint8_t { kTrailers, kInvalid, }; using MementoType = ValueType; using CompressionTraits = KnownValueCompressor; static absl::string_view key() { return "te"; } static MementoType ParseMemento(Slice value, MetadataParseErrorFn on_error); static ValueType MementoToValue(MementoType te) { return te; } static StaticSlice Encode(ValueType x) { GPR_ASSERT(x == kTrailers); return StaticSlice::FromStaticString("trailers"); } static const char* DisplayValue(ValueType te); static const char* DisplayMemento(MementoType te) { return DisplayValue(te); } }; inline size_t EncodedSizeOfKey(TeMetadata, TeMetadata::ValueType x) { return x == TeMetadata::kTrailers ? 8 : 0; } // content-type metadata trait. struct ContentTypeMetadata { static constexpr bool kRepeatable = false; // gRPC says that content-type can be application/grpc[;something] // Core has only ever verified the prefix. // IF we want to start verifying more, we can expand this type. enum ValueType : uint8_t { kApplicationGrpc, kEmpty, kInvalid, }; using MementoType = ValueType; using CompressionTraits = KnownValueCompressor; static absl::string_view key() { return "content-type"; } static MementoType ParseMemento(Slice value, MetadataParseErrorFn on_error); static ValueType MementoToValue(MementoType content_type) { return content_type; } static StaticSlice Encode(ValueType x); static const char* DisplayValue(ValueType content_type); static const char* DisplayMemento(ValueType content_type) { return DisplayValue(content_type); } }; // scheme metadata trait. struct HttpSchemeMetadata { static constexpr bool kRepeatable = false; enum ValueType : uint8_t { kHttp, kHttps, kInvalid, }; using MementoType = ValueType; using CompressionTraits = HttpSchemeCompressor; static absl::string_view key() { return ":scheme"; } static MementoType ParseMemento(Slice value, MetadataParseErrorFn on_error) { return Parse(value.as_string_view(), on_error); } static ValueType Parse(absl::string_view value, MetadataParseErrorFn on_error); static ValueType MementoToValue(MementoType content_type) { return content_type; } static StaticSlice Encode(ValueType x); static const char* DisplayValue(ValueType content_type); static const char* DisplayMemento(MementoType content_type) { return DisplayValue(content_type); } }; size_t EncodedSizeOfKey(HttpSchemeMetadata, HttpSchemeMetadata::ValueType x); // method metadata trait. struct HttpMethodMetadata { static constexpr bool kRepeatable = false; enum ValueType : uint8_t { kPost, kGet, kPut, kInvalid, }; using MementoType = ValueType; using CompressionTraits = HttpMethodCompressor; static absl::string_view key() { return ":method"; } static MementoType ParseMemento(Slice value, MetadataParseErrorFn on_error); static ValueType MementoToValue(MementoType content_type) { return content_type; } static StaticSlice Encode(ValueType x); static const char* DisplayValue(ValueType content_type); static const char* DisplayMemento(MementoType content_type) { return DisplayValue(content_type); } }; // Base type for metadata pertaining to a single compression algorithm // (e.g., "grpc-encoding"). struct CompressionAlgorithmBasedMetadata { using ValueType = grpc_compression_algorithm; using MementoType = ValueType; static MementoType ParseMemento(Slice value, MetadataParseErrorFn on_error); static ValueType MementoToValue(MementoType x) { return x; } static Slice Encode(ValueType x) { GPR_ASSERT(x != GRPC_COMPRESS_ALGORITHMS_COUNT); return Slice::FromStaticString(CompressionAlgorithmAsString(x)); } static const char* DisplayValue(ValueType x) { if (const char* p = CompressionAlgorithmAsString(x)) { return p; } else { return ""; } } static const char* DisplayMemento(MementoType x) { return DisplayValue(x); } }; // grpc-encoding metadata trait. struct GrpcEncodingMetadata : public CompressionAlgorithmBasedMetadata { static constexpr bool kRepeatable = false; using CompressionTraits = SmallIntegralValuesCompressor; static absl::string_view key() { return "grpc-encoding"; } }; // grpc-internal-encoding-request metadata trait. struct GrpcInternalEncodingRequest : public CompressionAlgorithmBasedMetadata { static constexpr bool kRepeatable = false; using CompressionTraits = NoCompressionCompressor; static absl::string_view key() { return "grpc-internal-encoding-request"; } }; // grpc-accept-encoding metadata trait. struct GrpcAcceptEncodingMetadata { static constexpr bool kRepeatable = false; static absl::string_view key() { return "grpc-accept-encoding"; } using ValueType = CompressionAlgorithmSet; using MementoType = ValueType; using CompressionTraits = StableValueCompressor; static MementoType ParseMemento(Slice value, MetadataParseErrorFn) { return CompressionAlgorithmSet::FromString(value.as_string_view()); } static ValueType MementoToValue(MementoType x) { return x; } static Slice Encode(ValueType x) { return x.ToSlice(); } static absl::string_view DisplayValue(ValueType x) { return x.ToString(); } static absl::string_view DisplayMemento(MementoType x) { return DisplayValue(x); } }; // user-agent metadata trait. struct UserAgentMetadata : public SimpleSliceBasedMetadata { static constexpr bool kRepeatable = false; using CompressionTraits = StableValueCompressor; static absl::string_view key() { return "user-agent"; } }; // grpc-message metadata trait. struct GrpcMessageMetadata : public SimpleSliceBasedMetadata { static constexpr bool kRepeatable = false; using CompressionTraits = NoCompressionCompressor; static absl::string_view key() { return "grpc-message"; } }; // host metadata trait. struct HostMetadata : public SimpleSliceBasedMetadata { static constexpr bool kRepeatable = false; using CompressionTraits = NoCompressionCompressor; static absl::string_view key() { return "host"; } }; // endpoint-load-metrics-bin metadata trait. struct EndpointLoadMetricsBinMetadata : public SimpleSliceBasedMetadata { static constexpr bool kRepeatable = false; using CompressionTraits = NoCompressionCompressor; static absl::string_view key() { return "endpoint-load-metrics-bin"; } }; // grpc-server-stats-bin metadata trait. struct GrpcServerStatsBinMetadata : public SimpleSliceBasedMetadata { static constexpr bool kRepeatable = false; using CompressionTraits = NoCompressionCompressor; static absl::string_view key() { return "grpc-server-stats-bin"; } }; // grpc-trace-bin metadata trait. struct GrpcTraceBinMetadata : public SimpleSliceBasedMetadata { static constexpr bool kRepeatable = false; using CompressionTraits = FrequentKeyWithNoValueCompressionCompressor; static absl::string_view key() { return "grpc-trace-bin"; } }; // grpc-tags-bin metadata trait. struct GrpcTagsBinMetadata : public SimpleSliceBasedMetadata { static constexpr bool kRepeatable = false; using CompressionTraits = FrequentKeyWithNoValueCompressionCompressor; static absl::string_view key() { return "grpc-tags-bin"; } }; // :authority metadata trait. struct HttpAuthorityMetadata : public SimpleSliceBasedMetadata { static constexpr bool kRepeatable = false; using CompressionTraits = SmallSetOfValuesCompressor; static absl::string_view key() { return ":authority"; } }; // :path metadata trait. struct HttpPathMetadata : public SimpleSliceBasedMetadata { static constexpr bool kRepeatable = false; using CompressionTraits = SmallSetOfValuesCompressor; static absl::string_view key() { return ":path"; } }; // We separate SimpleIntBasedMetadata into two pieces: one that does not // depend on the invalid value, and one that does. This allows the compiler to // easily see the functions that are shared, and helps reduce code bloat here. template struct SimpleIntBasedMetadataBase { using ValueType = Int; using MementoType = Int; static ValueType MementoToValue(MementoType value) { return value; } static Slice Encode(ValueType x) { return Slice::FromInt64(x); } static Int DisplayValue(ValueType x) { return x; } static Int DisplayMemento(MementoType x) { return x; } }; template struct SimpleIntBasedMetadata : public SimpleIntBasedMetadataBase { static constexpr Int invalid_value() { return kInvalidValue; } static Int ParseMemento(Slice value, MetadataParseErrorFn on_error) { Int out; if (!absl::SimpleAtoi(value.as_string_view(), &out)) { on_error("not an integer", value); out = kInvalidValue; } return out; } }; // grpc-status metadata trait. struct GrpcStatusMetadata : public SimpleIntBasedMetadata { static constexpr bool kRepeatable = false; using CompressionTraits = SmallIntegralValuesCompressor<16>; static absl::string_view key() { return "grpc-status"; } }; // grpc-previous-rpc-attempts metadata trait. struct GrpcPreviousRpcAttemptsMetadata : public SimpleIntBasedMetadata { static constexpr bool kRepeatable = false; using CompressionTraits = NoCompressionCompressor; static absl::string_view key() { return "grpc-previous-rpc-attempts"; } }; // grpc-retry-pushback-ms metadata trait. struct GrpcRetryPushbackMsMetadata { static constexpr bool kRepeatable = false; static absl::string_view key() { return "grpc-retry-pushback-ms"; } using ValueType = Duration; using MementoType = Duration; using CompressionTraits = NoCompressionCompressor; static ValueType MementoToValue(MementoType x) { return x; } static Slice Encode(Duration x) { return Slice::FromInt64(x.millis()); } static int64_t DisplayValue(Duration x) { return x.millis(); } static int64_t DisplayMemento(Duration x) { return DisplayValue(x); } static Duration ParseMemento(Slice value, MetadataParseErrorFn on_error); }; // :status metadata trait. // TODO(ctiller): consider moving to uint16_t struct HttpStatusMetadata : public SimpleIntBasedMetadata { static constexpr bool kRepeatable = false; using CompressionTraits = HttpStatusCompressor; static absl::string_view key() { return ":status"; } }; // "secret" metadata trait used to pass load balancing token between filters. // This should not be exposed outside of gRPC core. class GrpcLbClientStats; struct GrpcLbClientStatsMetadata { static constexpr bool kRepeatable = false; static absl::string_view key() { return "grpclb_client_stats"; } using ValueType = GrpcLbClientStats*; using MementoType = ValueType; using CompressionTraits = NoCompressionCompressor; static ValueType MementoToValue(MementoType value) { return value; } static Slice Encode(ValueType) { abort(); } static const char* DisplayValue(ValueType) { return ""; } static const char* DisplayMemento(MementoType) { return ""; } static MementoType ParseMemento(Slice, MetadataParseErrorFn) { return nullptr; } }; inline size_t EncodedSizeOfKey(GrpcLbClientStatsMetadata, GrpcLbClientStatsMetadata::ValueType) { return 0; } // lb-token metadata struct LbTokenMetadata : public SimpleSliceBasedMetadata { static constexpr bool kRepeatable = false; using CompressionTraits = NoCompressionCompressor; static absl::string_view key() { return "lb-token"; } }; // lb-cost-bin metadata struct LbCostBinMetadata { static constexpr bool kRepeatable = true; static absl::string_view key() { return "lb-cost-bin"; } struct ValueType { double cost; std::string name; }; using MementoType = ValueType; using CompressionTraits = NoCompressionCompressor; static ValueType MementoToValue(MementoType value) { return value; } static Slice Encode(const ValueType& x); static std::string DisplayValue(ValueType x); static std::string DisplayMemento(MementoType x) { return DisplayValue(x); } static MementoType ParseMemento(Slice value, MetadataParseErrorFn on_error); }; // Annotation added by a transport to note whether a failed request was never // placed on the wire, or never seen by a server. struct GrpcStreamNetworkState { static absl::string_view DebugKey() { return "GrpcStreamNetworkState"; } static constexpr bool kRepeatable = false; enum ValueType : uint8_t { kNotSentOnWire, kNotSeenByServer, }; static std::string DisplayValue(ValueType x); }; // Annotation added by a server transport to note the peer making a request. struct PeerString { static absl::string_view DebugKey() { return "PeerString"; } static constexpr bool kRepeatable = false; using ValueType = Slice; static std::string DisplayValue(const ValueType& x); }; // Annotation added by various systems to describe the reason for a failure. struct GrpcStatusContext { static absl::string_view DebugKey() { return "GrpcStatusContext"; } static constexpr bool kRepeatable = true; using ValueType = std::string; static const std::string& DisplayValue(const std::string& x); }; // Annotation added by a transport to note that the status came from the wire. struct GrpcStatusFromWire { static absl::string_view DebugKey() { return "GrpcStatusFromWire"; } static constexpr bool kRepeatable = false; using ValueType = bool; static absl::string_view DisplayValue(bool x) { return x ? "true" : "false"; } }; // Annotation to denote that this call qualifies for cancelled=1 for the // RECV_CLOSE_ON_SERVER op struct GrpcCallWasCancelled { static absl::string_view DebugKey() { return "GrpcCallWasCancelled"; } static constexpr bool kRepeatable = false; using ValueType = bool; static absl::string_view DisplayValue(bool x) { return x ? "true" : "false"; } }; // Annotation added by client surface code to denote wait-for-ready state struct WaitForReady { struct ValueType { bool value = false; bool explicitly_set = false; }; static absl::string_view DebugKey() { return "WaitForReady"; } static constexpr bool kRepeatable = false; static std::string DisplayValue(ValueType x); }; // Annotation added by a transport to note that server trailing metadata // is a Trailers-Only response. struct GrpcTrailersOnly { static absl::string_view DebugKey() { return "GrpcTrailersOnly"; } static constexpr bool kRepeatable = false; using ValueType = bool; static absl::string_view DisplayValue(bool x) { return x ? "true" : "false"; } }; namespace metadata_detail { // Build a key/value formatted debug string. // Output looks like 'key1: value1, key2: value2' // The string is expected to be readable, but not necessarily parsable. class DebugStringBuilder { public: // Add one key/value pair to the output. void Add(absl::string_view key, absl::string_view value); // Finalize the output and return the string. // Subsequent Add calls are UB. std::string TakeOutput() { return std::move(out_); } private: std::string out_; }; // IsEncodable: Given a trait, determine if that trait is encodable, or is // just a value attached to a MetadataMap. We use the presence of the key() // static method to determine if a trait is encodable or not - encodable // traits have string names, and non-encodable traits do not. template struct IsEncodableTrait { static const bool value = false; }; template struct IsEncodableTrait> { static const bool value = true; }; // Helper type - maps a string name to a trait. template struct NameLookup; template struct NameLookup::value, void>, Trait, Traits...> { // Call op->Found(Trait()) if op->name == Trait::key() for some Trait in // Traits. If not found, call op->NotFound(). template static auto Lookup(absl::string_view key, Op* op) -> decltype(op->Found(Trait())) { if (key == Trait::key()) { return op->Found(Trait()); } return NameLookup::Lookup(key, op); } }; template struct NameLookup::value, void>, Trait, Traits...> { template static auto Lookup(absl::string_view key, Op* op) -> decltype(NameLookup::Lookup(key, op)) { return NameLookup::Lookup(key, op); } }; template <> struct NameLookup { template static auto Lookup(absl::string_view key, Op* op) -> decltype(op->NotFound(key)) { return op->NotFound(key); } }; // Helper to take a slice to a memento to a value. // By splitting this part out we can scale code size as the number of // (memento, value) types, rather than as the number of traits. template struct ParseValue { template static GPR_ATTRIBUTE_NOINLINE auto Parse(Slice* value, MetadataParseErrorFn on_error) -> decltype(memento_to_value(parse_memento(std::move(*value), on_error))) { return memento_to_value(parse_memento(std::move(*value), on_error)); } }; // This is an "Op" type for NameLookup. // Used for MetadataMap::Parse, its Found/NotFound methods turn a slice into a // ParsedMetadata object. template class ParseHelper { public: ParseHelper(Slice value, MetadataParseErrorFn on_error, size_t transport_size) : value_(std::move(value)), on_error_(on_error), transport_size_(transport_size) {} template GPR_ATTRIBUTE_NOINLINE ParsedMetadata Found(Trait trait) { return ParsedMetadata( trait, ParseValueToMemento(), static_cast(transport_size_)); } GPR_ATTRIBUTE_NOINLINE ParsedMetadata NotFound( absl::string_view key) { return ParsedMetadata( typename ParsedMetadata::FromSlicePair{}, Slice::FromCopiedString(key), std::move(value_), transport_size_); } private: template GPR_ATTRIBUTE_NOINLINE T ParseValueToMemento() { return parse_memento(std::move(value_), on_error_); } Slice value_; MetadataParseErrorFn on_error_; const size_t transport_size_; }; // This is an "Op" type for NameLookup. // Used for MetadataMap::Append, its Found/NotFound methods turn a slice into // a value and add it to a container. template class AppendHelper { public: AppendHelper(Container* container, Slice value, MetadataParseErrorFn on_error) : container_(container), value_(std::move(value)), on_error_(on_error) {} template GPR_ATTRIBUTE_NOINLINE void Found(Trait trait) { container_->Set( trait, ParseValue:: template Parse( &value_, on_error_)); } GPR_ATTRIBUTE_NOINLINE void NotFound(absl::string_view key) { container_->unknown_.Append(key, std::move(value_)); } private: Container* const container_; Slice value_; MetadataParseErrorFn on_error_; }; // This is an "Op" type for NameLookup. // Used for MetadataMap::Remove, its Found/NotFound methods remove a key from // the container. template class RemoveHelper { public: explicit RemoveHelper(Container* container) : container_(container) {} template GPR_ATTRIBUTE_NOINLINE void Found(Trait trait) { container_->Remove(trait); } GPR_ATTRIBUTE_NOINLINE void NotFound(absl::string_view key) { container_->unknown_.Remove(key); } private: Container* const container_; }; // This is an "Op" type for NameLookup. // Used for MetadataMap::GetStringValue, its Found/NotFound methods generated // a string value from the container. template class GetStringValueHelper { public: explicit GetStringValueHelper(const Container* container, std::string* backing) : container_(container), backing_(backing) {} template GPR_ATTRIBUTE_NOINLINE absl::enable_if_t< Trait::kRepeatable == false && std::is_same::value, absl::optional> Found(Trait) { const auto* value = container_->get_pointer(Trait()); if (value == nullptr) return absl::nullopt; return value->as_string_view(); } template GPR_ATTRIBUTE_NOINLINE absl::enable_if_t< Trait::kRepeatable == true && !std::is_same::value, absl::optional> Found(Trait) { const auto* value = container_->get_pointer(Trait()); if (value == nullptr) return absl::nullopt; backing_->clear(); for (const auto& v : *value) { if (!backing_->empty()) backing_->push_back(','); auto new_segment = Trait::Encode(v); backing_->append(new_segment.begin(), new_segment.end()); } return *backing_; } template GPR_ATTRIBUTE_NOINLINE absl::enable_if_t< Trait::kRepeatable == false && !std::is_same::value, absl::optional> Found(Trait) { const auto* value = container_->get_pointer(Trait()); if (value == nullptr) return absl::nullopt; *backing_ = std::string(Trait::Encode(*value).as_string_view()); return *backing_; } GPR_ATTRIBUTE_NOINLINE absl::optional NotFound( absl::string_view key) { return container_->unknown_.GetStringValue(key, backing_); } private: const Container* const container_; std::string* backing_; }; // Sink for key value logs using LogFn = absl::FunctionRef; template struct AdaptDisplayValueToLog { static std::string ToString(const T& value) { return std::to_string(value); } }; template <> struct AdaptDisplayValueToLog { static std::string ToString(const std::string& value) { return value; } }; template <> struct AdaptDisplayValueToLog { static std::string ToString(const std::string& value) { return value; } }; template <> struct AdaptDisplayValueToLog { static std::string ToString(absl::string_view value) { return std::string(value); } }; template <> struct AdaptDisplayValueToLog { static std::string ToString(Slice value) { return std::string(value.as_string_view()); } }; template <> struct AdaptDisplayValueToLog { static std::string ToString(const char* value) { return std::string(value); } }; template <> struct AdaptDisplayValueToLog { static absl::string_view ToString(StaticSlice value) { return value.as_string_view(); } }; template GPR_ATTRIBUTE_NOINLINE void LogKeyValueTo(absl::string_view key, const T& value, V (*display_value)(U), LogFn log_fn) { log_fn(key, AdaptDisplayValueToLog::ToString(display_value(value))); } // Generate a strong type for metadata values per trait. template struct Value; template struct Value::value, void>> { Value() = default; explicit Value(const typename Which::ValueType& value) : value(value) {} explicit Value(typename Which::ValueType&& value) : value(std::forward(value)) {} Value(const Value&) = delete; Value& operator=(const Value&) = delete; Value(Value&&) noexcept = default; Value& operator=(Value&& other) noexcept { value = std::move(other.value); return *this; } template void EncodeTo(Encoder* encoder) const { encoder->Encode(Which(), value); } template void VisitWith(Encoder* encoder) const { return EncodeTo(encoder); } void LogTo(LogFn log_fn) const { LogKeyValueTo(Which::key(), value, Which::DisplayValue, log_fn); } using StorageType = typename Which::ValueType; GPR_NO_UNIQUE_ADDRESS StorageType value; }; template struct Value::value, void>> { Value() = default; explicit Value(const typename Which::ValueType& value) : value(value) {} explicit Value(typename Which::ValueType&& value) : value(std::forward(value)) {} Value(const Value&) = delete; Value& operator=(const Value&) = delete; Value(Value&&) noexcept = default; Value& operator=(Value&& other) noexcept { value = std::move(other.value); return *this; } template void EncodeTo(Encoder*) const {} template void VisitWith(Encoder* encoder) const { encoder->Encode(Which(), value); } void LogTo(LogFn log_fn) const { LogKeyValueTo(Which::DebugKey(), value, Which::DisplayValue, log_fn); } using StorageType = typename Which::ValueType; GPR_NO_UNIQUE_ADDRESS StorageType value; }; template struct Value::value, void>> { Value() = default; explicit Value(const typename Which::ValueType& value) { this->value.push_back(value); } explicit Value(typename Which::ValueType&& value) { this->value.emplace_back(std::forward(value)); } Value(const Value&) = delete; Value& operator=(const Value&) = delete; Value(Value&& other) noexcept : value(std::move(other.value)) {} Value& operator=(Value&& other) noexcept { value = std::move(other.value); return *this; } template void EncodeTo(Encoder* encoder) const { for (const auto& v : value) { encoder->Encode(Which(), v); } } template void VisitWith(Encoder* encoder) const { return EncodeTo(encoder); } void LogTo(LogFn log_fn) const { for (const auto& v : value) { LogKeyValueTo(Which::key(), v, Which::Encode, log_fn); } } using StorageType = absl::InlinedVector; StorageType value; }; template struct Value::value, void>> { Value() = default; explicit Value(const typename Which::ValueType& value) { this->value.push_back(value); } explicit Value(typename Which::ValueType&& value) { this->value.emplace_back(std::forward(value)); } Value(const Value&) = delete; Value& operator=(const Value&) = delete; Value(Value&& other) noexcept : value(std::move(other.value)) {} Value& operator=(Value&& other) noexcept { value = std::move(other.value); return *this; } template void EncodeTo(Encoder*) const {} template void VisitWith(Encoder* encoder) const { for (const auto& v : value) { encoder->Encode(Which(), v); } } void LogTo(LogFn log_fn) const { for (const auto& v : value) { LogKeyValueTo(Which::DebugKey(), v, Which::DisplayValue, log_fn); } } using StorageType = absl::InlinedVector; StorageType value; }; // Encoder to copy some metadata template class CopySink { public: explicit CopySink(Output* dst) : dst_(dst) {} template void Encode(T trait, V value) { dst_->Set(trait, value); } template void Encode(T trait, const Slice& value) { dst_->Set(trait, std::move(value.AsOwned())); } void Encode(const Slice& key, const Slice& value) { dst_->unknown_.Append(key.as_string_view(), value.Ref()); } private: Output* dst_; }; // Callable for the ForEach in Encode() -- for each value, call the // appropriate encoder method. template struct EncodeWrapper { Encoder* encoder; template void operator()(const Value& which) { which.EncodeTo(encoder); } }; // Callable for the table ForEach in ForEach() -- for each value, call the // appropriate visitor method. template struct ForEachWrapper { Encoder* encoder; template void operator()(const Value& which) { which.VisitWith(encoder); } }; // Callable for the ForEach in Log() struct LogWrapper { LogFn log_fn; template void operator()(const Value& which) { which.LogTo(log_fn); } }; // Encoder to compute TransportSize class TransportSizeEncoder { public: void Encode(const Slice& key, const Slice& value) { size_ += key.length() + value.length() + 32; } template void Encode(Which, const typename Which::ValueType& value) { Add(Which(), value); } void Encode(ContentTypeMetadata, const typename ContentTypeMetadata::ValueType& value) { if (value == ContentTypeMetadata::kInvalid) return; Add(ContentTypeMetadata(), value); } size_t size() const { return size_; } private: template void Add(Which, const typename Which::ValueType& value) { size_ += Which::key().length() + Which::Encode(value).length() + 32; } uint32_t size_ = 0; }; // Handle unknown (non-trait-based) fields in the metadata map. class UnknownMap { public: explicit UnknownMap(Arena* arena) : unknown_(arena) {} using BackingType = ChunkedVector, 10>; void Append(absl::string_view key, Slice value); void Remove(absl::string_view key); absl::optional GetStringValue(absl::string_view key, std::string* backing) const; BackingType::ConstForwardIterator begin() const { return unknown_.cbegin(); } BackingType::ConstForwardIterator end() const { return unknown_.cend(); } bool empty() const { return unknown_.empty(); } size_t size() const { return unknown_.size(); } void Clear() { unknown_.Clear(); } Arena* arena() const { return unknown_.arena(); } private: // Backing store for added metadata. ChunkedVector, 10> unknown_; }; // Given a factory template Factory, construct a type that derives from // Factory for all // MetadataTraits. Useful for transports in defining the stateful parts of their // compression algorithm. template