Branch data Line data Source code
1 : : /* 2 : : * Copyright (C) 2026 by Thun Lu. All rights reserved. 3 : : * Author: Thun Lu <thun.lu@zohomail.cn> 4 : : * Repo: https://github.com/thun-res/vlink 5 : : * _ __ __ _ __ 6 : : * | | / / / / (_) ____ / /__ 7 : : * | | / / / / / / / __ \ / //_/ 8 : : * | |/ / / /___ / / / / / / / ,< 9 : : * |___/ /_____/ /_/ /_/ /_/ /_/|_| 10 : : * 11 : : * Licensed under the Apache License, Version 2.0 (the "License"); 12 : : * you may not use this file except in compliance with the License. 13 : : * You may obtain a copy of the License at 14 : : * 15 : : * http://www.apache.org/licenses/LICENSE-2.0 16 : : * 17 : : * Unless required by applicable law or agreed to in writing, software 18 : : * distributed under the License is distributed on an "AS IS" BASIS, 19 : : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 : : * See the License for the specific language governing permissions and 21 : : * limitations under the License. 22 : : */ 23 : : 24 : : #include "./impl/node_impl.h" 25 : : 26 : : #include <atomic> 27 : : #include <charconv> 28 : : #include <memory> 29 : : #include <mutex> 30 : : #include <shared_mutex> 31 : : #include <utility> 32 : : 33 : : #include "./base/bytes.h" 34 : : #include "./base/logger.h" 35 : : #include "./base/message_loop.h" 36 : : #include "./extension/bag_writer.h" 37 : : #include "./extension/discovery_reporter.h" 38 : : #include "./impl/client_impl.h" 39 : : #include "./impl/server_impl.h" 40 : : #include "./private/license_check.h" 41 : : #include "./version.h" 42 : : 43 : : namespace vlink { 44 : : 45 : : static constexpr bool kIgnoreIntraUrl{false}; 46 : : 47 : : // GlobalDiscoveryReporter 48 : : struct GlobalDiscoveryReporter final { 49 : : public: 50 : 2098 : static DiscoveryReporter* get(bool create_if_missing = true) { 51 [ + + + - ]: 2098 : static GlobalDiscoveryReporter global; 52 : 2098 : return global.get_instance(create_if_missing); 53 : : } 54 : : 55 : : private: 56 : : GlobalDiscoveryReporter() = default; 57 : : 58 : 2098 : DiscoveryReporter* get_instance(bool create_if_missing) { 59 [ + - ]: 2098 : std::lock_guard lock(mtx_); 60 : : 61 [ + - ]: 2098 : check_disable_env(); 62 : : 63 [ + + + - : 2098 : if (!create_if_missing || disabled_ || instance_) { + + + + ] 64 : 2059 : return instance_.get(); 65 : : } 66 : : 67 [ + - ]: 39 : instance_ = std::make_unique<DiscoveryReporter>(); 68 [ + - ]: 39 : instance_->async_run(); 69 : : 70 : 39 : return instance_.get(); 71 : 2098 : } 72 : : 73 : 2098 : void check_disable_env() { 74 [ + + ]: 2098 : if (disable_checked_) { 75 : 2046 : return; 76 : : } 77 : : 78 : 52 : disable_checked_ = true; 79 : : 80 [ + - + - : 52 : static std::string discovery = Utils::get_env("VLINK_DISCOVER_DISABLE"); + - + - - - ] 81 : : 82 [ - + ]: 52 : if (!discovery.empty()) { 83 : 0 : int value = 0; 84 [ # # ]: 0 : auto [p, error] = std::from_chars(discovery.data(), discovery.data() + discovery.size(), value); 85 : : 86 [ # # # # ]: 0 : if (error == std::errc() && value == 1) { 87 [ # # # # ]: 0 : VLOG_I("DiscoveryReporter: Global discovery reporter is disabled."); 88 : 0 : disabled_ = true; 89 : 0 : return; 90 : : } 91 : : } 92 : : } 93 : : 94 : 52 : ~GlobalDiscoveryReporter() = default; 95 : : 96 : : std::mutex mtx_; 97 : : bool disable_checked_{false}; 98 : : bool disabled_{false}; 99 : : std::unique_ptr<DiscoveryReporter> instance_; 100 : : 101 : : VLINK_DISALLOW_COPY_AND_ASSIGN(GlobalDiscoveryReporter) 102 : : }; 103 : : 104 : 1952 : static bool should_report_discovery(const NodeImpl& node) { 105 [ + + ]: 1934 : return node.is_discovery_enabled && !node.url.empty() && 106 [ + + + + : 3886 : !(node.transport_type == TransportType::kDds && node.is_cdr_type) && !node.is_security_type && + - + + ] 107 : 1952 : (!kIgnoreIntraUrl || node.transport_type != TransportType::kIntra); 108 : : } 109 : : 110 : : // AbstractNode 111 [ + - ]: 1 : std::any AbstractNode::get_native_handle() const { return nullptr; } 112 : : 113 : 840 : AbstractNode::AbstractNode() = default; 114 : : 115 : 840 : AbstractNode::~AbstractNode() = default; 116 : : 117 : : // NodeImpl 118 : : struct NodeImplHelper final { 119 : : std::atomic_bool is_interrupted{false}; 120 : : 121 : : Conf::PropertiesMap property_map; 122 : : std::shared_mutex mtx; 123 : : std::shared_mutex status_mtx; 124 : : std::mutex post_mtx; 125 : : NodeImpl::StatusCallback status_callback; 126 : : std::atomic<MessageLoop*> message_loop{nullptr}; 127 : : 128 : : std::shared_ptr<BagWriter> data_recorder; 129 : : }; 130 : : 131 : 814 : bool NodeImpl::is_support_loan() const { return false; } 132 : : 133 : 2 : Bytes NodeImpl::loan(int64_t size) { 134 : : (void)size; 135 : : 136 : 2 : return Bytes(); 137 : : } 138 : : 139 : 5 : bool NodeImpl::return_loan(const Bytes& bytes) { 140 : : (void)bytes; 141 : : 142 : 5 : return false; 143 : : } 144 : : 145 : 0 : void NodeImpl::set_manual_unloan(bool manual_unloan) { (void)manual_unloan; } 146 : : 147 : 1 : bool NodeImpl::suspend() { 148 : : // has_suspend = true; 149 : : 150 [ + - + - ]: 2 : VLOG_W("Function [suspend] is not supported."); 151 : : 152 : 1 : return false; 153 : : } 154 : : 155 : 1 : bool NodeImpl::resume() { 156 : : // has_suspend = false; 157 : : 158 [ + - + - ]: 2 : VLOG_W("Function [resume] is not supported."); 159 : : 160 : 1 : return false; 161 : : } 162 : : 163 : 1 : bool NodeImpl::is_suspend() const { 164 [ + - + - ]: 2 : VLOG_W("Function [is_suspend] is not supported."); 165 : : 166 : 1 : return false; 167 : : } 168 : : 169 : 988 : void NodeImpl::interrupt() { helper_->is_interrupted.store(true, std::memory_order_release); } 170 : : 171 : 2 : const struct Conf* NodeImpl::get_conf() const { return nullptr; } 172 : : 173 : 1 : const AbstractNode* NodeImpl::get_abstract_node() const { return nullptr; } 174 : : 175 : 1 : Status::BasePtr NodeImpl::get_status(Status::Type type) const { 176 : : (void)type; 177 : : 178 [ + - + - ]: 2 : VLOG_W("Function [get_status] is not supported."); 179 : : 180 : 1 : return std::make_shared<Status::Unknown>(); 181 : : } 182 : : 183 : 974 : bool NodeImpl::check_version(const Version& version) { 184 : 974 : Version runtime_version{VLINK_VERSION_MAJOR, VLINK_VERSION_MINOR, VLINK_VERSION_PATCH}; 185 : : 186 [ + + ]: 974 : if VUNLIKELY (version != runtime_version) { 187 : : static std::atomic_bool print_warn{false}; 188 : : 189 [ + + ]: 2 : if VUNLIKELY (!print_warn.exchange(true, std::memory_order_relaxed)) { 190 [ + - + - ]: 2 : VLOG_W("The version may be incompatible. [Compiled]: ", version.to_string(), 191 : : " [Runtime]: ", runtime_version.to_string(), "."); 192 : : } 193 : : 194 : 2 : return false; 195 : : } 196 : : 197 : 972 : return true; 198 : : } 199 : : 200 : 28 : bool NodeImpl::attach(class MessageLoop* message_loop) { 201 : 28 : MessageLoop* expected = nullptr; 202 : 28 : return helper_->message_loop.compare_exchange_strong(expected, message_loop, std::memory_order_release, 203 : 28 : std::memory_order_relaxed); 204 : : } 205 : : 206 : 634 : bool NodeImpl::detach() { 207 : 634 : MessageLoop* message_loop = nullptr; 208 : : 209 : : { 210 [ + - ]: 634 : std::lock_guard lock(helper_->post_mtx); 211 : : 212 : 634 : message_loop = helper_->message_loop.exchange(nullptr, std::memory_order_acq_rel); 213 : 634 : } 214 : : 215 [ + + ]: 634 : if (!message_loop) { 216 : 609 : return false; 217 : : } 218 : : 219 [ + - ]: 25 : if (!message_loop->is_in_same_thread()) { 220 : 25 : message_loop->wait_for_idle(); 221 : : } 222 : : 223 : 25 : return true; 224 : : } 225 : : 226 : 925 : class MessageLoop* NodeImpl::get_message_loop() const { return helper_->message_loop.load(std::memory_order_acquire); } 227 : : 228 : 32 : void NodeImpl::register_status_handler(StatusCallback&& callback) { 229 [ + + + + : 32 : if VUNLIKELY (transport_type != TransportType::kDds && transport_type != TransportType::kDdsc && + + + + + + + + + + ] 230 : : transport_type != TransportType::kDdsr && transport_type != TransportType::kDdst) { 231 [ + - + - ]: 10 : VLOG_W("Function [register_status_handler] is not supported."); 232 : 5 : return; 233 : : } 234 : : 235 [ + - ]: 27 : std::lock_guard lock(helper_->status_mtx); 236 : 27 : helper_->status_callback = std::move(callback); 237 : 27 : } 238 : : 239 : 931 : bool NodeImpl::has_register_status() const { 240 [ + + + + : 931 : if VUNLIKELY (transport_type != TransportType::kDds && transport_type != TransportType::kDdsc && + + + + + + + + + + ] 241 : : transport_type != TransportType::kDdsr && transport_type != TransportType::kDdst) { 242 [ + - + - ]: 12 : VLOG_W("Function [has_register_status] is not supported."); 243 : 6 : return false; 244 : : } 245 : : 246 [ + - ]: 925 : std::shared_lock lock(helper_->status_mtx); 247 : : 248 : 925 : return helper_->status_callback != nullptr; 249 : 925 : } 250 : : 251 : 29 : void NodeImpl::call_status(Status::BasePtr ptr) { 252 [ + + - + : 29 : if VUNLIKELY (transport_type != TransportType::kDds && transport_type != TransportType::kDdsc && - + - - - + - - - + ] 253 : : transport_type != TransportType::kDdsr && transport_type != TransportType::kDdst) { 254 [ # # # # ]: 0 : VLOG_W("Function [call_status] is not supported."); 255 : 1 : return; 256 : : } 257 : : 258 : : { 259 [ + - ]: 29 : std::lock_guard post_lock(helper_->post_mtx); 260 : : 261 : 29 : auto* message_loop = helper_->message_loop.load(std::memory_order_acquire); 262 : : 263 [ + + ]: 29 : if VLIKELY (message_loop) { 264 [ + - + - ]: 1 : message_loop->post_task([this, ptr]() mutable { 265 [ + - ]: 1 : std::shared_lock lock(helper_->status_mtx); 266 [ + - ]: 1 : if VLIKELY (helper_->status_callback) { 267 [ + - ]: 1 : helper_->status_callback(std::move(ptr)); 268 : : } 269 : 1 : }); 270 : 1 : return; 271 : : } 272 [ + + ]: 29 : } 273 : : 274 [ + - ]: 28 : std::shared_lock lock(helper_->status_mtx); 275 : : 276 [ + - ]: 28 : if VLIKELY (helper_->status_callback) { 277 [ + - ]: 28 : helper_->status_callback(std::move(ptr)); 278 : : } 279 : 28 : } 280 : : 281 : 345 : void NodeImpl::set_property(const std::string& prop, const std::string& value) { 282 [ + - ]: 345 : std::lock_guard lock(helper_->mtx); 283 [ + - + - ]: 345 : helper_->property_map[prop] = value; 284 : 345 : } 285 : : 286 : 244 : std::string NodeImpl::get_property(const std::string& prop) const { 287 [ + - ]: 244 : std::shared_lock lock(helper_->mtx); 288 : : 289 [ + - ]: 244 : auto iter = helper_->property_map.find(prop); 290 : : 291 [ + + ]: 244 : if VLIKELY (iter != helper_->property_map.end()) { 292 [ + - ]: 237 : return iter->second; 293 : : } 294 : : 295 : 7 : return {}; 296 : 244 : } 297 : : 298 : 452 : Conf::PropertiesMap NodeImpl::get_all_properties() const { 299 [ + - ]: 452 : std::shared_lock lock(helper_->mtx); 300 [ + - ]: 904 : return helper_->property_map; 301 : 452 : } 302 : : 303 : 12 : void NodeImpl::set_discovery_enabled(bool enable) { is_discovery_enabled = enable; } 304 : : 305 : 5 : bool NodeImpl::get_discovery_enabled() const { return is_discovery_enabled; } 306 : : 307 : 1 : void NodeImpl::set_record_path(const std::string& path) { 308 : 1 : std::shared_ptr<BagWriter> new_recorder; 309 : : 310 [ - + ]: 1 : if (!path.empty()) { 311 [ # # ]: 0 : new_recorder = BagWriter::filter_get(path); 312 : : } 313 : : 314 : 1 : std::shared_ptr<BagWriter> old_recorder; 315 : : 316 : : { 317 [ + - ]: 1 : std::lock_guard lock(helper_->mtx); 318 : 1 : old_recorder = std::move(helper_->data_recorder); 319 : 1 : helper_->data_recorder = std::move(new_recorder); 320 : 1 : } 321 : : 322 : 1 : old_recorder.reset(); 323 : 1 : } 324 : : 325 : 0 : bool NodeImpl::enable_security(const Security::Config& cfg) { 326 [ # # ]: 0 : auto sec_cfg = cfg; 327 : : 328 [ # # ]: 0 : return enable_security(std::move(sec_cfg)); 329 : 0 : } 330 : : 331 : 49 : bool NodeImpl::enable_security(Security::Config&& cfg) { 332 [ + + + + : 49 : if VUNLIKELY (transport_type == TransportType::kIntra || (transport_type == TransportType::kDds && is_cdr_type)) { - + - + + + ] 333 [ + - + - ]: 10 : VLOG_W("Security::Config will ignore intra/dds(cdr) transport."); 334 : 5 : return false; 335 : : } 336 : : 337 [ + - ]: 44 : if VLIKELY (cfg.advanced.aad_context.empty()) { 338 [ + - ]: 44 : cfg.advanced.aad_context = url; 339 [ + - ]: 44 : cfg.advanced.aad_context += "|"; 340 [ + - ]: 44 : cfg.advanced.aad_context += ser_type; 341 [ + - ]: 44 : cfg.advanced.aad_context += "|"; 342 [ + - + - ]: 44 : cfg.advanced.aad_context += std::to_string(static_cast<uint32_t>(schema_type)); 343 : : } 344 : : 345 [ + - ]: 44 : auto candidate = std::make_unique<Security>(std::move(cfg)); 346 : : 347 [ - + ]: 44 : if VUNLIKELY (!candidate->is_configured()) { 348 : : VLOG_W("Security::Config has no usable slot."); // LCOV_EXCL_LINE GCOVR_EXCL_LINE 349 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE 350 : : } 351 : : 352 [ + + + + ]: 44 : bool needs_encrypt = (impl_type == kPublisher || impl_type == kSetter); 353 [ + + + + ]: 44 : bool needs_decrypt = (impl_type == kSubscriber || impl_type == kGetter); 354 : : 355 : : #if defined(NDEBUG) || defined(__ANDROID__) 356 : : if (impl_type == kClient) { 357 : : needs_encrypt = true; 358 : : const auto* client_impl = static_cast<const ClientImpl*>(this); 359 : : needs_decrypt = client_impl != nullptr && client_impl->is_resp_type; 360 : : } else if (impl_type == kServer) { 361 : : needs_decrypt = true; 362 : : const auto* server_impl = static_cast<const ServerImpl*>(this); 363 : : needs_encrypt = server_impl != nullptr && server_impl->is_resp_type; 364 : : } 365 : : #else 366 [ + + ]: 44 : if (impl_type == kClient) { 367 : 6 : needs_encrypt = true; 368 [ + - ]: 6 : const auto* client_impl = dynamic_cast<const ClientImpl*>(this); 369 [ + - + - ]: 6 : needs_decrypt = client_impl != nullptr && client_impl->is_resp_type; 370 [ + + ]: 38 : } else if (impl_type == kServer) { 371 : 7 : needs_decrypt = true; 372 [ + - ]: 7 : const auto* server_impl = dynamic_cast<const ServerImpl*>(this); 373 [ + - + - ]: 7 : needs_encrypt = server_impl != nullptr && server_impl->is_resp_type; 374 : : } 375 : : #endif 376 : : 377 [ + + - + : 44 : if VUNLIKELY (needs_encrypt && !candidate->can_encrypt()) { - + ] 378 : : VLOG_W("Security::Config cannot encrypt for this sender role."); // LCOV_EXCL_LINE GCOVR_EXCL_LINE 379 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE 380 : : } 381 : : 382 [ + + - + : 44 : if VUNLIKELY (needs_decrypt && !candidate->can_decrypt()) { - + ] 383 : : VLOG_W("Security::Config cannot decrypt for this receiver role."); // LCOV_EXCL_LINE GCOVR_EXCL_LINE 384 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE 385 : : } 386 : : 387 : 44 : security = std::move(candidate); 388 : : 389 : 44 : return true; 390 : 44 : } 391 : : 392 : 1 : void NodeImpl::set_ssl_options(const SslOptions& options) { 393 [ + - ]: 1 : std::lock_guard lock(helper_->mtx); 394 : 1 : options.parse_to(helper_->property_map); 395 : 1 : } 396 : : 397 : 2066 : void NodeImpl::try_record(ActionType action_type, const Bytes& data) { 398 [ + - ]: 2066 : auto* global_recorder = BagWriter::global_get(); 399 : : 400 : 2060 : std::shared_ptr<BagWriter> data_recorder; 401 : : { 402 [ + - ]: 2060 : std::shared_lock lock(helper_->mtx); 403 : 2061 : data_recorder = helper_->data_recorder; 404 : 2067 : } 405 : : 406 [ + - + - : 2065 : if VLIKELY (!global_recorder && !data_recorder) { + - ] 407 : 2064 : return; 408 : : } 409 : : 410 : 0 : if ((kIgnoreIntraUrl && transport_type == TransportType::kIntra) || 411 [ # # # # ]: 0 : (transport_type == TransportType::kDds && is_cdr_type)) { 412 : 0 : return; 413 : : } 414 : : 415 : 0 : Frame frame; 416 : 0 : frame.timestamp = -1; 417 [ # # ]: 0 : frame.url = url; 418 [ # # ]: 0 : frame.ser_type = ser_type; 419 : 0 : frame.schema_type = schema_type; 420 : 0 : frame.action_type = action_type; 421 : 0 : frame.data = Bytes::shallow_copy(data.data(), data.size()); 422 : : 423 [ # # ]: 0 : if VUNLIKELY (global_recorder) { 424 [ # # ]: 0 : global_recorder->push(frame); 425 : : } 426 : : 427 [ # # ]: 0 : if VUNLIKELY (data_recorder) { 428 [ # # ]: 0 : data_recorder->push(frame); 429 : : } 430 [ - + ]: 2064 : } 431 : : 432 : 103 : void NodeImpl::reset_interrupted() { helper_->is_interrupted.store(false, std::memory_order_release); } 433 : : 434 : 203 : bool NodeImpl::is_interrupted() const { return helper_->is_interrupted.load(std::memory_order_acquire); } 435 : : 436 : 976 : void NodeImpl::init_ext() { 437 [ + + ]: 976 : if (should_report_discovery(*this)) { 438 : 437 : auto* global_reporter = GlobalDiscoveryReporter::get(); 439 : : 440 [ + - ]: 437 : if (global_reporter) { 441 [ - + - - : 437 : if (CpuProfiler::is_global_enabled() && !profiler) { - + ] 442 : : profiler = std::make_unique<CpuProfiler>(); // LCOV_EXCL_LINE GCOVR_EXCL_LINE 443 : : } 444 : : 445 : 437 : global_reporter->add(this); 446 : : } 447 : : } 448 : : 449 : : #ifdef VLINK_ENABLE_CHECK_LICENSE 450 : : [[maybe_unused]] static LicenseCheck license; 451 : : #endif 452 : 976 : } 453 : : 454 : 976 : void NodeImpl::deinit_ext() { 455 [ + + ]: 976 : if (!should_report_discovery(*this)) { 456 : 539 : return; 457 : : } 458 : : 459 : 437 : auto* global_reporter = GlobalDiscoveryReporter::get(false); 460 : : 461 [ + - ]: 437 : if (global_reporter) { 462 : 437 : global_reporter->remove(this); 463 : : 464 [ - + - - : 437 : if (CpuProfiler::is_global_enabled() && profiler) { - + ] 465 : : profiler->restart(); // LCOV_EXCL_LINE GCOVR_EXCL_LINE 466 : : } 467 : : } 468 : : } 469 : : 470 : 1224 : void NodeImpl::global_init() { 471 : 1224 : Logger::get(); 472 : : 473 : 1224 : Bytes::init_memory_pool(); 474 : : 475 : 1224 : BagWriter::global_get(); 476 : : 477 : 1224 : GlobalDiscoveryReporter::get(false); 478 : 1224 : } 479 : : 480 [ + - + - ]: 1221 : NodeImpl::NodeImpl(ImplType type) : impl_type(type), helper_(std::make_unique<NodeImplHelper>()) { global_init(); } 481 : : 482 : 1221 : NodeImpl::~NodeImpl() = default; 483 : : 484 : : } // namespace vlink