|  | @@ -0,0 +1,920 @@
 | 
	
		
			
				|  |  | +/*
 | 
	
		
			
				|  |  | + *
 | 
	
		
			
				|  |  | + * Copyright 2018 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 GRPCPP_IMPL_CODEGEN_CALL_OP_SET_H
 | 
	
		
			
				|  |  | +#define GRPCPP_IMPL_CODEGEN_CALL_OP_SET_H
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#include <assert.h>
 | 
	
		
			
				|  |  | +#include <array>
 | 
	
		
			
				|  |  | +#include <cstring>
 | 
	
		
			
				|  |  | +#include <functional>
 | 
	
		
			
				|  |  | +#include <map>
 | 
	
		
			
				|  |  | +#include <memory>
 | 
	
		
			
				|  |  | +#include <vector>
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#include <grpcpp/impl/codegen/byte_buffer.h>
 | 
	
		
			
				|  |  | +#include <grpcpp/impl/codegen/call.h>
 | 
	
		
			
				|  |  | +#include <grpcpp/impl/codegen/call_hook.h>
 | 
	
		
			
				|  |  | +#include <grpcpp/impl/codegen/call_op_set_interface.h>
 | 
	
		
			
				|  |  | +#include <grpcpp/impl/codegen/client_context.h>
 | 
	
		
			
				|  |  | +#include <grpcpp/impl/codegen/completion_queue_tag.h>
 | 
	
		
			
				|  |  | +#include <grpcpp/impl/codegen/config.h>
 | 
	
		
			
				|  |  | +#include <grpcpp/impl/codegen/core_codegen_interface.h>
 | 
	
		
			
				|  |  | +#include <grpcpp/impl/codegen/intercepted_channel.h>
 | 
	
		
			
				|  |  | +#include <grpcpp/impl/codegen/interceptor_common.h>
 | 
	
		
			
				|  |  | +#include <grpcpp/impl/codegen/serialization_traits.h>
 | 
	
		
			
				|  |  | +#include <grpcpp/impl/codegen/slice.h>
 | 
	
		
			
				|  |  | +#include <grpcpp/impl/codegen/string_ref.h>
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#include <grpc/impl/codegen/atm.h>
 | 
	
		
			
				|  |  | +#include <grpc/impl/codegen/compression_types.h>
 | 
	
		
			
				|  |  | +#include <grpc/impl/codegen/grpc_types.h>
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +namespace grpc {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +class CompletionQueue;
 | 
	
		
			
				|  |  | +extern CoreCodegenInterface* g_core_codegen_interface;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +namespace internal {
 | 
	
		
			
				|  |  | +class Call;
 | 
	
		
			
				|  |  | +class CallHook;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +// TODO(yangg) if the map is changed before we send, the pointers will be a
 | 
	
		
			
				|  |  | +// mess. Make sure it does not happen.
 | 
	
		
			
				|  |  | +inline grpc_metadata* FillMetadataArray(
 | 
	
		
			
				|  |  | +    const std::multimap<grpc::string, grpc::string>& metadata,
 | 
	
		
			
				|  |  | +    size_t* metadata_count, const grpc::string& optional_error_details) {
 | 
	
		
			
				|  |  | +  *metadata_count = metadata.size() + (optional_error_details.empty() ? 0 : 1);
 | 
	
		
			
				|  |  | +  if (*metadata_count == 0) {
 | 
	
		
			
				|  |  | +    return nullptr;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  grpc_metadata* metadata_array =
 | 
	
		
			
				|  |  | +      (grpc_metadata*)(g_core_codegen_interface->gpr_malloc(
 | 
	
		
			
				|  |  | +          (*metadata_count) * sizeof(grpc_metadata)));
 | 
	
		
			
				|  |  | +  size_t i = 0;
 | 
	
		
			
				|  |  | +  for (auto iter = metadata.cbegin(); iter != metadata.cend(); ++iter, ++i) {
 | 
	
		
			
				|  |  | +    metadata_array[i].key = SliceReferencingString(iter->first);
 | 
	
		
			
				|  |  | +    metadata_array[i].value = SliceReferencingString(iter->second);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  if (!optional_error_details.empty()) {
 | 
	
		
			
				|  |  | +    metadata_array[i].key =
 | 
	
		
			
				|  |  | +        g_core_codegen_interface->grpc_slice_from_static_buffer(
 | 
	
		
			
				|  |  | +            kBinaryErrorDetailsKey, sizeof(kBinaryErrorDetailsKey) - 1);
 | 
	
		
			
				|  |  | +    metadata_array[i].value = SliceReferencingString(optional_error_details);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  return metadata_array;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +}  // namespace internal
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/// Per-message write options.
 | 
	
		
			
				|  |  | +class WriteOptions {
 | 
	
		
			
				|  |  | + public:
 | 
	
		
			
				|  |  | +  WriteOptions() : flags_(0), last_message_(false) {}
 | 
	
		
			
				|  |  | +  WriteOptions(const WriteOptions& other)
 | 
	
		
			
				|  |  | +      : flags_(other.flags_), last_message_(other.last_message_) {}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /// Clear all flags.
 | 
	
		
			
				|  |  | +  inline void Clear() { flags_ = 0; }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /// Returns raw flags bitset.
 | 
	
		
			
				|  |  | +  inline uint32_t flags() const { return flags_; }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /// Sets flag for the disabling of compression for the next message write.
 | 
	
		
			
				|  |  | +  ///
 | 
	
		
			
				|  |  | +  /// \sa GRPC_WRITE_NO_COMPRESS
 | 
	
		
			
				|  |  | +  inline WriteOptions& set_no_compression() {
 | 
	
		
			
				|  |  | +    SetBit(GRPC_WRITE_NO_COMPRESS);
 | 
	
		
			
				|  |  | +    return *this;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /// Clears flag for the disabling of compression for the next message write.
 | 
	
		
			
				|  |  | +  ///
 | 
	
		
			
				|  |  | +  /// \sa GRPC_WRITE_NO_COMPRESS
 | 
	
		
			
				|  |  | +  inline WriteOptions& clear_no_compression() {
 | 
	
		
			
				|  |  | +    ClearBit(GRPC_WRITE_NO_COMPRESS);
 | 
	
		
			
				|  |  | +    return *this;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /// Get value for the flag indicating whether compression for the next
 | 
	
		
			
				|  |  | +  /// message write is forcefully disabled.
 | 
	
		
			
				|  |  | +  ///
 | 
	
		
			
				|  |  | +  /// \sa GRPC_WRITE_NO_COMPRESS
 | 
	
		
			
				|  |  | +  inline bool get_no_compression() const {
 | 
	
		
			
				|  |  | +    return GetBit(GRPC_WRITE_NO_COMPRESS);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /// Sets flag indicating that the write may be buffered and need not go out on
 | 
	
		
			
				|  |  | +  /// the wire immediately.
 | 
	
		
			
				|  |  | +  ///
 | 
	
		
			
				|  |  | +  /// \sa GRPC_WRITE_BUFFER_HINT
 | 
	
		
			
				|  |  | +  inline WriteOptions& set_buffer_hint() {
 | 
	
		
			
				|  |  | +    SetBit(GRPC_WRITE_BUFFER_HINT);
 | 
	
		
			
				|  |  | +    return *this;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /// Clears flag indicating that the write may be buffered and need not go out
 | 
	
		
			
				|  |  | +  /// on the wire immediately.
 | 
	
		
			
				|  |  | +  ///
 | 
	
		
			
				|  |  | +  /// \sa GRPC_WRITE_BUFFER_HINT
 | 
	
		
			
				|  |  | +  inline WriteOptions& clear_buffer_hint() {
 | 
	
		
			
				|  |  | +    ClearBit(GRPC_WRITE_BUFFER_HINT);
 | 
	
		
			
				|  |  | +    return *this;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /// Get value for the flag indicating that the write may be buffered and need
 | 
	
		
			
				|  |  | +  /// not go out on the wire immediately.
 | 
	
		
			
				|  |  | +  ///
 | 
	
		
			
				|  |  | +  /// \sa GRPC_WRITE_BUFFER_HINT
 | 
	
		
			
				|  |  | +  inline bool get_buffer_hint() const { return GetBit(GRPC_WRITE_BUFFER_HINT); }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /// corked bit: aliases set_buffer_hint currently, with the intent that
 | 
	
		
			
				|  |  | +  /// set_buffer_hint will be removed in the future
 | 
	
		
			
				|  |  | +  inline WriteOptions& set_corked() {
 | 
	
		
			
				|  |  | +    SetBit(GRPC_WRITE_BUFFER_HINT);
 | 
	
		
			
				|  |  | +    return *this;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  inline WriteOptions& clear_corked() {
 | 
	
		
			
				|  |  | +    ClearBit(GRPC_WRITE_BUFFER_HINT);
 | 
	
		
			
				|  |  | +    return *this;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  inline bool is_corked() const { return GetBit(GRPC_WRITE_BUFFER_HINT); }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /// last-message bit: indicates this is the last message in a stream
 | 
	
		
			
				|  |  | +  /// client-side:  makes Write the equivalent of performing Write, WritesDone
 | 
	
		
			
				|  |  | +  /// in a single step
 | 
	
		
			
				|  |  | +  /// server-side:  hold the Write until the service handler returns (sync api)
 | 
	
		
			
				|  |  | +  /// or until Finish is called (async api)
 | 
	
		
			
				|  |  | +  inline WriteOptions& set_last_message() {
 | 
	
		
			
				|  |  | +    last_message_ = true;
 | 
	
		
			
				|  |  | +    return *this;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /// Clears flag indicating that this is the last message in a stream,
 | 
	
		
			
				|  |  | +  /// disabling coalescing.
 | 
	
		
			
				|  |  | +  inline WriteOptions& clear_last_message() {
 | 
	
		
			
				|  |  | +    last_message_ = false;
 | 
	
		
			
				|  |  | +    return *this;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /// Guarantee that all bytes have been written to the socket before completing
 | 
	
		
			
				|  |  | +  /// this write (usually writes are completed when they pass flow control).
 | 
	
		
			
				|  |  | +  inline WriteOptions& set_write_through() {
 | 
	
		
			
				|  |  | +    SetBit(GRPC_WRITE_THROUGH);
 | 
	
		
			
				|  |  | +    return *this;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  inline bool is_write_through() const { return GetBit(GRPC_WRITE_THROUGH); }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /// Get value for the flag indicating that this is the last message, and
 | 
	
		
			
				|  |  | +  /// should be coalesced with trailing metadata.
 | 
	
		
			
				|  |  | +  ///
 | 
	
		
			
				|  |  | +  /// \sa GRPC_WRITE_LAST_MESSAGE
 | 
	
		
			
				|  |  | +  bool is_last_message() const { return last_message_; }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  WriteOptions& operator=(const WriteOptions& rhs) {
 | 
	
		
			
				|  |  | +    flags_ = rhs.flags_;
 | 
	
		
			
				|  |  | +    return *this;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | + private:
 | 
	
		
			
				|  |  | +  void SetBit(const uint32_t mask) { flags_ |= mask; }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void ClearBit(const uint32_t mask) { flags_ &= ~mask; }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  bool GetBit(const uint32_t mask) const { return (flags_ & mask) != 0; }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  uint32_t flags_;
 | 
	
		
			
				|  |  | +  bool last_message_;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +namespace internal {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/// Default argument for CallOpSet. I is unused by the class, but can be
 | 
	
		
			
				|  |  | +/// used for generating multiple names for the same thing.
 | 
	
		
			
				|  |  | +template <int I>
 | 
	
		
			
				|  |  | +class CallNoOp {
 | 
	
		
			
				|  |  | + protected:
 | 
	
		
			
				|  |  | +  void AddOp(grpc_op* ops, size_t* nops) {}
 | 
	
		
			
				|  |  | +  void FinishOp(bool* status) {}
 | 
	
		
			
				|  |  | +  void SetInterceptionHookPoint(
 | 
	
		
			
				|  |  | +      InternalInterceptorBatchMethods* interceptor_methods) {}
 | 
	
		
			
				|  |  | +  void SetFinishInterceptionHookPoint(
 | 
	
		
			
				|  |  | +      InternalInterceptorBatchMethods* interceptor_methods) {}
 | 
	
		
			
				|  |  | +  void SetHijackingState(InternalInterceptorBatchMethods* interceptor_methods) {
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +class CallOpSendInitialMetadata {
 | 
	
		
			
				|  |  | + public:
 | 
	
		
			
				|  |  | +  CallOpSendInitialMetadata() : send_(false) {
 | 
	
		
			
				|  |  | +    maybe_compression_level_.is_set = false;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void SendInitialMetadata(std::multimap<grpc::string, grpc::string>* metadata,
 | 
	
		
			
				|  |  | +                           uint32_t flags) {
 | 
	
		
			
				|  |  | +    maybe_compression_level_.is_set = false;
 | 
	
		
			
				|  |  | +    send_ = true;
 | 
	
		
			
				|  |  | +    flags_ = flags;
 | 
	
		
			
				|  |  | +    metadata_map_ = metadata;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void set_compression_level(grpc_compression_level level) {
 | 
	
		
			
				|  |  | +    maybe_compression_level_.is_set = true;
 | 
	
		
			
				|  |  | +    maybe_compression_level_.level = level;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | + protected:
 | 
	
		
			
				|  |  | +  void AddOp(grpc_op* ops, size_t* nops) {
 | 
	
		
			
				|  |  | +    if (!send_ || hijacked_) return;
 | 
	
		
			
				|  |  | +    grpc_op* op = &ops[(*nops)++];
 | 
	
		
			
				|  |  | +    op->op = GRPC_OP_SEND_INITIAL_METADATA;
 | 
	
		
			
				|  |  | +    op->flags = flags_;
 | 
	
		
			
				|  |  | +    op->reserved = NULL;
 | 
	
		
			
				|  |  | +    initial_metadata_ =
 | 
	
		
			
				|  |  | +        FillMetadataArray(*metadata_map_, &initial_metadata_count_, "");
 | 
	
		
			
				|  |  | +    op->data.send_initial_metadata.count = initial_metadata_count_;
 | 
	
		
			
				|  |  | +    op->data.send_initial_metadata.metadata = initial_metadata_;
 | 
	
		
			
				|  |  | +    op->data.send_initial_metadata.maybe_compression_level.is_set =
 | 
	
		
			
				|  |  | +        maybe_compression_level_.is_set;
 | 
	
		
			
				|  |  | +    if (maybe_compression_level_.is_set) {
 | 
	
		
			
				|  |  | +      op->data.send_initial_metadata.maybe_compression_level.level =
 | 
	
		
			
				|  |  | +          maybe_compression_level_.level;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  void FinishOp(bool* status) {
 | 
	
		
			
				|  |  | +    if (!send_ || hijacked_) return;
 | 
	
		
			
				|  |  | +    g_core_codegen_interface->gpr_free(initial_metadata_);
 | 
	
		
			
				|  |  | +    send_ = false;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void SetInterceptionHookPoint(
 | 
	
		
			
				|  |  | +      InternalInterceptorBatchMethods* interceptor_methods) {
 | 
	
		
			
				|  |  | +    if (!send_) return;
 | 
	
		
			
				|  |  | +    interceptor_methods->AddInterceptionHookPoint(
 | 
	
		
			
				|  |  | +        experimental::InterceptionHookPoints::PRE_SEND_INITIAL_METADATA);
 | 
	
		
			
				|  |  | +    interceptor_methods->SetSendInitialMetadata(metadata_map_);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void SetFinishInterceptionHookPoint(
 | 
	
		
			
				|  |  | +      InternalInterceptorBatchMethods* interceptor_methods) {}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void SetHijackingState(InternalInterceptorBatchMethods* interceptor_methods) {
 | 
	
		
			
				|  |  | +    hijacked_ = true;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  bool hijacked_ = false;
 | 
	
		
			
				|  |  | +  bool send_;
 | 
	
		
			
				|  |  | +  uint32_t flags_;
 | 
	
		
			
				|  |  | +  size_t initial_metadata_count_;
 | 
	
		
			
				|  |  | +  std::multimap<grpc::string, grpc::string>* metadata_map_;
 | 
	
		
			
				|  |  | +  grpc_metadata* initial_metadata_;
 | 
	
		
			
				|  |  | +  struct {
 | 
	
		
			
				|  |  | +    bool is_set;
 | 
	
		
			
				|  |  | +    grpc_compression_level level;
 | 
	
		
			
				|  |  | +  } maybe_compression_level_;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +class CallOpSendMessage {
 | 
	
		
			
				|  |  | + public:
 | 
	
		
			
				|  |  | +  CallOpSendMessage() : send_buf_() {}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /// Send \a message using \a options for the write. The \a options are cleared
 | 
	
		
			
				|  |  | +  /// after use.
 | 
	
		
			
				|  |  | +  template <class M>
 | 
	
		
			
				|  |  | +  Status SendMessage(const M& message,
 | 
	
		
			
				|  |  | +                     WriteOptions options) GRPC_MUST_USE_RESULT;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  template <class M>
 | 
	
		
			
				|  |  | +  Status SendMessage(const M& message) GRPC_MUST_USE_RESULT;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | + protected:
 | 
	
		
			
				|  |  | +  void AddOp(grpc_op* ops, size_t* nops) {
 | 
	
		
			
				|  |  | +    if (!send_buf_.Valid() || hijacked_) return;
 | 
	
		
			
				|  |  | +    grpc_op* op = &ops[(*nops)++];
 | 
	
		
			
				|  |  | +    op->op = GRPC_OP_SEND_MESSAGE;
 | 
	
		
			
				|  |  | +    op->flags = write_options_.flags();
 | 
	
		
			
				|  |  | +    op->reserved = NULL;
 | 
	
		
			
				|  |  | +    op->data.send_message.send_message = send_buf_.c_buffer();
 | 
	
		
			
				|  |  | +    // Flags are per-message: clear them after use.
 | 
	
		
			
				|  |  | +    write_options_.Clear();
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  void FinishOp(bool* status) { send_buf_.Clear(); }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void SetInterceptionHookPoint(
 | 
	
		
			
				|  |  | +      InternalInterceptorBatchMethods* interceptor_methods) {
 | 
	
		
			
				|  |  | +    if (!send_buf_.Valid()) return;
 | 
	
		
			
				|  |  | +    interceptor_methods->AddInterceptionHookPoint(
 | 
	
		
			
				|  |  | +        experimental::InterceptionHookPoints::PRE_SEND_MESSAGE);
 | 
	
		
			
				|  |  | +    interceptor_methods->SetSendMessage(&send_buf_);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void SetFinishInterceptionHookPoint(
 | 
	
		
			
				|  |  | +      InternalInterceptorBatchMethods* interceptor_methods) {}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void SetHijackingState(InternalInterceptorBatchMethods* interceptor_methods) {
 | 
	
		
			
				|  |  | +    hijacked_ = true;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | + private:
 | 
	
		
			
				|  |  | +  bool hijacked_ = false;
 | 
	
		
			
				|  |  | +  ByteBuffer send_buf_;
 | 
	
		
			
				|  |  | +  WriteOptions write_options_;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +template <class M>
 | 
	
		
			
				|  |  | +Status CallOpSendMessage::SendMessage(const M& message, WriteOptions options) {
 | 
	
		
			
				|  |  | +  write_options_ = options;
 | 
	
		
			
				|  |  | +  bool own_buf;
 | 
	
		
			
				|  |  | +  // TODO(vjpai): Remove the void below when possible
 | 
	
		
			
				|  |  | +  // The void in the template parameter below should not be needed
 | 
	
		
			
				|  |  | +  // (since it should be implicit) but is needed due to an observed
 | 
	
		
			
				|  |  | +  // difference in behavior between clang and gcc for certain internal users
 | 
	
		
			
				|  |  | +  Status result = SerializationTraits<M, void>::Serialize(
 | 
	
		
			
				|  |  | +      message, send_buf_.bbuf_ptr(), &own_buf);
 | 
	
		
			
				|  |  | +  if (!own_buf) {
 | 
	
		
			
				|  |  | +    send_buf_.Duplicate();
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  return result;
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +template <class M>
 | 
	
		
			
				|  |  | +Status CallOpSendMessage::SendMessage(const M& message) {
 | 
	
		
			
				|  |  | +  return SendMessage(message, WriteOptions());
 | 
	
		
			
				|  |  | +}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +template <class R>
 | 
	
		
			
				|  |  | +class CallOpRecvMessage {
 | 
	
		
			
				|  |  | + public:
 | 
	
		
			
				|  |  | +  CallOpRecvMessage()
 | 
	
		
			
				|  |  | +      : got_message(false),
 | 
	
		
			
				|  |  | +        message_(nullptr),
 | 
	
		
			
				|  |  | +        allow_not_getting_message_(false) {}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void RecvMessage(R* message) { message_ = message; }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // Do not change status if no message is received.
 | 
	
		
			
				|  |  | +  void AllowNoMessage() { allow_not_getting_message_ = true; }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  bool got_message;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | + protected:
 | 
	
		
			
				|  |  | +  void AddOp(grpc_op* ops, size_t* nops) {
 | 
	
		
			
				|  |  | +    if (message_ == nullptr || hijacked_) return;
 | 
	
		
			
				|  |  | +    grpc_op* op = &ops[(*nops)++];
 | 
	
		
			
				|  |  | +    op->op = GRPC_OP_RECV_MESSAGE;
 | 
	
		
			
				|  |  | +    op->flags = 0;
 | 
	
		
			
				|  |  | +    op->reserved = NULL;
 | 
	
		
			
				|  |  | +    op->data.recv_message.recv_message = recv_buf_.c_buffer_ptr();
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void FinishOp(bool* status) {
 | 
	
		
			
				|  |  | +    if (message_ == nullptr || hijacked_) return;
 | 
	
		
			
				|  |  | +    if (recv_buf_.Valid()) {
 | 
	
		
			
				|  |  | +      if (*status) {
 | 
	
		
			
				|  |  | +        got_message = *status =
 | 
	
		
			
				|  |  | +            SerializationTraits<R>::Deserialize(recv_buf_.bbuf_ptr(), message_)
 | 
	
		
			
				|  |  | +                .ok();
 | 
	
		
			
				|  |  | +        recv_buf_.Release();
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  | +        got_message = false;
 | 
	
		
			
				|  |  | +        recv_buf_.Clear();
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    } else {
 | 
	
		
			
				|  |  | +      got_message = false;
 | 
	
		
			
				|  |  | +      if (!allow_not_getting_message_) {
 | 
	
		
			
				|  |  | +        *status = false;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    message_ = nullptr;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void SetInterceptionHookPoint(
 | 
	
		
			
				|  |  | +      InternalInterceptorBatchMethods* interceptor_methods) {
 | 
	
		
			
				|  |  | +    interceptor_methods->SetRecvMessage(message_);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void SetFinishInterceptionHookPoint(
 | 
	
		
			
				|  |  | +      InternalInterceptorBatchMethods* interceptor_methods) {
 | 
	
		
			
				|  |  | +    if (!got_message) return;
 | 
	
		
			
				|  |  | +    interceptor_methods->AddInterceptionHookPoint(
 | 
	
		
			
				|  |  | +        experimental::InterceptionHookPoints::POST_RECV_MESSAGE);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  void SetHijackingState(InternalInterceptorBatchMethods* interceptor_methods) {
 | 
	
		
			
				|  |  | +    hijacked_ = true;
 | 
	
		
			
				|  |  | +    if (message_ == nullptr) return;
 | 
	
		
			
				|  |  | +    interceptor_methods->AddInterceptionHookPoint(
 | 
	
		
			
				|  |  | +        experimental::InterceptionHookPoints::PRE_RECV_MESSAGE);
 | 
	
		
			
				|  |  | +    got_message = true;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | + private:
 | 
	
		
			
				|  |  | +  R* message_;
 | 
	
		
			
				|  |  | +  ByteBuffer recv_buf_;
 | 
	
		
			
				|  |  | +  bool allow_not_getting_message_;
 | 
	
		
			
				|  |  | +  bool hijacked_ = false;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +class DeserializeFunc {
 | 
	
		
			
				|  |  | + public:
 | 
	
		
			
				|  |  | +  virtual Status Deserialize(ByteBuffer* buf) = 0;
 | 
	
		
			
				|  |  | +  virtual ~DeserializeFunc() {}
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +template <class R>
 | 
	
		
			
				|  |  | +class DeserializeFuncType final : public DeserializeFunc {
 | 
	
		
			
				|  |  | + public:
 | 
	
		
			
				|  |  | +  DeserializeFuncType(R* message) : message_(message) {}
 | 
	
		
			
				|  |  | +  Status Deserialize(ByteBuffer* buf) override {
 | 
	
		
			
				|  |  | +    return SerializationTraits<R>::Deserialize(buf->bbuf_ptr(), message_);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  ~DeserializeFuncType() override {}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | + private:
 | 
	
		
			
				|  |  | +  R* message_;  // Not a managed pointer because management is external to this
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +class CallOpGenericRecvMessage {
 | 
	
		
			
				|  |  | + public:
 | 
	
		
			
				|  |  | +  CallOpGenericRecvMessage()
 | 
	
		
			
				|  |  | +      : got_message(false), allow_not_getting_message_(false) {}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  template <class R>
 | 
	
		
			
				|  |  | +  void RecvMessage(R* message) {
 | 
	
		
			
				|  |  | +    // Use an explicit base class pointer to avoid resolution error in the
 | 
	
		
			
				|  |  | +    // following unique_ptr::reset for some old implementations.
 | 
	
		
			
				|  |  | +    DeserializeFunc* func = new DeserializeFuncType<R>(message);
 | 
	
		
			
				|  |  | +    deserialize_.reset(func);
 | 
	
		
			
				|  |  | +    message_ = message;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // Do not change status if no message is received.
 | 
	
		
			
				|  |  | +  void AllowNoMessage() { allow_not_getting_message_ = true; }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  bool got_message;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | + protected:
 | 
	
		
			
				|  |  | +  void AddOp(grpc_op* ops, size_t* nops) {
 | 
	
		
			
				|  |  | +    if (!deserialize_ || hijacked_) return;
 | 
	
		
			
				|  |  | +    grpc_op* op = &ops[(*nops)++];
 | 
	
		
			
				|  |  | +    op->op = GRPC_OP_RECV_MESSAGE;
 | 
	
		
			
				|  |  | +    op->flags = 0;
 | 
	
		
			
				|  |  | +    op->reserved = NULL;
 | 
	
		
			
				|  |  | +    op->data.recv_message.recv_message = recv_buf_.c_buffer_ptr();
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void FinishOp(bool* status) {
 | 
	
		
			
				|  |  | +    if (!deserialize_ || hijacked_) return;
 | 
	
		
			
				|  |  | +    if (recv_buf_.Valid()) {
 | 
	
		
			
				|  |  | +      if (*status) {
 | 
	
		
			
				|  |  | +        got_message = true;
 | 
	
		
			
				|  |  | +        *status = deserialize_->Deserialize(&recv_buf_).ok();
 | 
	
		
			
				|  |  | +        recv_buf_.Release();
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  | +        got_message = false;
 | 
	
		
			
				|  |  | +        recv_buf_.Clear();
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    } else {
 | 
	
		
			
				|  |  | +      got_message = false;
 | 
	
		
			
				|  |  | +      if (!allow_not_getting_message_) {
 | 
	
		
			
				|  |  | +        *status = false;
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    deserialize_.reset();
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void SetInterceptionHookPoint(
 | 
	
		
			
				|  |  | +      InternalInterceptorBatchMethods* interceptor_methods) {
 | 
	
		
			
				|  |  | +    interceptor_methods->SetRecvMessage(message_);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void SetFinishInterceptionHookPoint(
 | 
	
		
			
				|  |  | +      InternalInterceptorBatchMethods* interceptor_methods) {
 | 
	
		
			
				|  |  | +    if (!got_message) return;
 | 
	
		
			
				|  |  | +    interceptor_methods->AddInterceptionHookPoint(
 | 
	
		
			
				|  |  | +        experimental::InterceptionHookPoints::POST_RECV_MESSAGE);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  void SetHijackingState(InternalInterceptorBatchMethods* interceptor_methods) {
 | 
	
		
			
				|  |  | +    hijacked_ = true;
 | 
	
		
			
				|  |  | +    if (!deserialize_) return;
 | 
	
		
			
				|  |  | +    interceptor_methods->AddInterceptionHookPoint(
 | 
	
		
			
				|  |  | +        experimental::InterceptionHookPoints::PRE_RECV_MESSAGE);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | + private:
 | 
	
		
			
				|  |  | +  void* message_;
 | 
	
		
			
				|  |  | +  bool hijacked_ = false;
 | 
	
		
			
				|  |  | +  std::unique_ptr<DeserializeFunc> deserialize_;
 | 
	
		
			
				|  |  | +  ByteBuffer recv_buf_;
 | 
	
		
			
				|  |  | +  bool allow_not_getting_message_;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +class CallOpClientSendClose {
 | 
	
		
			
				|  |  | + public:
 | 
	
		
			
				|  |  | +  CallOpClientSendClose() : send_(false) {}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void ClientSendClose() { send_ = true; }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | + protected:
 | 
	
		
			
				|  |  | +  void AddOp(grpc_op* ops, size_t* nops) {
 | 
	
		
			
				|  |  | +    if (!send_ || hijacked_) return;
 | 
	
		
			
				|  |  | +    grpc_op* op = &ops[(*nops)++];
 | 
	
		
			
				|  |  | +    op->op = GRPC_OP_SEND_CLOSE_FROM_CLIENT;
 | 
	
		
			
				|  |  | +    op->flags = 0;
 | 
	
		
			
				|  |  | +    op->reserved = NULL;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  void FinishOp(bool* status) { send_ = false; }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void SetInterceptionHookPoint(
 | 
	
		
			
				|  |  | +      InternalInterceptorBatchMethods* interceptor_methods) {
 | 
	
		
			
				|  |  | +    if (!send_) return;
 | 
	
		
			
				|  |  | +    interceptor_methods->AddInterceptionHookPoint(
 | 
	
		
			
				|  |  | +        experimental::InterceptionHookPoints::PRE_SEND_CLOSE);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void SetFinishInterceptionHookPoint(
 | 
	
		
			
				|  |  | +      InternalInterceptorBatchMethods* interceptor_methods) {}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void SetHijackingState(InternalInterceptorBatchMethods* interceptor_methods) {
 | 
	
		
			
				|  |  | +    hijacked_ = true;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | + private:
 | 
	
		
			
				|  |  | +  bool hijacked_ = false;
 | 
	
		
			
				|  |  | +  bool send_;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +class CallOpServerSendStatus {
 | 
	
		
			
				|  |  | + public:
 | 
	
		
			
				|  |  | +  CallOpServerSendStatus() : send_status_available_(false) {}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void ServerSendStatus(
 | 
	
		
			
				|  |  | +      std::multimap<grpc::string, grpc::string>* trailing_metadata,
 | 
	
		
			
				|  |  | +      const Status& status) {
 | 
	
		
			
				|  |  | +    send_error_details_ = status.error_details();
 | 
	
		
			
				|  |  | +    metadata_map_ = trailing_metadata;
 | 
	
		
			
				|  |  | +    send_status_available_ = true;
 | 
	
		
			
				|  |  | +    send_status_code_ = static_cast<grpc_status_code>(status.error_code());
 | 
	
		
			
				|  |  | +    send_error_message_ = status.error_message();
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | + protected:
 | 
	
		
			
				|  |  | +  void AddOp(grpc_op* ops, size_t* nops) {
 | 
	
		
			
				|  |  | +    if (!send_status_available_ || hijacked_) return;
 | 
	
		
			
				|  |  | +    trailing_metadata_ = FillMetadataArray(
 | 
	
		
			
				|  |  | +        *metadata_map_, &trailing_metadata_count_, send_error_details_);
 | 
	
		
			
				|  |  | +    grpc_op* op = &ops[(*nops)++];
 | 
	
		
			
				|  |  | +    op->op = GRPC_OP_SEND_STATUS_FROM_SERVER;
 | 
	
		
			
				|  |  | +    op->data.send_status_from_server.trailing_metadata_count =
 | 
	
		
			
				|  |  | +        trailing_metadata_count_;
 | 
	
		
			
				|  |  | +    op->data.send_status_from_server.trailing_metadata = trailing_metadata_;
 | 
	
		
			
				|  |  | +    op->data.send_status_from_server.status = send_status_code_;
 | 
	
		
			
				|  |  | +    error_message_slice_ = SliceReferencingString(send_error_message_);
 | 
	
		
			
				|  |  | +    op->data.send_status_from_server.status_details =
 | 
	
		
			
				|  |  | +        send_error_message_.empty() ? nullptr : &error_message_slice_;
 | 
	
		
			
				|  |  | +    op->flags = 0;
 | 
	
		
			
				|  |  | +    op->reserved = NULL;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void FinishOp(bool* status) {
 | 
	
		
			
				|  |  | +    if (!send_status_available_ || hijacked_) return;
 | 
	
		
			
				|  |  | +    g_core_codegen_interface->gpr_free(trailing_metadata_);
 | 
	
		
			
				|  |  | +    send_status_available_ = false;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void SetInterceptionHookPoint(
 | 
	
		
			
				|  |  | +      InternalInterceptorBatchMethods* interceptor_methods) {
 | 
	
		
			
				|  |  | +    if (!send_status_available_) return;
 | 
	
		
			
				|  |  | +    interceptor_methods->AddInterceptionHookPoint(
 | 
	
		
			
				|  |  | +        experimental::InterceptionHookPoints::PRE_SEND_STATUS);
 | 
	
		
			
				|  |  | +    interceptor_methods->SetSendTrailingMetadata(metadata_map_);
 | 
	
		
			
				|  |  | +    interceptor_methods->SetSendStatus(&send_status_code_, &send_error_details_,
 | 
	
		
			
				|  |  | +                                       &send_error_message_);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void SetFinishInterceptionHookPoint(
 | 
	
		
			
				|  |  | +      InternalInterceptorBatchMethods* interceptor_methods) {}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void SetHijackingState(InternalInterceptorBatchMethods* interceptor_methods) {
 | 
	
		
			
				|  |  | +    hijacked_ = true;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | + private:
 | 
	
		
			
				|  |  | +  bool hijacked_ = false;
 | 
	
		
			
				|  |  | +  bool send_status_available_;
 | 
	
		
			
				|  |  | +  grpc_status_code send_status_code_;
 | 
	
		
			
				|  |  | +  grpc::string send_error_details_;
 | 
	
		
			
				|  |  | +  grpc::string send_error_message_;
 | 
	
		
			
				|  |  | +  size_t trailing_metadata_count_;
 | 
	
		
			
				|  |  | +  std::multimap<grpc::string, grpc::string>* metadata_map_;
 | 
	
		
			
				|  |  | +  grpc_metadata* trailing_metadata_;
 | 
	
		
			
				|  |  | +  grpc_slice error_message_slice_;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +class CallOpRecvInitialMetadata {
 | 
	
		
			
				|  |  | + public:
 | 
	
		
			
				|  |  | +  CallOpRecvInitialMetadata() : metadata_map_(nullptr) {}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void RecvInitialMetadata(ClientContext* context) {
 | 
	
		
			
				|  |  | +    context->initial_metadata_received_ = true;
 | 
	
		
			
				|  |  | +    metadata_map_ = &context->recv_initial_metadata_;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | + protected:
 | 
	
		
			
				|  |  | +  void AddOp(grpc_op* ops, size_t* nops) {
 | 
	
		
			
				|  |  | +    if (metadata_map_ == nullptr || hijacked_) return;
 | 
	
		
			
				|  |  | +    grpc_op* op = &ops[(*nops)++];
 | 
	
		
			
				|  |  | +    op->op = GRPC_OP_RECV_INITIAL_METADATA;
 | 
	
		
			
				|  |  | +    op->data.recv_initial_metadata.recv_initial_metadata = metadata_map_->arr();
 | 
	
		
			
				|  |  | +    op->flags = 0;
 | 
	
		
			
				|  |  | +    op->reserved = NULL;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void FinishOp(bool* status) {
 | 
	
		
			
				|  |  | +    if (metadata_map_ == nullptr || hijacked_) return;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void SetInterceptionHookPoint(
 | 
	
		
			
				|  |  | +      InternalInterceptorBatchMethods* interceptor_methods) {
 | 
	
		
			
				|  |  | +    interceptor_methods->SetRecvInitialMetadata(metadata_map_);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void SetFinishInterceptionHookPoint(
 | 
	
		
			
				|  |  | +      InternalInterceptorBatchMethods* interceptor_methods) {
 | 
	
		
			
				|  |  | +    if (metadata_map_ == nullptr) return;
 | 
	
		
			
				|  |  | +    interceptor_methods->AddInterceptionHookPoint(
 | 
	
		
			
				|  |  | +        experimental::InterceptionHookPoints::POST_RECV_INITIAL_METADATA);
 | 
	
		
			
				|  |  | +    metadata_map_ = nullptr;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void SetHijackingState(InternalInterceptorBatchMethods* interceptor_methods) {
 | 
	
		
			
				|  |  | +    hijacked_ = true;
 | 
	
		
			
				|  |  | +    if (metadata_map_ == nullptr) return;
 | 
	
		
			
				|  |  | +    interceptor_methods->AddInterceptionHookPoint(
 | 
	
		
			
				|  |  | +        experimental::InterceptionHookPoints::PRE_RECV_INITIAL_METADATA);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | + private:
 | 
	
		
			
				|  |  | +  bool hijacked_ = false;
 | 
	
		
			
				|  |  | +  MetadataMap* metadata_map_;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +class CallOpClientRecvStatus {
 | 
	
		
			
				|  |  | + public:
 | 
	
		
			
				|  |  | +  CallOpClientRecvStatus()
 | 
	
		
			
				|  |  | +      : recv_status_(nullptr), debug_error_string_(nullptr) {}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void ClientRecvStatus(ClientContext* context, Status* status) {
 | 
	
		
			
				|  |  | +    client_context_ = context;
 | 
	
		
			
				|  |  | +    metadata_map_ = &client_context_->trailing_metadata_;
 | 
	
		
			
				|  |  | +    recv_status_ = status;
 | 
	
		
			
				|  |  | +    error_message_ = g_core_codegen_interface->grpc_empty_slice();
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | + protected:
 | 
	
		
			
				|  |  | +  void AddOp(grpc_op* ops, size_t* nops) {
 | 
	
		
			
				|  |  | +    if (recv_status_ == nullptr || hijacked_) return;
 | 
	
		
			
				|  |  | +    grpc_op* op = &ops[(*nops)++];
 | 
	
		
			
				|  |  | +    op->op = GRPC_OP_RECV_STATUS_ON_CLIENT;
 | 
	
		
			
				|  |  | +    op->data.recv_status_on_client.trailing_metadata = metadata_map_->arr();
 | 
	
		
			
				|  |  | +    op->data.recv_status_on_client.status = &status_code_;
 | 
	
		
			
				|  |  | +    op->data.recv_status_on_client.status_details = &error_message_;
 | 
	
		
			
				|  |  | +    op->data.recv_status_on_client.error_string = &debug_error_string_;
 | 
	
		
			
				|  |  | +    op->flags = 0;
 | 
	
		
			
				|  |  | +    op->reserved = NULL;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void FinishOp(bool* status) {
 | 
	
		
			
				|  |  | +    if (recv_status_ == nullptr || hijacked_) return;
 | 
	
		
			
				|  |  | +    grpc::string binary_error_details = metadata_map_->GetBinaryErrorDetails();
 | 
	
		
			
				|  |  | +    *recv_status_ =
 | 
	
		
			
				|  |  | +        Status(static_cast<StatusCode>(status_code_),
 | 
	
		
			
				|  |  | +               GRPC_SLICE_IS_EMPTY(error_message_)
 | 
	
		
			
				|  |  | +                   ? grpc::string()
 | 
	
		
			
				|  |  | +                   : grpc::string(GRPC_SLICE_START_PTR(error_message_),
 | 
	
		
			
				|  |  | +                                  GRPC_SLICE_END_PTR(error_message_)),
 | 
	
		
			
				|  |  | +               binary_error_details);
 | 
	
		
			
				|  |  | +    client_context_->set_debug_error_string(
 | 
	
		
			
				|  |  | +        debug_error_string_ != nullptr ? debug_error_string_ : "");
 | 
	
		
			
				|  |  | +    g_core_codegen_interface->grpc_slice_unref(error_message_);
 | 
	
		
			
				|  |  | +    if (debug_error_string_ != nullptr) {
 | 
	
		
			
				|  |  | +      g_core_codegen_interface->gpr_free((void*)debug_error_string_);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void SetInterceptionHookPoint(
 | 
	
		
			
				|  |  | +      InternalInterceptorBatchMethods* interceptor_methods) {
 | 
	
		
			
				|  |  | +    interceptor_methods->SetRecvStatus(recv_status_);
 | 
	
		
			
				|  |  | +    interceptor_methods->SetRecvTrailingMetadata(metadata_map_);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void SetFinishInterceptionHookPoint(
 | 
	
		
			
				|  |  | +      InternalInterceptorBatchMethods* interceptor_methods) {
 | 
	
		
			
				|  |  | +    if (recv_status_ == nullptr) return;
 | 
	
		
			
				|  |  | +    interceptor_methods->AddInterceptionHookPoint(
 | 
	
		
			
				|  |  | +        experimental::InterceptionHookPoints::POST_RECV_STATUS);
 | 
	
		
			
				|  |  | +    recv_status_ = nullptr;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void SetHijackingState(InternalInterceptorBatchMethods* interceptor_methods) {
 | 
	
		
			
				|  |  | +    hijacked_ = true;
 | 
	
		
			
				|  |  | +    if (recv_status_ == nullptr) return;
 | 
	
		
			
				|  |  | +    interceptor_methods->AddInterceptionHookPoint(
 | 
	
		
			
				|  |  | +        experimental::InterceptionHookPoints::PRE_RECV_STATUS);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | + private:
 | 
	
		
			
				|  |  | +  bool hijacked_ = false;
 | 
	
		
			
				|  |  | +  ClientContext* client_context_;
 | 
	
		
			
				|  |  | +  MetadataMap* metadata_map_;
 | 
	
		
			
				|  |  | +  Status* recv_status_;
 | 
	
		
			
				|  |  | +  const char* debug_error_string_;
 | 
	
		
			
				|  |  | +  grpc_status_code status_code_;
 | 
	
		
			
				|  |  | +  grpc_slice error_message_;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +template <class Op1 = CallNoOp<1>, class Op2 = CallNoOp<2>,
 | 
	
		
			
				|  |  | +          class Op3 = CallNoOp<3>, class Op4 = CallNoOp<4>,
 | 
	
		
			
				|  |  | +          class Op5 = CallNoOp<5>, class Op6 = CallNoOp<6>>
 | 
	
		
			
				|  |  | +class CallOpSet;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/// Primary implementation of CallOpSetInterface.
 | 
	
		
			
				|  |  | +/// Since we cannot use variadic templates, we declare slots up to
 | 
	
		
			
				|  |  | +/// the maximum count of ops we'll need in a set. We leverage the
 | 
	
		
			
				|  |  | +/// empty base class optimization to slim this class (especially
 | 
	
		
			
				|  |  | +/// when there are many unused slots used). To avoid duplicate base classes,
 | 
	
		
			
				|  |  | +/// the template parmeter for CallNoOp is varied by argument position.
 | 
	
		
			
				|  |  | +template <class Op1, class Op2, class Op3, class Op4, class Op5, class Op6>
 | 
	
		
			
				|  |  | +class CallOpSet : public CallOpSetInterface,
 | 
	
		
			
				|  |  | +                  public Op1,
 | 
	
		
			
				|  |  | +                  public Op2,
 | 
	
		
			
				|  |  | +                  public Op3,
 | 
	
		
			
				|  |  | +                  public Op4,
 | 
	
		
			
				|  |  | +                  public Op5,
 | 
	
		
			
				|  |  | +                  public Op6 {
 | 
	
		
			
				|  |  | + public:
 | 
	
		
			
				|  |  | +  CallOpSet() : cq_tag_(this), return_tag_(this) {}
 | 
	
		
			
				|  |  | +  // The copy constructor and assignment operator reset the value of
 | 
	
		
			
				|  |  | +  // cq_tag_, return_tag_, done_intercepting_ and interceptor_methods_ since
 | 
	
		
			
				|  |  | +  // those are only meaningful on a specific object, not across objects.
 | 
	
		
			
				|  |  | +  CallOpSet(const CallOpSet& other)
 | 
	
		
			
				|  |  | +      : cq_tag_(this),
 | 
	
		
			
				|  |  | +        return_tag_(this),
 | 
	
		
			
				|  |  | +        call_(other.call_),
 | 
	
		
			
				|  |  | +        done_intercepting_(false),
 | 
	
		
			
				|  |  | +        interceptor_methods_(InterceptorBatchMethodsImpl()) {}
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  CallOpSet& operator=(const CallOpSet& other) {
 | 
	
		
			
				|  |  | +    cq_tag_ = this;
 | 
	
		
			
				|  |  | +    return_tag_ = this;
 | 
	
		
			
				|  |  | +    call_ = other.call_;
 | 
	
		
			
				|  |  | +    done_intercepting_ = false;
 | 
	
		
			
				|  |  | +    interceptor_methods_ = InterceptorBatchMethodsImpl();
 | 
	
		
			
				|  |  | +    return *this;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void FillOps(Call* call) override {
 | 
	
		
			
				|  |  | +    done_intercepting_ = false;
 | 
	
		
			
				|  |  | +    g_core_codegen_interface->grpc_call_ref(call->call());
 | 
	
		
			
				|  |  | +    call_ =
 | 
	
		
			
				|  |  | +        *call;  // It's fine to create a copy of call since it's just pointers
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    if (RunInterceptors()) {
 | 
	
		
			
				|  |  | +      ContinueFillOpsAfterInterception();
 | 
	
		
			
				|  |  | +    } else {
 | 
	
		
			
				|  |  | +      // After the interceptors are run, ContinueFillOpsAfterInterception will
 | 
	
		
			
				|  |  | +      // be run
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  bool FinalizeResult(void** tag, bool* status) override {
 | 
	
		
			
				|  |  | +    if (done_intercepting_) {
 | 
	
		
			
				|  |  | +      // We have already finished intercepting and filling in the results. This
 | 
	
		
			
				|  |  | +      // round trip from the core needed to be made because interceptors were
 | 
	
		
			
				|  |  | +      // run
 | 
	
		
			
				|  |  | +      *tag = return_tag_;
 | 
	
		
			
				|  |  | +      *status = saved_status_;
 | 
	
		
			
				|  |  | +      g_core_codegen_interface->grpc_call_unref(call_.call());
 | 
	
		
			
				|  |  | +      return true;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    this->Op1::FinishOp(status);
 | 
	
		
			
				|  |  | +    this->Op2::FinishOp(status);
 | 
	
		
			
				|  |  | +    this->Op3::FinishOp(status);
 | 
	
		
			
				|  |  | +    this->Op4::FinishOp(status);
 | 
	
		
			
				|  |  | +    this->Op5::FinishOp(status);
 | 
	
		
			
				|  |  | +    this->Op6::FinishOp(status);
 | 
	
		
			
				|  |  | +    saved_status_ = *status;
 | 
	
		
			
				|  |  | +    if (RunInterceptorsPostRecv()) {
 | 
	
		
			
				|  |  | +      *tag = return_tag_;
 | 
	
		
			
				|  |  | +      g_core_codegen_interface->grpc_call_unref(call_.call());
 | 
	
		
			
				|  |  | +      return true;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +    // Interceptors are going to be run, so we can't return the tag just yet.
 | 
	
		
			
				|  |  | +    // After the interceptors are run, ContinueFinalizeResultAfterInterception
 | 
	
		
			
				|  |  | +    return false;
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void set_output_tag(void* return_tag) { return_tag_ = return_tag; }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void* cq_tag() override { return cq_tag_; }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  /// set_cq_tag is used to provide a different core CQ tag than "this".
 | 
	
		
			
				|  |  | +  /// This is used for callback-based tags, where the core tag is the core
 | 
	
		
			
				|  |  | +  /// callback function. It does not change the use or behavior of any other
 | 
	
		
			
				|  |  | +  /// function (such as FinalizeResult)
 | 
	
		
			
				|  |  | +  void set_cq_tag(void* cq_tag) { cq_tag_ = cq_tag; }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // This will be called while interceptors are run if the RPC is a hijacked
 | 
	
		
			
				|  |  | +  // RPC. This should set hijacking state for each of the ops.
 | 
	
		
			
				|  |  | +  void SetHijackingState() override {
 | 
	
		
			
				|  |  | +    this->Op1::SetHijackingState(&interceptor_methods_);
 | 
	
		
			
				|  |  | +    this->Op2::SetHijackingState(&interceptor_methods_);
 | 
	
		
			
				|  |  | +    this->Op3::SetHijackingState(&interceptor_methods_);
 | 
	
		
			
				|  |  | +    this->Op4::SetHijackingState(&interceptor_methods_);
 | 
	
		
			
				|  |  | +    this->Op5::SetHijackingState(&interceptor_methods_);
 | 
	
		
			
				|  |  | +    this->Op6::SetHijackingState(&interceptor_methods_);
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // Should be called after interceptors are done running
 | 
	
		
			
				|  |  | +  void ContinueFillOpsAfterInterception() override {
 | 
	
		
			
				|  |  | +    static const size_t MAX_OPS = 6;
 | 
	
		
			
				|  |  | +    grpc_op ops[MAX_OPS];
 | 
	
		
			
				|  |  | +    size_t nops = 0;
 | 
	
		
			
				|  |  | +    this->Op1::AddOp(ops, &nops);
 | 
	
		
			
				|  |  | +    this->Op2::AddOp(ops, &nops);
 | 
	
		
			
				|  |  | +    this->Op3::AddOp(ops, &nops);
 | 
	
		
			
				|  |  | +    this->Op4::AddOp(ops, &nops);
 | 
	
		
			
				|  |  | +    this->Op5::AddOp(ops, &nops);
 | 
	
		
			
				|  |  | +    this->Op6::AddOp(ops, &nops);
 | 
	
		
			
				|  |  | +    GPR_CODEGEN_ASSERT(GRPC_CALL_OK ==
 | 
	
		
			
				|  |  | +                       g_core_codegen_interface->grpc_call_start_batch(
 | 
	
		
			
				|  |  | +                           call_.call(), ops, nops, cq_tag(), nullptr));
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  // Should be called after interceptors are done running on the finalize result
 | 
	
		
			
				|  |  | +  // path
 | 
	
		
			
				|  |  | +  void ContinueFinalizeResultAfterInterception() override {
 | 
	
		
			
				|  |  | +    done_intercepting_ = true;
 | 
	
		
			
				|  |  | +    GPR_CODEGEN_ASSERT(GRPC_CALL_OK ==
 | 
	
		
			
				|  |  | +                       g_core_codegen_interface->grpc_call_start_batch(
 | 
	
		
			
				|  |  | +                           call_.call(), nullptr, 0, cq_tag(), nullptr));
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | + private:
 | 
	
		
			
				|  |  | +  // Returns true if no interceptors need to be run
 | 
	
		
			
				|  |  | +  bool RunInterceptors() {
 | 
	
		
			
				|  |  | +    interceptor_methods_.ClearState();
 | 
	
		
			
				|  |  | +    interceptor_methods_.SetCallOpSetInterface(this);
 | 
	
		
			
				|  |  | +    interceptor_methods_.SetCall(&call_);
 | 
	
		
			
				|  |  | +    this->Op1::SetInterceptionHookPoint(&interceptor_methods_);
 | 
	
		
			
				|  |  | +    this->Op2::SetInterceptionHookPoint(&interceptor_methods_);
 | 
	
		
			
				|  |  | +    this->Op3::SetInterceptionHookPoint(&interceptor_methods_);
 | 
	
		
			
				|  |  | +    this->Op4::SetInterceptionHookPoint(&interceptor_methods_);
 | 
	
		
			
				|  |  | +    this->Op5::SetInterceptionHookPoint(&interceptor_methods_);
 | 
	
		
			
				|  |  | +    this->Op6::SetInterceptionHookPoint(&interceptor_methods_);
 | 
	
		
			
				|  |  | +    return interceptor_methods_.RunInterceptors();
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +  // Returns true if no interceptors need to be run
 | 
	
		
			
				|  |  | +  bool RunInterceptorsPostRecv() {
 | 
	
		
			
				|  |  | +    // Call and OpSet had already been set on the set state.
 | 
	
		
			
				|  |  | +    // SetReverse also clears previously set hook points
 | 
	
		
			
				|  |  | +    interceptor_methods_.SetReverse();
 | 
	
		
			
				|  |  | +    this->Op1::SetFinishInterceptionHookPoint(&interceptor_methods_);
 | 
	
		
			
				|  |  | +    this->Op2::SetFinishInterceptionHookPoint(&interceptor_methods_);
 | 
	
		
			
				|  |  | +    this->Op3::SetFinishInterceptionHookPoint(&interceptor_methods_);
 | 
	
		
			
				|  |  | +    this->Op4::SetFinishInterceptionHookPoint(&interceptor_methods_);
 | 
	
		
			
				|  |  | +    this->Op5::SetFinishInterceptionHookPoint(&interceptor_methods_);
 | 
	
		
			
				|  |  | +    this->Op6::SetFinishInterceptionHookPoint(&interceptor_methods_);
 | 
	
		
			
				|  |  | +    return interceptor_methods_.RunInterceptors();
 | 
	
		
			
				|  |  | +  }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +  void* cq_tag_;
 | 
	
		
			
				|  |  | +  void* return_tag_;
 | 
	
		
			
				|  |  | +  Call call_;
 | 
	
		
			
				|  |  | +  bool done_intercepting_ = false;
 | 
	
		
			
				|  |  | +  InterceptorBatchMethodsImpl interceptor_methods_;
 | 
	
		
			
				|  |  | +  bool saved_status_;
 | 
	
		
			
				|  |  | +};
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +}  // namespace internal
 | 
	
		
			
				|  |  | +}  // namespace grpc
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +#endif  // GRPCPP_IMPL_CODEGEN_CALL_OP_SET_H
 |