| /** |
| @section license License |
| |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you 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 |
| |
| /// JSONRPC 2.0 RPC network client. |
| |
| #include <chrono> |
| #include <string_view> |
| |
| #include <yaml-cpp/yaml.h> |
| #include <tscore/Layout.h> |
| #include <tscore/ink_assert.h> |
| |
| #include <swoc/BufferWriter.h> |
| |
| #include "shared/rpc/IPCSocketClient.h" |
| #include "shared/rpc/yaml_codecs.h" |
| |
| namespace shared::rpc |
| { |
| /// |
| /// @brief Wrapper class to interact with the RPC node. Do not use this internally, this is for client's applications only. |
| /// |
| class RPCClient |
| { |
| public: |
| RPCClient() : _client(Layout::get()->runtimedir + "/jsonrpc20.sock") {} |
| |
| /// @brief invoke the remote function using the passed jsonrpc message string. |
| /// This function will connect with the remote rpc node and send the passed json string. If you don't want to deal with the |
| /// endode/decode you can just call @c invoke(JSONRPCRequest const &req). |
| /// @throw runtime_error |
| std::string |
| invoke(std::string_view req, std::chrono::milliseconds timeout_ms, int attempts) |
| { |
| std::string err_text; // for error messages. |
| try { |
| _client.connect(); |
| if (!_client.is_closed()) { |
| std::string resp; |
| _client.send(req); |
| switch (_client.read_all(resp, timeout_ms, attempts)) { |
| case IPCSocketClient::ReadStatus::NO_ERROR: { |
| _client.disconnect(); |
| return resp; |
| } |
| case IPCSocketClient::ReadStatus::BUFFER_FULL: |
| // we don't expect this to happen as client will not let us know about |
| // this for now. Keep this in case we decide to put a limit on |
| // responses. |
| ink_assert(!"Buffer full, not enough space to read the response."); |
| break; |
| case IPCSocketClient::ReadStatus::READ_ERROR: |
| err_text = swoc::bwprint(err_text, "READ_ERROR: Error while reading response. {}({})", std::strerror(errno), errno); |
| break; |
| case IPCSocketClient::ReadStatus::TIMEOUT: |
| err_text = swoc::bwprint(err_text, "TIMEOUT: Couldn't get the response. {}({})", std::strerror(errno), errno); |
| break; |
| default: |
| err_text = "Something happened, we can't read the response. Unknown error."; |
| break; |
| } |
| } else { |
| swoc::bwprint(err_text, "Node seems not available: {}", std ::strerror(errno)); |
| } |
| } catch (std::exception const &ex) { |
| swoc::bwprint(err_text, "RPC Node Error: {}", ex.what()); |
| } |
| _client.disconnect(); |
| // If we've got this far, then there must be an error to report back. |
| throw std::runtime_error(err_text); |
| } |
| |
| /// @brief Invoke the rpc node passing the JSONRPC objects. |
| /// This function will connect with the remote rpc node and send the passed objects which will be encoded and decoded using the |
| /// yamlcpp_json_emitter impl. |
| /// @note If you inherit from @c JSONRPCRequest make sure the base members are properly filled before calling this function, the |
| /// encode/decode will only deal with the @c JSONRPCRequest members, unless you pass your own codec class. By default @c |
| /// yamlcpp_json_emitter is used. If you pass your own Codecs, make sure you follow the yamlcpp_json_emitter API. |
| /// @throw runtime_error |
| /// @throw YAML::Exception |
| template <typename Codec = yamlcpp_json_emitter> |
| JSONRPCResponse |
| invoke(JSONRPCRequest const &req, std::chrono::milliseconds timeout_ms, int attempts) |
| { |
| static_assert(internal::has_decode<Codec>::value || internal::has_encode<Codec>::value, |
| "You need to implement encode/decode in your own codec impl."); |
| // We should add a static_assert and make sure encode/decode are part of Codec type. |
| auto const &reqStr = Codec::encode(req); |
| return Codec::decode(invoke(reqStr, timeout_ms, attempts)); |
| } |
| |
| private: |
| IPCSocketClient _client; |
| }; |
| } // namespace shared::rpc |