30 #include "../base/cpu_profiler_guard.h"
31 #include "../base/logger.h"
32 #include "../base/memory_resource.h"
33 #include "../client.h"
34 #include "../impl/url.h"
35 #include "../serializer.h"
39 template <
typename ReqT,
typename RespT, SecurityType SecT>
41 const std::string& url_str,
InitType type) {
42 return std::make_unique<Client<ReqT, RespT, SecT>>(url_str, type);
45 template <
typename ReqT,
typename RespT, SecurityType SecT>
47 const std::string& url_str,
InitType type) {
48 return std::make_shared<Client<ReqT, RespT, SecT>>(url_str, type);
51 template <
typename ReqT,
typename RespT, SecurityType SecT>
52 template <
typename ConfT,
typename>
54 static_assert(ConfT::get_allow_impl_type() & kImplType,
"Conf does not support client mode.");
56 if VUNLIKELY (!conf.parse(kImplType) || !conf.is_valid()) {
57 VLOG_F(conf,
" client configuration is invalid or could not be parsed.");
61 this->impl_ = conf.create_client();
64 VLOG_F(conf,
" client implementation not available for this transport.");
68 this->impl_->transport_type = conf.get_transport_type();
69 this->impl_->ser_type = Serializer::get_serialized_type<kReqType, ReqT>();
71 if constexpr (kHasResp) {
72 const auto resp_ser_type = Serializer::get_serialized_type<kRespType, RespT>();
74 if (!this->impl_->ser_type.empty() || !resp_ser_type.empty()) {
75 this->impl_->ser_type +=
"|" + resp_ser_type;
80 constexpr
auto kReqSchemaType = Serializer::get_schema_type<kReqType, ReqT>();
81 constexpr
auto kRespSchemaType = Serializer::get_schema_type<kRespType, RespT>();
83 if constexpr (kHasResp && kReqSchemaType != kRespSchemaType) {
86 this->impl_->schema_type = kReqSchemaType;
90 this->impl_->is_cdr_type = Serializer::is_cdr_type<ReqT>();
91 this->impl_->is_resp_type = kHasResp;
93 if constexpr (std::is_same_v<ConfT, Url>) {
94 this->impl_->url = conf.get_str();
98 this->impl_->is_security_type =
true;
106 template <
typename ReqT,
typename RespT, SecurityType SecT>
108 :
Client<ReqT, RespT, SecT>(
Url(url_str), type) {}
110 template <
typename ReqT,
typename RespT, SecurityType SecT>
113 std::lock_guard lock(future_mtx_);
121 template <
typename ReqT,
typename RespT, SecurityType SecT>
123 this->impl_->detect_connected(std::move(callback));
126 template <
typename ReqT,
typename RespT, SecurityType SecT>
129 VLOG_W(
"Client: Timeout value is 0, using infinite wait instead.");
133 return this->impl_->wait_for_connected(timeout);
136 template <
typename ReqT,
typename RespT, SecurityType SecT>
138 return this->impl_->is_connected();
141 template <
typename ReqT,
typename RespT, SecurityType SecT>
144 VLOG_W(
"Client: Timeout value is 0, using infinite wait instead.");
148 #ifndef VLINK_DISABLE_PROFILER
152 static_assert(kHasResp,
"Invoke requires a response type.");
156 if constexpr (std::is_same_v<ReqT, Bytes> && std::is_same_v<RespT, Bytes>) {
157 ret = call_bytes(req, [&resp](
const Bytes& resp_data) { resp = resp_data; }, timeout);
162 if (this->is_support_loan_) {
163 size_t ser_size = Serializer::get_serialized_size<kReqType>(req);
165 req_data = this->impl_->loan(ser_size);
173 if VUNLIKELY (!Serializer::serialize<kReqType>(req, req_data, this->impl_->transport_type)) {
174 VLOG_T(
"Client serialize failed, url: ", this->impl_->url,
".");
177 if (this->is_support_loan_) {
178 this->impl_->return_loan(req_data);
185 bool deserialize_success =
false;
189 [
this, &resp, &deserialize_success](
const Bytes& resp_data) {
190 if VLIKELY (Serializer::deserialize<kRespType>(resp_data, resp, this->impl_->transport_type)) {
191 deserialize_success =
true;
193 VLOG_T(
"Client deserialize failed, url: ", this->impl_->url,
".");
198 ret = ret && deserialize_success;
204 template <
typename ReqT,
typename RespT, SecurityType SecT>
207 VLOG_W(
"Client: Timeout value is 0, using infinite wait instead.");
211 #ifndef VLINK_DISABLE_PROFILER
215 thread_local
auto resp = this->
template get_default_value<RespT>();
217 if VLIKELY (invoke(req, resp, timeout)) {
218 return std::make_optional<RespT>(resp);
224 template <
typename ReqT,
typename RespT, SecurityType SecT>
226 #ifndef VLINK_DISABLE_PROFILER
230 static_assert(kHasResp,
"Invoke requires a response type.");
234 if constexpr (std::is_same_v<ReqT, Bytes> && std::is_same_v<RespT, Bytes>) {
235 ret = call_bytes(req, [callback = std::move(callback)](
const Bytes& resp_data) { callback(resp_data); });
240 if (this->is_support_loan_) {
241 size_t ser_size = Serializer::get_serialized_size<kReqType>(req);
243 req_data = this->impl_->loan(ser_size);
245 if VUNLIKELY (ser_size != 0 && req_data.empty()) {
251 if VUNLIKELY (!Serializer::serialize<kReqType>(req, req_data, this->impl_->transport_type)) {
252 VLOG_T(
"Client serialize failed, url: ", this->impl_->url,
".");
255 if (this->is_support_loan_) {
256 this->impl_->return_loan(req_data);
263 ret = call_bytes(req_data, [
this, callback = std::move(callback)](
const Bytes& resp_data) {
264 thread_local
auto resp = this->
template get_default_value<RespT>();
266 if VUNLIKELY (!Serializer::deserialize<kRespType>(resp_data, resp, this->impl_->transport_type)) {
267 VLOG_T(
"Client deserialize failed, url: ", this->impl_->url,
".");
278 template <
typename ReqT,
typename RespT, SecurityType SecT>
280 #ifndef VLINK_DISABLE_PROFILER
284 static_assert(kHasResp,
"async_invoke requires a response type.");
286 auto pro = MemoryResource::make_shared<std::promise<RespT>>();
287 auto future = pro->get_future();
290 int64_t target_seq = 0;
293 std::lock_guard lock(future_mtx_);
294 target_seq = future_seq_++;
295 future_map_.emplace(target_seq, pro);
298 auto cleanup_on_error = [
this, target_seq, pro](
const std::string& error_str) {
299 std::lock_guard lock(future_mtx_);
300 future_map_.erase(target_seq);
304 }
catch (std::exception&) {
305 pro->set_exception(std::current_exception());
309 if constexpr (std::is_same_v<ReqT, Bytes> && std::is_same_v<RespT, Bytes>) {
310 ret = call_bytes(req, [
this, target_seq](
const Bytes& resp_data) {
311 std::lock_guard lock(future_mtx_);
312 auto it = future_map_.find(target_seq);
314 if VLIKELY (it != future_map_.end()) {
315 it->second->set_value(resp_data);
316 future_map_.erase(it);
323 if (this->is_support_loan_) {
324 size_t ser_size = Serializer::get_serialized_size<kReqType>(req);
325 req_data = this->impl_->loan(ser_size);
327 if VUNLIKELY (ser_size != 0 && req_data.empty()) {
328 cleanup_on_error(
"Client async_invoke error (Failed to Loan)");
334 if VUNLIKELY (!Serializer::serialize<kReqType>(req, req_data, this->impl_->transport_type)) {
335 VLOG_T(
"Client serialize failed, url: ", this->impl_->url,
".");
338 if (this->is_support_loan_) {
339 this->impl_->return_loan(req_data);
343 cleanup_on_error(
"Client async_invoke error (Failed to serialize req)");
347 ret = call_bytes(req_data, [
this, target_seq](
const Bytes& resp_data) {
348 bool convert_success =
false;
350 thread_local
auto resp = this->
template get_default_value<RespT>();
352 if VLIKELY (Serializer::deserialize<kRespType>(resp_data, resp, this->impl_->transport_type)) {
353 convert_success =
true;
355 VLOG_T(
"Client deserialize failed, url: ", this->impl_->url,
".");
358 std::lock_guard lock(future_mtx_);
360 auto it = future_map_.find(target_seq);
362 if VLIKELY (it != future_map_.end()) {
364 it->second->set_value(resp);
367 throw Exception::RuntimeError(
"Client async_invoke error (Failed to deserialize resp)");
368 }
catch (std::exception&) {
369 it->second->set_exception(std::current_exception());
373 future_map_.erase(it);
379 cleanup_on_error(
"Client async_invoke error (Failed to call)");
385 template <
typename ReqT,
typename RespT, SecurityType SecT>
387 #ifndef VLINK_DISABLE_PROFILER
391 static_assert(!kHasResp,
"Send not supported; use invoke() for request-response.");
395 if constexpr (std::is_same_v<ReqT, Bytes>) {
396 ret = call_bytes(req);
401 if (this->is_support_loan_) {
402 size_t ser_size = Serializer::get_serialized_size<kReqType>(req);
404 req_data = this->impl_->loan(ser_size);
412 if VUNLIKELY (!Serializer::serialize<kReqType>(req, req_data, this->impl_->transport_type)) {
413 VLOG_T(
"Client serialize failed, url: ", this->impl_->url,
".");
416 if (this->is_support_loan_) {
417 this->impl_->return_loan(req_data);
424 ret = call_bytes(req_data);
430 template <
typename ReqT,
typename RespT, SecurityType SecT>
432 std::chrono::milliseconds timeout) {
436 if VUNLIKELY (!this->impl_->security || !this->impl_->security->encrypt(req_data, req_sec_data)) {
437 VLOG_T(
"Client encrypt failed, url: ", this->impl_->url,
".");
442 return this->impl_->call(req_sec_data,
nullptr, timeout);
445 return this->impl_->call(
447 [
this, callback = std::move(callback)](
const Bytes& resp_data) {
450 if VUNLIKELY (!this->impl_->security || !this->impl_->security->decrypt(resp_data, resp_sec_data)) {
451 VLOG_T(
"Client decrypt failed, url: ", this->impl_->url,
".");
455 this->invoke_callback(callback, resp_sec_data);
462 return this->impl_->call(req_data,
nullptr, timeout);
465 return this->impl_->call(
467 [
this, callback = std::move(callback)](
const Bytes& resp_data) {
470 this->invoke_callback(callback, resp_data);
476 template <
typename ReqT,
typename RespT>
477 template <
typename SecurityConfigT>
479 const std::string& url_str, SecurityConfigT&& sec_cfg,
InitType type) {
480 static_assert(std::is_same_v<std::decay_t<SecurityConfigT>,
Security::Config>,
481 "SecurityConfigT must be Security::Config.");
483 return std::make_unique<SecurityClient<ReqT, RespT>>(url_str, std::forward<SecurityConfigT>(sec_cfg), type);
486 template <
typename ReqT,
typename RespT>
487 template <
typename SecurityConfigT>
489 const std::string& url_str, SecurityConfigT&& sec_cfg,
InitType type) {
490 static_assert(std::is_same_v<std::decay_t<SecurityConfigT>,
Security::Config>,
491 "SecurityConfigT must be Security::Config.");
493 return std::make_shared<SecurityClient<ReqT, RespT>>(url_str, std::forward<SecurityConfigT>(sec_cfg), type);
496 template <
typename ReqT,
typename RespT>
497 template <
typename ConfT,
typename SecurityConfigT,
typename>
500 static_assert(std::is_same_v<std::decay_t<SecurityConfigT>,
Security::Config>,
501 "SecurityConfigT must be Security::Config.");
510 template <
typename ReqT,
typename RespT>
511 template <
typename SecurityConfigT>
514 static_assert(std::is_same_v<std::decay_t<SecurityConfigT>,
Security::Config>,
515 "SecurityConfigT must be Security::Config.");
Fixed-size 128-byte buffer holder with SBO, five ownership modes and integrated codecs.
Definition: bytes.h:120
bool empty() const noexcept
Reports whether the buffer is logically empty.
Definition: bytes.h:861
Type-safe RPC caller for the VLink method communication model.
Definition: client.h:132
void detect_connected(ConnectCallback &&callback)
Registers a callback invoked when the server-presence state changes.
Definition: client-inl.h:122
std::unique_ptr< Client< ReqT, RespT, SecT > > UniquePtr
Owning unique-pointer alias.
Definition: client.h:134
static SharedPtr create_shared(const std::string &url_str, InitType type=InitType::kWithInit)
Heap-allocates a Client and wraps it in a std::shared_ptr.
Definition: client-inl.h:46
bool wait_for_connected(std::chrono::milliseconds timeout=Timeout::kDefaultInterval)
Blocks until a server is discovered or timeout expires.
Definition: client-inl.h:127
Client(const ConfT &conf, InitType type=InitType::kWithInit)
Constructs a client from a typed transport configuration object.
Definition: client-inl.h:53
static UniquePtr create_unique(const std::string &url_str, InitType type=InitType::kWithInit)
Heap-allocates a Client and wraps it in a std::unique_ptr.
Definition: client-inl.h:40
bool invoke(const ReqT &req, RespT &resp, std::chrono::milliseconds timeout=Timeout::kDefaultInterval)
Synchronous request/response invocation with an output parameter.
Definition: client-inl.h:142
std::future< RespT > async_invoke(const ReqT &req)
Asynchronous future-based invocation.
Definition: client-inl.h:279
~Client() override
Destroys the client and tears down outstanding promises.
Definition: client-inl.h:111
std::shared_ptr< Client< ReqT, RespT, SecT > > SharedPtr
Owning shared-pointer alias.
Definition: client.h:135
bool is_connected() const
Non-blocking query of server presence.
Definition: client-inl.h:137
bool send(const ReqT &req)
Fire-and-forget request emission.
Definition: client-inl.h:386
Scope guard that opens and closes a CpuProfiler active interval.
Definition: cpu_profiler_guard.h:80
Generic runtime failure; thrown by the logger on kFatal messages.
Definition: exception.h:90
Copyable type-erased callable analogue of std::function with a tunable SBO and pool spill.
Definition: functional.h:131
bool enable_security(const Security::Config &cfg)
Installs a Security configuration before transport initialisation.
Definition: node-inl.h:321
virtual bool init()
Initialises the node and its transport back-end.
Definition: node-inl.h:39
std::unique_ptr< SecurityClient< ReqT, RespT > > UniquePtr
Owning unique-pointer alias.
Definition: client.h:323
static UniquePtr create_unique(const std::string &url_str, SecurityConfigT &&sec_cfg={}, InitType type=InitType::kWithInit)
Heap-allocates a SecurityClient and wraps it in a std::unique_ptr.
Definition: client-inl.h:478
std::shared_ptr< SecurityClient< ReqT, RespT > > SharedPtr
Owning shared-pointer alias.
Definition: client.h:324
static SharedPtr create_shared(const std::string &url_str, SecurityConfigT &&sec_cfg={}, InitType type=InitType::kWithInit)
Heap-allocates a SecurityClient and wraps it in a std::shared_ptr.
Definition: client-inl.h:488
SecurityClient(const ConfT &conf, SecurityConfigT &&sec_cfg={}, InitType type=InitType::kWithInit)
Constructs a SecurityClient from a typed configuration object.
Definition: client-inl.h:498
#define VLOG_F(...)
Definition: logger.h:791
#define VLOG_W(...)
Definition: logger.h:787
#define VLOG_T(...)
Definition: logger.h:781
#define VUNLIKELY(...)
Short alias for VLINK_UNLIKELY.
Definition: macros.h:289
#define VLIKELY(...)
Short alias for VLINK_LIKELY.
Definition: macros.h:284
InitType
Selects between immediate and deferred node initialisation.
Definition: types.h:154
@ kWithoutInit
Defer initialisation; call init() manually.
@ kWithInit
Initialise immediately in the constructor.
@ kUnknown
Decoding family unknown.
@ kClientResponse
RPC response observed by a Client node.
@ kClientRequest
RPC request emitted by a Client node.
SecurityType
Compile-time selector for the per-node message security variant.
Definition: types.h:172
@ kWithSecurity
Encrypted and authenticated transport.
Aggregate of every parameter accepted by the Security constructor.
Definition: security.h:164
static constexpr std::chrono::milliseconds kInfinite
Wait indefinitely (negative timeout).
Definition: types.h:260
Conf subclass that routes virtual calls to the transport selected by a URL string.
Definition: url.h:213