#pragma once #include #include #include #include #include "communication/rpc/messages.hpp" #include "io/network/endpoint.hpp" #include "io/network/socket.hpp" namespace communication::rpc { // Client is thread safe, but it is recommended to use thread_local clients. class Client { public: Client(const io::network::Endpoint &endpoint, const std::string &name); // Call function can initiate only one request at the time. Function blocks // until there is a response. If there was an error nullptr is returned. template std::unique_ptr Call(Args &&... args) { using Req = typename TRequestResponse::Request; using Res = typename TRequestResponse::Response; static_assert(std::is_base_of::value, "TRequestResponse::Request must be derived from Message"); static_assert(std::is_base_of::value, "TRequestResponse::Response must be derived from Message"); auto response = Call(std::unique_ptr( std::make_unique(std::forward(args)...))); auto *real_response = dynamic_cast(response.get()); if (!real_response && response) { // Since message_id was checked in private Call function, this means // something is very wrong (probably on the server side). LOG(ERROR) << "Message response was of unexpected type"; socket_ = std::experimental::nullopt; return nullptr; } response.release(); return std::unique_ptr(real_response); } // Call this function from another thread to abort a pending RPC call. void Abort(); private: std::unique_ptr Call(std::unique_ptr request); io::network::Endpoint endpoint_; std::string service_name_; std::experimental::optional socket_; uint32_t next_message_id_{0}; std::array buffer_; size_t received_bytes_{0}; std::mutex mutex_; }; } // namespace communication::rpc