| /* |
| * |
| * 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_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_ENCODER_H |
| #define GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_ENCODER_H |
| |
| #include <grpc/support/port_platform.h> |
| |
| #include <cstdint> |
| |
| #include <grpc/slice.h> |
| #include <grpc/slice_buffer.h> |
| |
| #include "src/core/ext/transport/chttp2/transport/frame.h" |
| #include "src/core/ext/transport/chttp2/transport/hpack_encoder_table.h" |
| #include "src/core/lib/transport/metadata_batch.h" |
| #include "src/core/lib/transport/transport.h" |
| |
| extern grpc_core::TraceFlag grpc_http_trace; |
| |
| namespace grpc_core { |
| |
| class HPackCompressor { |
| class SliceIndex; |
| |
| public: |
| HPackCompressor() = default; |
| ~HPackCompressor() = default; |
| |
| // Maximum table size we'll actually use. |
| static constexpr uint32_t kMaxTableSize = 1024 * 1024; |
| |
| void SetMaxTableSize(uint32_t max_table_size); |
| void SetMaxUsableSize(uint32_t max_table_size); |
| |
| uint32_t test_only_table_size() const { |
| return table_.test_only_table_size(); |
| } |
| |
| struct EncodeHeaderOptions { |
| uint32_t stream_id; |
| bool is_end_of_stream; |
| bool use_true_binary_metadata; |
| size_t max_frame_size; |
| grpc_transport_one_way_stats* stats; |
| }; |
| |
| template <typename HeaderSet> |
| void EncodeHeaders(const EncodeHeaderOptions& options, |
| const HeaderSet& headers, grpc_slice_buffer* output) { |
| Framer framer(options, this, output); |
| headers.Encode(&framer); |
| } |
| |
| class Framer { |
| public: |
| Framer(const EncodeHeaderOptions& options, HPackCompressor* compressor, |
| grpc_slice_buffer* output); |
| ~Framer() { FinishFrame(true); } |
| |
| Framer(const Framer&) = delete; |
| Framer& operator=(const Framer&) = delete; |
| |
| void Encode(const Slice& key, const Slice& value); |
| void Encode(HttpPathMetadata, const Slice& value); |
| void Encode(HttpAuthorityMetadata, const Slice& value); |
| void Encode(HttpStatusMetadata, uint32_t status); |
| void Encode(GrpcTimeoutMetadata, Timestamp deadline); |
| void Encode(TeMetadata, TeMetadata::ValueType value); |
| void Encode(ContentTypeMetadata, ContentTypeMetadata::ValueType value); |
| void Encode(HttpSchemeMetadata, HttpSchemeMetadata::ValueType value); |
| void Encode(HttpMethodMetadata, HttpMethodMetadata::ValueType method); |
| void Encode(UserAgentMetadata, const Slice& slice); |
| void Encode(GrpcStatusMetadata, grpc_status_code status); |
| void Encode(GrpcEncodingMetadata, grpc_compression_algorithm value); |
| void Encode(GrpcAcceptEncodingMetadata, CompressionAlgorithmSet value); |
| void Encode(GrpcTagsBinMetadata, const Slice& slice); |
| void Encode(GrpcTraceBinMetadata, const Slice& slice); |
| void Encode(GrpcMessageMetadata, const Slice& slice) { |
| if (slice.empty()) return; |
| EmitLitHdrWithNonBinaryStringKeyNotIdx( |
| Slice::FromStaticString("grpc-message"), slice.Ref()); |
| } |
| template <typename Which> |
| void Encode(Which, const typename Which::ValueType& value) { |
| const Slice& slice = MetadataValueAsSlice<Which>(value); |
| if (absl::EndsWith(Which::key(), "-bin")) { |
| EmitLitHdrWithBinaryStringKeyNotIdx( |
| Slice::FromStaticString(Which::key()), slice.Ref()); |
| } else { |
| EmitLitHdrWithNonBinaryStringKeyNotIdx( |
| Slice::FromStaticString(Which::key()), slice.Ref()); |
| } |
| } |
| |
| private: |
| friend class SliceIndex; |
| |
| struct FramePrefix { |
| // index (in output_) of the header for the frame |
| size_t header_idx; |
| // number of bytes in 'output' when we started the frame - used to |
| // calculate frame length |
| size_t output_length_at_start_of_frame; |
| }; |
| |
| FramePrefix BeginFrame(); |
| void FinishFrame(bool is_header_boundary); |
| void EnsureSpace(size_t need_bytes); |
| |
| void AdvertiseTableSizeChange(); |
| void EmitIndexed(uint32_t index); |
| void EmitLitHdrWithNonBinaryStringKeyIncIdx(Slice key_slice, |
| Slice value_slice); |
| void EmitLitHdrWithBinaryStringKeyIncIdx(Slice key_slice, |
| Slice value_slice); |
| void EmitLitHdrWithBinaryStringKeyNotIdx(Slice key_slice, |
| Slice value_slice); |
| void EmitLitHdrWithBinaryStringKeyNotIdx(uint32_t key_index, |
| Slice value_slice); |
| void EmitLitHdrWithNonBinaryStringKeyNotIdx(Slice key_slice, |
| Slice value_slice); |
| |
| void EncodeAlwaysIndexed(uint32_t* index, absl::string_view key, |
| Slice value, uint32_t transport_length); |
| void EncodeIndexedKeyWithBinaryValue(uint32_t* index, absl::string_view key, |
| Slice value); |
| |
| size_t CurrentFrameSize() const; |
| void Add(Slice slice); |
| uint8_t* AddTiny(size_t len); |
| |
| // maximum size of a frame |
| const size_t max_frame_size_; |
| bool is_first_frame_ = true; |
| const bool use_true_binary_metadata_; |
| const bool is_end_of_stream_; |
| // output stream id |
| const uint32_t stream_id_; |
| grpc_slice_buffer* const output_; |
| grpc_transport_one_way_stats* const stats_; |
| HPackCompressor* const compressor_; |
| FramePrefix prefix_; |
| }; |
| |
| private: |
| static constexpr size_t kNumFilterValues = 64; |
| static constexpr uint32_t kNumCachedGrpcStatusValues = 16; |
| |
| // maximum number of bytes we'll use for the decode table (to guard against |
| // peers ooming us by setting decode table size high) |
| uint32_t max_usable_size_ = hpack_constants::kInitialTableSize; |
| // if non-zero, advertise to the decoder that we'll start using a table |
| // of this size |
| bool advertise_table_size_change_ = false; |
| HPackEncoderTable table_; |
| |
| class SliceIndex { |
| public: |
| void EmitTo(absl::string_view key, const Slice& value, Framer* framer); |
| |
| private: |
| struct ValueIndex { |
| ValueIndex(Slice value, uint32_t index) |
| : value(std::move(value)), index(index) {} |
| Slice value; |
| uint32_t index; |
| }; |
| std::vector<ValueIndex> values_; |
| }; |
| |
| struct PreviousTimeout { |
| Timeout timeout; |
| uint32_t index; |
| }; |
| |
| // Index into table_ for the te:trailers metadata element |
| uint32_t te_index_ = 0; |
| // Index into table_ for the content-type metadata element |
| uint32_t content_type_index_ = 0; |
| // Index into table_ for the user-agent metadata element |
| uint32_t user_agent_index_ = 0; |
| // Cached grpc-status values |
| uint32_t cached_grpc_status_[kNumCachedGrpcStatusValues] = {}; |
| // Cached grpc-encoding values |
| uint32_t cached_grpc_encoding_[GRPC_COMPRESS_ALGORITHMS_COUNT] = {}; |
| // Cached grpc-accept-encoding value |
| uint32_t grpc_accept_encoding_index_ = 0; |
| // The grpc-accept-encoding string referred to by grpc_accept_encoding_index_ |
| CompressionAlgorithmSet grpc_accept_encoding_; |
| // Index of something that was sent with grpc-tags-bin |
| uint32_t grpc_tags_bin_index_ = 0; |
| // Index of something that was sent with grpc-trace-bin |
| uint32_t grpc_trace_bin_index_ = 0; |
| // The user-agent string referred to by user_agent_index_ |
| Slice user_agent_; |
| SliceIndex path_index_; |
| SliceIndex authority_index_; |
| std::vector<PreviousTimeout> previous_timeouts_; |
| }; |
| |
| } // namespace grpc_core |
| |
| #endif /* GRPC_CORE_EXT_TRANSPORT_CHTTP2_TRANSPORT_HPACK_ENCODER_H */ |