// // // 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 <grpc/support/port_platform.h> #include <stdlib.h> #include <cstdint> #include <string> #include <type_traits> #include <utility> #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 <grpc/impl/compression_types.h> #include <grpc/status.h> #include <grpc/support/log.h> #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/parsed_metadata.h" namespace grpc_core { // 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 <typename Key> size_t EncodedSizeOfKey(Key, const typename Key::ValueType& value) { … } // 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 { … }; // TE metadata trait. struct TeMetadata { … }; inline size_t EncodedSizeOfKey(TeMetadata, TeMetadata::ValueType x) { … } // content-type metadata trait. struct ContentTypeMetadata { … }; // scheme metadata trait. struct HttpSchemeMetadata { … }; size_t EncodedSizeOfKey(HttpSchemeMetadata, HttpSchemeMetadata::ValueType x); // method metadata trait. struct HttpMethodMetadata { … }; // Base type for metadata pertaining to a single compression algorithm // (e.g., "grpc-encoding"). struct CompressionAlgorithmBasedMetadata { … }; // grpc-encoding metadata trait. struct GrpcEncodingMetadata : public CompressionAlgorithmBasedMetadata { … }; // grpc-internal-encoding-request metadata trait. struct GrpcInternalEncodingRequest : public CompressionAlgorithmBasedMetadata { … }; // grpc-accept-encoding metadata trait. struct GrpcAcceptEncodingMetadata { … }; struct SimpleSliceBasedMetadata { … }; // user-agent metadata trait. struct UserAgentMetadata : public SimpleSliceBasedMetadata { … }; // grpc-message metadata trait. struct GrpcMessageMetadata : public SimpleSliceBasedMetadata { … }; // host metadata trait. struct HostMetadata : public SimpleSliceBasedMetadata { … }; // endpoint-load-metrics-bin metadata trait. struct EndpointLoadMetricsBinMetadata : public SimpleSliceBasedMetadata { … }; // grpc-server-stats-bin metadata trait. struct GrpcServerStatsBinMetadata : public SimpleSliceBasedMetadata { … }; // grpc-trace-bin metadata trait. struct GrpcTraceBinMetadata : public SimpleSliceBasedMetadata { … }; // grpc-tags-bin metadata trait. struct GrpcTagsBinMetadata : public SimpleSliceBasedMetadata { … }; // :authority metadata trait. struct HttpAuthorityMetadata : public SimpleSliceBasedMetadata { … }; // :path metadata trait. struct HttpPathMetadata : public SimpleSliceBasedMetadata { … }; // 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 <typename Int> struct SimpleIntBasedMetadataBase { … }; template <typename Int, Int kInvalidValue> struct SimpleIntBasedMetadata : public SimpleIntBasedMetadataBase<Int> { … }; // grpc-status metadata trait. struct GrpcStatusMetadata : public SimpleIntBasedMetadata<grpc_status_code, GRPC_STATUS_UNKNOWN> { … }; // grpc-previous-rpc-attempts metadata trait. struct GrpcPreviousRpcAttemptsMetadata : public SimpleIntBasedMetadata<uint32_t, 0> { … }; // grpc-retry-pushback-ms metadata trait. struct GrpcRetryPushbackMsMetadata { … }; // :status metadata trait. // TODO(ctiller): consider moving to uint16_t struct HttpStatusMetadata : public SimpleIntBasedMetadata<uint32_t, 0> { … }; // "secret" metadata trait used to pass load balancing token between filters. // This should not be exposed outside of gRPC core. class GrpcLbClientStats; struct GrpcLbClientStatsMetadata { … }; inline size_t EncodedSizeOfKey(GrpcLbClientStatsMetadata, GrpcLbClientStatsMetadata::ValueType) { … } // lb-token metadata struct LbTokenMetadata : public SimpleSliceBasedMetadata { … }; // lb-cost-bin metadata struct LbCostBinMetadata { … }; // 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 { … }; // Annotation added by a server transport to note the peer making a request. struct PeerString { … }; // Annotation added by various systems to describe the reason for a failure. struct GrpcStatusContext { … }; // Annotation added by a transport to note that the status came from the wire. struct GrpcStatusFromWire { … }; // Annotation added by client surface code to denote wait-for-ready state struct WaitForReady { … }; // Annotation added by a transport to note that server trailing metadata // is a Trailers-Only response. struct GrpcTrailersOnly { … }; 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 { … }; // 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 <typename Trait, typename Ignored = void> struct IsEncodableTrait { … }; IsEncodableTrait<Trait, absl::void_t<decltype(Trait::key())>>; // Helper type - maps a string name to a trait. template <typename MustBeVoid, typename... Traits> struct NameLookup; NameLookup<absl::enable_if_t<IsEncodableTrait<Trait>::value, void>, Trait, Traits...>; NameLookup<absl::enable_if_t<!IsEncodableTrait<Trait>::value, void>, Trait, Traits...>; template <> struct NameLookup<void> { … }; // 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 <typename ParseMementoFn, typename MementoToValueFn> struct ParseValue { … }; // This is an "Op" type for NameLookup. // Used for MetadataMap::Parse, its Found/NotFound methods turn a slice into a // ParsedMetadata object. template <typename Container> class ParseHelper { … }; // 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 <typename Container> class AppendHelper { … }; // This is an "Op" type for NameLookup. // Used for MetadataMap::Remove, its Found/NotFound methods remove a key from // the container. template <typename Container> class RemoveHelper { … }; // This is an "Op" type for NameLookup. // Used for MetadataMap::GetStringValue, its Found/NotFound methods generated // a string value from the container. template <typename Container> class GetStringValueHelper { … }; // Sink for key value logs LogFn; template <typename T> struct AdaptDisplayValueToLog { … }; template <> struct AdaptDisplayValueToLog<std::string> { … }; template <> struct AdaptDisplayValueToLog<const std::string&> { … }; template <> struct AdaptDisplayValueToLog<absl::string_view> { … }; template <> struct AdaptDisplayValueToLog<Slice> { … }; template <> struct AdaptDisplayValueToLog<const char*> { … }; template <> struct AdaptDisplayValueToLog<StaticSlice> { … }; template <typename T, typename U, typename V> GPR_ATTRIBUTE_NOINLINE void LogKeyValueTo(absl::string_view key, const T& value, V (*display_value)(U), LogFn log_fn) { … } // Generate a strong type for metadata values per trait. template <typename Which, typename Ignored = void> struct Value; Value<Which, absl::enable_if_t<Which::kRepeatable == false && IsEncodableTrait<Which>::value, void>>; Value<Which, absl::enable_if_t<Which::kRepeatable == false && !IsEncodableTrait<Which>::value, void>>; Value<Which, absl::enable_if_t<Which::kRepeatable == true && IsEncodableTrait<Which>::value, void>>; Value<Which, absl::enable_if_t<Which::kRepeatable == true && !IsEncodableTrait<Which>::value, void>>; // Encoder to copy some metadata template <typename Output> class CopySink { … }; // Callable for the ForEach in Encode() -- for each value, call the // appropriate encoder method. template <typename Encoder> struct EncodeWrapper { … }; // Callable for the table ForEach in ForEach() -- for each value, call the // appropriate visitor method. template <typename Encoder> struct ForEachWrapper { … }; // Callable for the ForEach in Log() struct LogWrapper { … }; // Encoder to compute TransportSize class TransportSizeEncoder { … }; // Handle unknown (non-trait-based) fields in the metadata map. class UnknownMap { … }; } // namespace metadata_detail // Helper function for encoders // Given a metadata trait, convert the value to a slice. template <typename Which> absl::enable_if_t<std::is_same<typename Which::ValueType, Slice>::value, const Slice&> MetadataValueAsSlice(const Slice& slice) { … } template <typename Which> absl::enable_if_t<!std::is_same<typename Which::ValueType, Slice>::value, Slice> MetadataValueAsSlice(typename Which::ValueType value) { … } // MetadataMap encodes the mapping of metadata keys to metadata values. // // MetadataMap takes a derived class and list of traits. Each of these trait // objects defines one metadata field that is used by core, and so should have // more specialized handling than just using the generic APIs. // // MetadataMap is the backing type for some derived type via the curiously // recursive template pattern. This is because many types consumed by // MetadataMap require the container type to operate on, and many of those // types are instantiated one per trait. A naive implementation without the // Derived type would, for traits A,B,C, then instantiate for some // T<Container, Trait>: // - T<MetadataMap<A,B,C>, A>, // - T<MetadataMap<A,B,C>, B>, // - T<MetadataMap<A,B,C>, C>. // Since these types ultimately need to be recorded in the .dynstr segment // for dynamic linkers (if gRPC is linked as a static library) this would // create O(N^2) bytes of symbols even in stripped libraries. To avoid this // we use the derived type (e.g. grpc_metadata_batch right now) to capture // the container type, and we would write T<grpc_metadata_batch, A>, etc... // Note that now the container type uses a number of bytes that is independent // of the number of traits, and so we return to a linear symbol table growth // function. // // Each trait object has one of two possible signatures, depending on whether // that traits field is encodable or not. // Non-encodable traits are carried in a MetadataMap, but are never passed to // the application nor serialized to wire. // // Encodable traits have the following signature: // // Traits for the "grpc-xyz" metadata field: // struct GrpcXyzMetadata { // // Can this metadata field be repeated? // static constexpr bool kRepeatable = ...; // // The type that's stored on MetadataBatch // using ValueType = ...; // // The type that's stored in compression/decompression tables // using MementoType = ...; // // The string key for this metadata type (for transports that require it) // static absl::string_view key() { return "grpc-xyz"; } // // Parse a memento from a slice // // Takes ownership of value // // Calls fn in the case of an error that should be reported to the user // static MementoType ParseMemento(Slice value, MementoParseErrorFn fn) { // ... // } // // Convert a memento to a value // static ValueType MementoToValue(MementoType memento) { ... } // // Convert a value to its canonical text wire format (the format that // // ParseMemento will accept!) // static Slice Encode(const ValueType& value); // // Convert a value to something that can be passed to StrCat and // displayed // // for debugging // static SomeStrCatableType DisplayValue(ValueType value) { ... } // static SomeStrCatableType DisplayMemento(MementoType value) { ... } // }; // // Non-encodable traits are determined by missing the key() method, and have // the following signature (and by convention omit the Metadata part of the // type name): // // Traits for the GrpcXyz field: // struct GrpcXyz { // // The string key that should be used for debug dumps - should not be a // // valid http2 key (ie all lower case) // static absl::string_view DebugKey() { return "GRPC_XYZ"; } // // Can this metadata field be repeated? // static constexpr bool kRepeatable = ...; // // The type that's stored on MetadataBatch // using ValueType = ...; // // Convert a value to something that can be passed to StrCat and // displayed // // for debugging // static SomeStrCatableType DisplayValue(ValueType value) { ... } // }; // // About parsing and mementos: // // Many gRPC transports exchange metadata as key/value strings, but also allow // for a more efficient representation as a single integer. We can use this // integer representation to avoid reparsing too, by storing the parsed value // in the compression table. This is what mementos are used for. // // A trait offers the capability to turn a slice into a memento via // ParseMemento. This is exposed to users of MetadataMap via the Parse() // method, that returns a ParsedMetadata object. That ParsedMetadata object // can in turn be used to set the same value on many different MetadataMaps // without having to reparse. // // Implementation wise, ParsedMetadata is a type erased wrapper around // MementoType. When we set a value on MetadataMap, we first turn that memento // into a value. For most types, this is going to be a no-op, but for example // for grpc-timeout we make the memento the timeout expressed on the wire, but // we make the value the timestamp of when the timeout will expire (i.e. the // deadline). template <class Derived, typename... Traits> class MetadataMap { … }; // Ok/not-ok check for metadata maps that contain GrpcStatusMetadata, so that // they can be used as result types for TrySeq. template <typename Derived, typename... Args> inline bool IsStatusOk(const MetadataMap<Derived, Args...>& m) { … } template <typename Derived, typename... Traits> MetadataMap<Derived, Traits...>::MetadataMap(Arena* arena) : … { … } template <typename Derived, typename... Traits> MetadataMap<Derived, Traits...>::MetadataMap(MetadataMap&& other) noexcept : … { … } // We never create MetadataMap directly, instead we create Derived, but we // want to be able to move it without redeclaring this. // NOLINTNEXTLINE(misc-unconventional-assign-operator) template <typename Derived, typename... Traits> Derived& MetadataMap<Derived, Traits...>::operator=( MetadataMap&& other) noexcept { … } template <typename Derived, typename... Traits> MetadataMap<Derived, Traits...>::~MetadataMap() = default; template <typename Derived, typename... Traits> void MetadataMap<Derived, Traits...>::Clear() { … } template <typename Derived, typename... Traits> size_t MetadataMap<Derived, Traits...>::TransportSize() const { … } template <typename Derived, typename... Traits> Derived MetadataMap<Derived, Traits...>::Copy() const { … } } // namespace grpc_core struct grpc_metadata_batch; grpc_metadata_batch_base; struct grpc_metadata_batch : public grpc_metadata_batch_base { … }; #endif // GRPC_SRC_CORE_LIB_TRANSPORT_METADATA_BATCH_H