include/ylt/coro_rpc/impl/context.hpp (138 lines of code) (raw):

/* * Copyright (c) 2023, Alibaba Group Holding Limited; * * 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. */ #pragma once #include <async_simple/coro/Lazy.h> #include <any> #include <cstdint> #include <functional> #include <memory> #include <type_traits> #include <utility> #include <variant> #include <ylt/easylog.hpp> #include "coro_connection.hpp" #include "ylt/coro_rpc/impl/errno.h" #include "ylt/util/type_traits.h" #include "ylt/util/utils.hpp" namespace coro_rpc { /*! * * @tparam return_msg_type * @tparam coro_connection */ template <typename return_msg_type, typename rpc_protocol> class context_base { protected: std::shared_ptr<context_info_t<rpc_protocol>> self_; typename rpc_protocol::req_header &get_req_head() { return self_->req_head_; } bool check_status() { auto old_flag = self_->status_.exchange(context_status::start_response); if (old_flag != context_status::init) AS_UNLIKELY { ELOG_ERROR << "response message more than one time"; return false; } if (self_->has_closed()) AS_UNLIKELY { ELOG_DEBUG << "response_msg failed: connection has been closed"; return false; } return true; } public: /*! * Construct a context by a share pointer of context Concept * instance * @param a share pointer for coro_connection */ context_base(std::shared_ptr<context_info_t<rpc_protocol>> context_info) : self_(std::move(context_info)) { if (self_->conn_) { self_->conn_->set_rpc_return_by_callback(); } }; context_base() = default; using return_type = return_msg_type; void response_error(coro_rpc::err_code error_code, std::string_view error_msg) { if (!check_status()) AS_UNLIKELY { return; }; ELOGI << "rpc error in function:" << self_->get_rpc_function_name() << ". error code:" << error_code.ec << ". message : " << error_msg; self_->conn_->template response_error<rpc_protocol>( error_code, error_msg, self_->req_head_, std::move(self_->complete_handler_)); } void response_error(coro_rpc::err_code error_code) { response_error(error_code, error_code.message()); } /*! * Send response message * * The `return_msg_type` must be constructed by these `args`. * * If the connection has already sent response message, * an error log will be reported. * * @tparam Args * @param args */ template <typename... Args> void response_msg(Args &&...args) { if (!check_status()) AS_UNLIKELY { return; }; if constexpr (std::is_same_v<return_msg_type, void>) { static_assert(sizeof...(args) == 0, "illegal args"); std::visit( [&]<typename serialize_proto>(const serialize_proto &) { self_->conn_->template response_msg<rpc_protocol>( serialize_proto::serialize(), std::move(self_->resp_attachment_), self_->req_head_, std::move(self_->complete_handler_)); }, *rpc_protocol::get_serialize_protocol(self_->req_head_)); } else { static_assert( requires { return_msg_type{std::forward<Args>(args)...}; }, "constructed return_msg_type failed by illegal args"); return_msg_type ret{std::forward<Args>(args)...}; std::visit( [&]<typename serialize_proto>(const serialize_proto &) { self_->conn_->template response_msg<rpc_protocol>( serialize_proto::serialize(ret), std::move(self_->resp_attachment_), self_->req_head_, std::move(self_->complete_handler_)); }, *rpc_protocol::get_serialize_protocol(self_->req_head_)); // complete_handler_(std::move(conn_), std::move(ret)); } /*finish here*/ self_->status_ = context_status::finish_response; } const context_info_t<rpc_protocol> *get_context_info() const noexcept { return self_.get(); } context_info_t<rpc_protocol> *get_context_info() noexcept { return self_.get(); } }; template <typename T> struct get_type_t { using type = T; }; template <typename T> struct get_type_t<async_simple::coro::Lazy<T>> { using type = T; }; template <auto func> inline auto get_return_type() { using T = decltype(func); using param_type = util::function_parameters_t<T>; using return_type = typename get_type_t<util::function_return_type_t<T>>::type; if constexpr (std::is_void_v<param_type>) { if constexpr (std::is_void_v<return_type>) { return; } else { return return_type{}; } } else { using First = std::tuple_element_t<0, param_type>; constexpr bool is_conn = util::is_specialization<First, context_base>::value; if constexpr (is_conn) { using U = typename First::return_type; if constexpr (std::is_void_v<U>) { return; } else { return U{}; } } else if constexpr (!std::is_same_v<void, return_type>) { return return_type{}; } else { return; } } } } // namespace coro_rpc