| /* | |
| * | |
| * 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_INTERCEPTOR_COMMON_H | |
| #define GRPCPP_IMPL_CODEGEN_INTERCEPTOR_COMMON_H | |
| #include <array> | |
| #include <functional> | |
| #include <grpcpp/impl/codegen/call.h> | |
| #include <grpcpp/impl/codegen/call_op_set_interface.h> | |
| #include <grpcpp/impl/codegen/client_interceptor.h> | |
| #include <grpcpp/impl/codegen/intercepted_channel.h> | |
| #include <grpcpp/impl/codegen/server_interceptor.h> | |
| #include <grpc/impl/codegen/grpc_types.h> | |
| namespace grpc { | |
| namespace internal { | |
| class InterceptorBatchMethodsImpl | |
| : public experimental::InterceptorBatchMethods { | |
| public: | |
| InterceptorBatchMethodsImpl() { | |
| for (auto i = static_cast<experimental::InterceptionHookPoints>(0); | |
| i < experimental::InterceptionHookPoints::NUM_INTERCEPTION_HOOKS; | |
| i = static_cast<experimental::InterceptionHookPoints>( | |
| static_cast<size_t>(i) + 1)) { | |
| hooks_[static_cast<size_t>(i)] = false; | |
| } | |
| } | |
| ~InterceptorBatchMethodsImpl() {} | |
| bool QueryInterceptionHookPoint( | |
| experimental::InterceptionHookPoints type) override { | |
| return hooks_[static_cast<size_t>(type)]; | |
| } | |
| void Proceed() override { | |
| if (call_->client_rpc_info() != nullptr) { | |
| return ProceedClient(); | |
| } | |
| GPR_CODEGEN_ASSERT(call_->server_rpc_info() != nullptr); | |
| ProceedServer(); | |
| } | |
| void Hijack() override { | |
| // Only the client can hijack when sending down initial metadata | |
| GPR_CODEGEN_ASSERT(!reverse_ && ops_ != nullptr && | |
| call_->client_rpc_info() != nullptr); | |
| // It is illegal to call Hijack twice | |
| GPR_CODEGEN_ASSERT(!ran_hijacking_interceptor_); | |
| auto* rpc_info = call_->client_rpc_info(); | |
| rpc_info->hijacked_ = true; | |
| rpc_info->hijacked_interceptor_ = current_interceptor_index_; | |
| ClearHookPoints(); | |
| ops_->SetHijackingState(); | |
| ran_hijacking_interceptor_ = true; | |
| rpc_info->RunInterceptor(this, current_interceptor_index_); | |
| } | |
| void AddInterceptionHookPoint(experimental::InterceptionHookPoints type) { | |
| hooks_[static_cast<size_t>(type)] = true; | |
| } | |
| ByteBuffer* GetSendMessage() override { return send_message_; } | |
| std::multimap<grpc::string, grpc::string>* GetSendInitialMetadata() override { | |
| return send_initial_metadata_; | |
| } | |
| Status GetSendStatus() override { | |
| return Status(static_cast<StatusCode>(*code_), *error_message_, | |
| *error_details_); | |
| } | |
| void ModifySendStatus(const Status& status) override { | |
| *code_ = static_cast<grpc_status_code>(status.error_code()); | |
| *error_details_ = status.error_details(); | |
| *error_message_ = status.error_message(); | |
| } | |
| std::multimap<grpc::string, grpc::string>* GetSendTrailingMetadata() | |
| override { | |
| return send_trailing_metadata_; | |
| } | |
| void* GetRecvMessage() override { return recv_message_; } | |
| std::multimap<grpc::string_ref, grpc::string_ref>* GetRecvInitialMetadata() | |
| override { | |
| return recv_initial_metadata_->map(); | |
| } | |
| Status* GetRecvStatus() override { return recv_status_; } | |
| std::multimap<grpc::string_ref, grpc::string_ref>* GetRecvTrailingMetadata() | |
| override { | |
| return recv_trailing_metadata_->map(); | |
| } | |
| void SetSendMessage(ByteBuffer* buf) { send_message_ = buf; } | |
| void SetSendInitialMetadata( | |
| std::multimap<grpc::string, grpc::string>* metadata) { | |
| send_initial_metadata_ = metadata; | |
| } | |
| void SetSendStatus(grpc_status_code* code, grpc::string* error_details, | |
| grpc::string* error_message) { | |
| code_ = code; | |
| error_details_ = error_details; | |
| error_message_ = error_message; | |
| } | |
| void SetSendTrailingMetadata( | |
| std::multimap<grpc::string, grpc::string>* metadata) { | |
| send_trailing_metadata_ = metadata; | |
| } | |
| void SetRecvMessage(void* message) { recv_message_ = message; } | |
| void SetRecvInitialMetadata(MetadataMap* map) { | |
| recv_initial_metadata_ = map; | |
| } | |
| void SetRecvStatus(Status* status) { recv_status_ = status; } | |
| void SetRecvTrailingMetadata(MetadataMap* map) { | |
| recv_trailing_metadata_ = map; | |
| } | |
| std::unique_ptr<ChannelInterface> GetInterceptedChannel() override { | |
| auto* info = call_->client_rpc_info(); | |
| if (info == nullptr) { | |
| return std::unique_ptr<ChannelInterface>(nullptr); | |
| } | |
| // The intercepted channel starts from the interceptor just after the | |
| // current interceptor | |
| return std::unique_ptr<ChannelInterface>(new InterceptedChannel( | |
| info->channel(), current_interceptor_index_ + 1)); | |
| } | |
| // Clears all state | |
| void ClearState() { | |
| reverse_ = false; | |
| ran_hijacking_interceptor_ = false; | |
| ClearHookPoints(); | |
| } | |
| // Prepares for Post_recv operations | |
| void SetReverse() { | |
| reverse_ = true; | |
| ran_hijacking_interceptor_ = false; | |
| ClearHookPoints(); | |
| } | |
| // This needs to be set before interceptors are run | |
| void SetCall(Call* call) { call_ = call; } | |
| // This needs to be set before interceptors are run using RunInterceptors(). | |
| // Alternatively, RunInterceptors(std::function<void(void)> f) can be used. | |
| void SetCallOpSetInterface(CallOpSetInterface* ops) { ops_ = ops; } | |
| // Returns true if no interceptors are run. This should be used only by | |
| // subclasses of CallOpSetInterface. SetCall and SetCallOpSetInterface should | |
| // have been called before this. After all the interceptors are done running, | |
| // either ContinueFillOpsAfterInterception or | |
| // ContinueFinalizeOpsAfterInterception will be called. Note that neither of | |
| // them is invoked if there were no interceptors registered. | |
| bool RunInterceptors() { | |
| GPR_CODEGEN_ASSERT(ops_); | |
| auto* client_rpc_info = call_->client_rpc_info(); | |
| if (client_rpc_info != nullptr) { | |
| if (client_rpc_info->interceptors_.size() == 0) { | |
| return true; | |
| } else { | |
| RunClientInterceptors(); | |
| return false; | |
| } | |
| } | |
| auto* server_rpc_info = call_->server_rpc_info(); | |
| if (server_rpc_info == nullptr || | |
| server_rpc_info->interceptors_.size() == 0) { | |
| return true; | |
| } | |
| RunServerInterceptors(); | |
| return false; | |
| } | |
| // Returns true if no interceptors are run. Returns false otherwise if there | |
| // are interceptors registered. After the interceptors are done running \a f | |
| // will be invoked. This is to be used only by BaseAsyncRequest and | |
| // SyncRequest. | |
| bool RunInterceptors(std::function<void(void)> f) { | |
| // This is used only by the server for initial call request | |
| GPR_CODEGEN_ASSERT(reverse_ == true); | |
| GPR_CODEGEN_ASSERT(call_->client_rpc_info() == nullptr); | |
| auto* server_rpc_info = call_->server_rpc_info(); | |
| if (server_rpc_info == nullptr || | |
| server_rpc_info->interceptors_.size() == 0) { | |
| return true; | |
| } | |
| callback_ = std::move(f); | |
| RunServerInterceptors(); | |
| return false; | |
| } | |
| private: | |
| void RunClientInterceptors() { | |
| auto* rpc_info = call_->client_rpc_info(); | |
| if (!reverse_) { | |
| current_interceptor_index_ = 0; | |
| } else { | |
| if (rpc_info->hijacked_) { | |
| current_interceptor_index_ = rpc_info->hijacked_interceptor_; | |
| } else { | |
| current_interceptor_index_ = rpc_info->interceptors_.size() - 1; | |
| } | |
| } | |
| rpc_info->RunInterceptor(this, current_interceptor_index_); | |
| } | |
| void RunServerInterceptors() { | |
| auto* rpc_info = call_->server_rpc_info(); | |
| if (!reverse_) { | |
| current_interceptor_index_ = 0; | |
| } else { | |
| current_interceptor_index_ = rpc_info->interceptors_.size() - 1; | |
| } | |
| rpc_info->RunInterceptor(this, current_interceptor_index_); | |
| } | |
| void ProceedClient() { | |
| auto* rpc_info = call_->client_rpc_info(); | |
| if (rpc_info->hijacked_ && !reverse_ && | |
| current_interceptor_index_ == rpc_info->hijacked_interceptor_ && | |
| !ran_hijacking_interceptor_) { | |
| // We now need to provide hijacked recv ops to this interceptor | |
| ClearHookPoints(); | |
| ops_->SetHijackingState(); | |
| ran_hijacking_interceptor_ = true; | |
| rpc_info->RunInterceptor(this, current_interceptor_index_); | |
| return; | |
| } | |
| if (!reverse_) { | |
| current_interceptor_index_++; | |
| // We are going down the stack of interceptors | |
| if (current_interceptor_index_ < rpc_info->interceptors_.size()) { | |
| if (rpc_info->hijacked_ && | |
| current_interceptor_index_ > rpc_info->hijacked_interceptor_) { | |
| // This is a hijacked RPC and we are done with hijacking | |
| ops_->ContinueFillOpsAfterInterception(); | |
| } else { | |
| rpc_info->RunInterceptor(this, current_interceptor_index_); | |
| } | |
| } else { | |
| // we are done running all the interceptors without any hijacking | |
| ops_->ContinueFillOpsAfterInterception(); | |
| } | |
| } else { | |
| // We are going up the stack of interceptors | |
| if (current_interceptor_index_ > 0) { | |
| // Continue running interceptors | |
| current_interceptor_index_--; | |
| rpc_info->RunInterceptor(this, current_interceptor_index_); | |
| } else { | |
| // we are done running all the interceptors without any hijacking | |
| ops_->ContinueFinalizeResultAfterInterception(); | |
| } | |
| } | |
| } | |
| void ProceedServer() { | |
| auto* rpc_info = call_->server_rpc_info(); | |
| if (!reverse_) { | |
| current_interceptor_index_++; | |
| if (current_interceptor_index_ < rpc_info->interceptors_.size()) { | |
| return rpc_info->RunInterceptor(this, current_interceptor_index_); | |
| } else if (ops_) { | |
| return ops_->ContinueFillOpsAfterInterception(); | |
| } | |
| } else { | |
| // We are going up the stack of interceptors | |
| if (current_interceptor_index_ > 0) { | |
| // Continue running interceptors | |
| current_interceptor_index_--; | |
| return rpc_info->RunInterceptor(this, current_interceptor_index_); | |
| } else if (ops_) { | |
| return ops_->ContinueFinalizeResultAfterInterception(); | |
| } | |
| } | |
| GPR_CODEGEN_ASSERT(callback_); | |
| callback_(); | |
| } | |
| void ClearHookPoints() { | |
| for (auto i = static_cast<experimental::InterceptionHookPoints>(0); | |
| i < experimental::InterceptionHookPoints::NUM_INTERCEPTION_HOOKS; | |
| i = static_cast<experimental::InterceptionHookPoints>( | |
| static_cast<size_t>(i) + 1)) { | |
| hooks_[static_cast<size_t>(i)] = false; | |
| } | |
| } | |
| std::array<bool, | |
| static_cast<size_t>( | |
| experimental::InterceptionHookPoints::NUM_INTERCEPTION_HOOKS)> | |
| hooks_; | |
| size_t current_interceptor_index_ = 0; // Current iterator | |
| bool reverse_ = false; | |
| bool ran_hijacking_interceptor_ = false; | |
| Call* call_ = nullptr; // The Call object is present along with CallOpSet | |
| // object/callback | |
| CallOpSetInterface* ops_ = nullptr; | |
| std::function<void(void)> callback_; | |
| ByteBuffer* send_message_ = nullptr; | |
| std::multimap<grpc::string, grpc::string>* send_initial_metadata_; | |
| grpc_status_code* code_ = nullptr; | |
| grpc::string* error_details_ = nullptr; | |
| grpc::string* error_message_ = nullptr; | |
| Status send_status_; | |
| std::multimap<grpc::string, grpc::string>* send_trailing_metadata_ = nullptr; | |
| void* recv_message_ = nullptr; | |
| MetadataMap* recv_initial_metadata_ = nullptr; | |
| Status* recv_status_ = nullptr; | |
| MetadataMap* recv_trailing_metadata_ = nullptr; | |
| }; | |
| // A special implementation of InterceptorBatchMethods to send a Cancel | |
| // notification down the interceptor stack | |
| class CancelInterceptorBatchMethods | |
| : public experimental::InterceptorBatchMethods { | |
| public: | |
| bool QueryInterceptionHookPoint( | |
| experimental::InterceptionHookPoints type) override { | |
| if (type == experimental::InterceptionHookPoints::PRE_SEND_CANCEL) { | |
| return true; | |
| } else { | |
| return false; | |
| } | |
| } | |
| void Proceed() override { | |
| // This is a no-op. For actual continuation of the RPC simply needs to | |
| // return from the Intercept method | |
| } | |
| void Hijack() override { | |
| // Only the client can hijack when sending down initial metadata | |
| GPR_CODEGEN_ASSERT(false && | |
| "It is illegal to call Hijack on a method which has a " | |
| "Cancel notification"); | |
| } | |
| ByteBuffer* GetSendMessage() override { | |
| GPR_CODEGEN_ASSERT(false && | |
| "It is illegal to call GetSendMessage on a method which " | |
| "has a Cancel notification"); | |
| return nullptr; | |
| } | |
| std::multimap<grpc::string, grpc::string>* GetSendInitialMetadata() override { | |
| GPR_CODEGEN_ASSERT(false && | |
| "It is illegal to call GetSendInitialMetadata on a " | |
| "method which has a Cancel notification"); | |
| return nullptr; | |
| } | |
| Status GetSendStatus() override { | |
| GPR_CODEGEN_ASSERT(false && | |
| "It is illegal to call GetSendStatus on a method which " | |
| "has a Cancel notification"); | |
| return Status(); | |
| } | |
| void ModifySendStatus(const Status& status) override { | |
| GPR_CODEGEN_ASSERT(false && | |
| "It is illegal to call ModifySendStatus on a method " | |
| "which has a Cancel notification"); | |
| return; | |
| } | |
| std::multimap<grpc::string, grpc::string>* GetSendTrailingMetadata() | |
| override { | |
| GPR_CODEGEN_ASSERT(false && | |
| "It is illegal to call GetSendTrailingMetadata on a " | |
| "method which has a Cancel notification"); | |
| return nullptr; | |
| } | |
| void* GetRecvMessage() override { | |
| GPR_CODEGEN_ASSERT(false && | |
| "It is illegal to call GetRecvMessage on a method which " | |
| "has a Cancel notification"); | |
| return nullptr; | |
| } | |
| std::multimap<grpc::string_ref, grpc::string_ref>* GetRecvInitialMetadata() | |
| override { | |
| GPR_CODEGEN_ASSERT(false && | |
| "It is illegal to call GetRecvInitialMetadata on a " | |
| "method which has a Cancel notification"); | |
| return nullptr; | |
| } | |
| Status* GetRecvStatus() override { | |
| GPR_CODEGEN_ASSERT(false && | |
| "It is illegal to call GetRecvStatus on a method which " | |
| "has a Cancel notification"); | |
| return nullptr; | |
| } | |
| std::multimap<grpc::string_ref, grpc::string_ref>* GetRecvTrailingMetadata() | |
| override { | |
| GPR_CODEGEN_ASSERT(false && | |
| "It is illegal to call GetRecvTrailingMetadata on a " | |
| "method which has a Cancel notification"); | |
| return nullptr; | |
| } | |
| std::unique_ptr<ChannelInterface> GetInterceptedChannel() override { | |
| GPR_CODEGEN_ASSERT(false && | |
| "It is illegal to call GetInterceptedChannel on a " | |
| "method which has a Cancel notification"); | |
| return std::unique_ptr<ChannelInterface>(nullptr); | |
| } | |
| }; | |
| } // namespace internal | |
| } // namespace grpc | |
| #endif // GRPCPP_IMPL_CODEGEN_INTERCEPTOR_COMMON_H |