#pragma once #include #include #include "envoy/common/exception.h" #include "envoy/common/platform.h" #include "envoy/grpc/status.h" #include "envoy/http/filter.h" #include "envoy/http/header_map.h" #include "envoy/http/message.h" #include "common/common/hash.h" #include "common/grpc/status.h" #include "common/protobuf/protobuf.h" #include "absl/types/optional.h" #include "google/rpc/status.pb.h" namespace Envoy { namespace Grpc { class Exception : public EnvoyException { public: Exception(const absl::optional& grpc_status, const std::string& message) : EnvoyException(message), grpc_status_(grpc_status) {} const absl::optional grpc_status_; }; class Common { public: /** * @param headers the headers to parse. * @return bool indicating whether content-type is gRPC. */ static bool hasGrpcContentType(const Http::RequestOrResponseHeaderMap& headers); /** * @param headers the headers to parse. * @return bool indicating whether the header is a gRPC request header. * Currently headers are considered gRPC request headers if they have the gRPC * content type, and have a path header. */ static bool isGrpcRequestHeaders(const Http::RequestHeaderMap& headers); /** * @param headers the headers to parse. * @param bool indicating whether the header is at end_stream. * @return bool indicating whether the header is a gRPC response header */ static bool isGrpcResponseHeaders(const Http::ResponseHeaderMap& headers, bool end_stream); /** * Returns the GrpcStatus code from a given set of trailers, if present. * @param trailers the trailers to parse. * @param allow_user_status whether allow user defined grpc status. * if this value is false, custom grpc status is regarded as invalid status * @return absl::optional the parsed status code or InvalidCode if no valid * status is found. */ static absl::optional getGrpcStatus(const Http::ResponseHeaderOrTrailerMap& trailers, bool allow_user_defined = false); /** * Returns the GrpcStatus code from the set of trailers, headers, and StreamInfo, if present. * @param trailers the trailers to parse for a status code * @param headers the headers to parse if no status code was found in the trailers * @param info the StreamInfo to check for HTTP response code if no code was found in the trailers * or headers * @return absl::optional the parsed status code or absl::nullopt if no status * is found */ static absl::optional getGrpcStatus(const Http::ResponseTrailerMap& trailers, const Http::ResponseHeaderMap& headers, const StreamInfo::StreamInfo& info, bool allow_user_defined = false); /** * Returns the grpc-message from a given set of trailers, if present. * @param trailers the trailers to parse. * @return std::string the gRPC status message or empty string if grpc-message is not present in * trailers. */ static std::string getGrpcMessage(const Http::ResponseHeaderOrTrailerMap& trailers); /** * Returns the decoded google.rpc.Status message from a given set of trailers, if present. * @param trailers the trailers to parse. * @return std::unique_ptr the gRPC status message or empty pointer if no * grpc-status-details-bin trailer found or it was invalid. */ static absl::optional getGrpcStatusDetailsBin(const Http::HeaderMap& trailers); /** * Parse gRPC header 'grpc-timeout' value to a duration in milliseconds. * @param request_headers the header map from which to extract the value of 'grpc-timeout' header. * If this header is missing the timeout corresponds to infinity. The header is encoded in * maximum of 8 decimal digits and a char for the unit. * @return absl::optional the duration in milliseconds. absl::nullopt * is returned if 'grpc-timeout' is missing or malformed. */ static absl::optional getGrpcTimeout(const Http::RequestHeaderMap& request_headers); /** * Encode 'timeout' into 'grpc-timeout' format in the grpc-timeout header. * @param timeout the duration in std::chrono::milliseconds. * @param headers the HeaderMap in which the grpc-timeout header will be set with the timeout in * 'grpc-timeout' format, up to 8 decimal digits and a letter indicating the unit. */ static void toGrpcTimeout(const std::chrono::milliseconds& timeout, Http::RequestHeaderMap& headers); /** * Serialize protobuf message with gRPC frame header. */ static Buffer::InstancePtr serializeToGrpcFrame(const Protobuf::Message& message); /** * Serialize protobuf message. Without grpc header. */ static Buffer::InstancePtr serializeMessage(const Protobuf::Message& message); /** * Prepare headers for protobuf service. */ static Http::RequestMessagePtr prepareHeaders(const std::string& upstream_cluster, const std::string& service_full_name, const std::string& method_name, const absl::optional& timeout); /** * Basic validation of gRPC response, @throws Grpc::Exception in case of non successful response. */ static void validateResponse(Http::ResponseMessage& http_response); /** * @return const std::string& type URL prefix. */ static const std::string& typeUrlPrefix(); /** * Prefix type URL to a qualified name. * @param qualified_name packagename.messagename. * @return qualified_name prefixed with typeUrlPrefix + "/". */ static std::string typeUrl(const std::string& qualified_name); /** * Prepend a gRPC frame header to a Buffer::Instance containing a single gRPC frame. * @param buffer containing the frame data which will be modified. */ static void prependGrpcFrameHeader(Buffer::Instance& buffer); /** * Parse a Buffer::Instance into a Protobuf::Message. * @param buffer containing the data to be parsed. * @param proto the parsed proto. * @return bool true if the parse was successful. */ static bool parseBufferInstance(Buffer::InstancePtr&& buffer, Protobuf::Message& proto); struct RequestNames { absl::string_view service_; absl::string_view method_; }; /** * Resolve the gRPC service and method from the HTTP2 :path header. * @param path supplies the :path header. * @return if both gRPC serve and method have been resolved successfully returns * a populated RequestNames, otherwise returns an empty optional. * @note The return value is only valid as long as `path` is still valid and unmodified. */ static absl::optional resolveServiceAndMethod(const Http::HeaderEntry* path); private: static void checkForHeaderOnlyError(Http::ResponseMessage& http_response); }; } // namespace Grpc } // namespace Envoy