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 : 2177 : static DiscoveryReporter* get(bool create_if_missing = true) { 51 [ + + + - ]: 2177 : static GlobalDiscoveryReporter global; 52 : 2177 : return global.get_instance(create_if_missing); 53 : : } 54 : : 55 : : private: 56 : : GlobalDiscoveryReporter() = default; 57 : : 58 : 2177 : DiscoveryReporter* get_instance(bool create_if_missing) { 59 [ + - ]: 2177 : std::lock_guard lock(mtx_); 60 : : 61 [ + - ]: 2177 : check_disable_env(); 62 : : 63 [ + + + - : 2177 : if (!create_if_missing || disabled_ || instance_) { + + + + ] 64 : 2138 : return instance_.get(); 65 : : } 66 : : 67 [ + - ]: 39 : instance_ = std::make_unique<DiscoveryReporter>(); 68 [ + - ]: 39 : instance_->async_run(); 69 : : 70 : 39 : return instance_.get(); 71 : 2177 : } 72 : : 73 : 2177 : void check_disable_env() { 74 [ + + ]: 2177 : if (disable_checked_) { 75 : 2124 : return; 76 : : } 77 : : 78 : 53 : disable_checked_ = true; 79 : : 80 [ + - + - : 53 : static std::string discovery = Utils::get_env("VLINK_DISCOVER_DISABLE"); + - + - - - ] 81 : : 82 [ - + ]: 53 : 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 : 53 : ~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 : 2046 : static bool should_report_discovery(const NodeImpl& node) { 105 [ + + + + : 2046 : return node.is_discovery_enabled && !node.url.empty() && !node.is_security_type && + + ] 106 : 2046 : (!kIgnoreIntraUrl || node.transport_type != TransportType::kIntra); 107 : : } 108 : : 109 : : // AbstractNode 110 [ + - ]: 1 : std::any AbstractNode::get_native_handle() const { return nullptr; } 111 : : 112 : 868 : AbstractNode::AbstractNode() = default; 113 : : 114 : 868 : AbstractNode::~AbstractNode() = default; 115 : : 116 : : // NodeImpl 117 : : struct NodeImplHelper final { 118 : : std::atomic_bool is_interrupted{false}; 119 : : 120 : : Conf::PropertiesMap property_map; 121 : : std::shared_mutex mtx; 122 : : std::shared_mutex status_mtx; 123 : : std::mutex post_mtx; 124 : : NodeImpl::StatusCallback status_callback; 125 : : std::atomic<MessageLoop*> message_loop{nullptr}; 126 : : 127 : : std::shared_ptr<BagWriter> data_recorder; 128 : : std::atomic_bool data_recorder_enabled{false}; 129 : : }; 130 : : 131 : 860 : 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 : 7 : bool NodeImpl::return_loan(const Bytes& bytes) { 140 : : (void)bytes; 141 : : 142 : 7 : return false; 143 : : } 144 : : 145 : 1 : bool NodeImpl::suspend() { 146 : : // has_suspend = true; 147 : : 148 [ + - + - ]: 2 : VLOG_W("Function [suspend] is not supported."); 149 : : 150 : 1 : return false; 151 : : } 152 : : 153 : 1 : bool NodeImpl::resume() { 154 : : // has_suspend = false; 155 : : 156 [ + - + - ]: 2 : VLOG_W("Function [resume] is not supported."); 157 : : 158 : 1 : return false; 159 : : } 160 : : 161 : 1 : bool NodeImpl::is_suspend() const { 162 [ + - + - ]: 2 : VLOG_W("Function [is_suspend] is not supported."); 163 : : 164 : 1 : return false; 165 : : } 166 : : 167 : 1035 : void NodeImpl::interrupt() { helper_->is_interrupted.store(true, std::memory_order_release); } 168 : : 169 : 2 : const struct Conf* NodeImpl::get_conf() const { return nullptr; } 170 : : 171 : 1 : const AbstractNode* NodeImpl::get_abstract_node() const { return nullptr; } 172 : : 173 : 1 : Status::BasePtr NodeImpl::get_status(Status::Type type) const { 174 : : (void)type; 175 : : 176 [ + - + - ]: 2 : VLOG_W("Function [get_status] is not supported."); 177 : : 178 : 1 : return std::make_shared<Status::Unknown>(); 179 : : } 180 : : 181 : 1021 : bool NodeImpl::check_version(const Version& version) { 182 : 1021 : Version runtime_version{VLINK_VERSION_MAJOR, VLINK_VERSION_MINOR, VLINK_VERSION_PATCH}; 183 : : 184 [ + + ]: 1021 : if VUNLIKELY (version != runtime_version) { 185 : : static std::atomic_bool print_warn{false}; 186 : : 187 [ + + ]: 2 : if VUNLIKELY (!print_warn.exchange(true, std::memory_order_relaxed)) { 188 [ + - + - ]: 2 : VLOG_W("The version may be incompatible. [Compiled]: ", version.to_string(), 189 : : " [Runtime]: ", runtime_version.to_string(), "."); 190 : : } 191 : : 192 : 2 : return false; 193 : : } 194 : : 195 : 1019 : return true; 196 : : } 197 : : 198 : 28 : bool NodeImpl::attach(class MessageLoop* message_loop) { 199 : 28 : MessageLoop* expected = nullptr; 200 : 28 : return helper_->message_loop.compare_exchange_strong(expected, message_loop, std::memory_order_release, 201 : 28 : std::memory_order_relaxed); 202 : : } 203 : : 204 : 635 : bool NodeImpl::detach() { 205 : 635 : MessageLoop* message_loop = nullptr; 206 : : 207 : : { 208 [ + - ]: 635 : std::lock_guard lock(helper_->post_mtx); 209 : : 210 : 635 : message_loop = helper_->message_loop.exchange(nullptr, std::memory_order_acq_rel); 211 : 635 : } 212 : : 213 [ + + ]: 635 : if (!message_loop) { 214 : 610 : return false; 215 : : } 216 : : 217 [ + - ]: 25 : if (!message_loop->is_in_same_thread()) { 218 : 25 : message_loop->wait_for_idle(); 219 : : } 220 : : 221 : 25 : return true; 222 : : } 223 : : 224 : 926 : class MessageLoop* NodeImpl::get_message_loop() const { return helper_->message_loop.load(std::memory_order_acquire); } 225 : : 226 : 29 : void NodeImpl::register_status_handler(StatusCallback&& callback) { 227 [ + + + + : 29 : if VUNLIKELY (transport_type != TransportType::kDds && transport_type != TransportType::kDdsc && + + + + + + ] 228 : : transport_type != TransportType::kDdsr) { 229 [ + - + - ]: 10 : VLOG_W("Function [register_status_handler] is not supported."); 230 : 5 : return; 231 : : } 232 : : 233 [ + - ]: 24 : std::lock_guard lock(helper_->status_mtx); 234 : 24 : helper_->status_callback = std::move(callback); 235 : 24 : } 236 : : 237 : 912 : bool NodeImpl::has_register_status() const { 238 [ + + + + : 912 : if VUNLIKELY (transport_type != TransportType::kDds && transport_type != TransportType::kDdsc && + + + + + + ] 239 : : transport_type != TransportType::kDdsr) { 240 [ + - + - ]: 12 : VLOG_W("Function [has_register_status] is not supported."); 241 : 6 : return false; 242 : : } 243 : : 244 [ + - ]: 906 : std::shared_lock lock(helper_->status_mtx); 245 : : 246 : 906 : return helper_->status_callback != nullptr; 247 : 906 : } 248 : : 249 : 29 : void NodeImpl::call_status(Status::BasePtr ptr) { 250 [ + + - + : 29 : if VUNLIKELY (transport_type != TransportType::kDds && transport_type != TransportType::kDdsc && - + - - - + ] 251 : : transport_type != TransportType::kDdsr) { 252 [ # # # # ]: 0 : VLOG_W("Function [call_status] is not supported."); 253 : 1 : return; 254 : : } 255 : : 256 : : { 257 [ + - ]: 29 : std::lock_guard post_lock(helper_->post_mtx); 258 : : 259 : 29 : auto* message_loop = helper_->message_loop.load(std::memory_order_acquire); 260 : : 261 [ + + ]: 29 : if VLIKELY (message_loop) { 262 [ + - + - ]: 1 : message_loop->post_task([this, ptr]() mutable { 263 [ + - ]: 1 : std::shared_lock lock(helper_->status_mtx); 264 [ + - ]: 1 : if VLIKELY (helper_->status_callback) { 265 [ + - ]: 1 : helper_->status_callback(std::move(ptr)); 266 : : } 267 : 1 : }); 268 : 1 : return; 269 : : } 270 [ + + ]: 29 : } 271 : : 272 [ + - ]: 28 : std::shared_lock lock(helper_->status_mtx); 273 : : 274 [ + - ]: 28 : if VLIKELY (helper_->status_callback) { 275 [ + - ]: 28 : helper_->status_callback(std::move(ptr)); 276 : : } 277 : 28 : } 278 : : 279 : 345 : void NodeImpl::set_property(const std::string& prop, const std::string& value) { 280 [ + - ]: 345 : std::lock_guard lock(helper_->mtx); 281 [ + - + - ]: 345 : helper_->property_map[prop] = value; 282 : 345 : } 283 : : 284 : 244 : std::string NodeImpl::get_property(const std::string& prop) const { 285 [ + - ]: 244 : std::shared_lock lock(helper_->mtx); 286 : : 287 [ + - ]: 244 : auto iter = helper_->property_map.find(prop); 288 : : 289 [ + + ]: 244 : if VLIKELY (iter != helper_->property_map.end()) { 290 [ + - ]: 237 : return iter->second; 291 : : } 292 : : 293 : 7 : return {}; 294 : 244 : } 295 : : 296 : 452 : Conf::PropertiesMap NodeImpl::get_all_properties() const { 297 [ + - ]: 452 : std::shared_lock lock(helper_->mtx); 298 [ + - ]: 904 : return helper_->property_map; 299 : 452 : } 300 : : 301 : 25 : void NodeImpl::set_discovery_enabled(bool enable) { is_discovery_enabled = enable; } 302 : : 303 : 5 : bool NodeImpl::get_discovery_enabled() const { return is_discovery_enabled; } 304 : : 305 : 1 : void NodeImpl::set_record_path(const std::string& path) { 306 : 1 : std::shared_ptr<BagWriter> new_recorder; 307 : : 308 [ - + ]: 1 : if (!path.empty()) { 309 [ # # ]: 0 : new_recorder = BagWriter::filter_get(path); 310 : : } 311 : : 312 : 1 : std::shared_ptr<BagWriter> old_recorder; 313 : : 314 : : { 315 [ + - ]: 1 : std::lock_guard lock(helper_->mtx); 316 : 1 : old_recorder = std::move(helper_->data_recorder); 317 : 1 : helper_->data_recorder = std::move(new_recorder); 318 : 1 : helper_->data_recorder_enabled.store(helper_->data_recorder != nullptr, std::memory_order_release); 319 : 1 : } 320 : : 321 : 1 : old_recorder.reset(); 322 : 1 : } 323 : : 324 : 0 : bool NodeImpl::enable_security(const Security::Config& cfg) { 325 [ # # ]: 0 : auto sec_cfg = cfg; 326 : : 327 [ # # ]: 0 : return enable_security(std::move(sec_cfg)); 328 : 0 : } 329 : : 330 : 47 : bool NodeImpl::enable_security(Security::Config&& cfg) { 331 [ + + + + : 47 : if VUNLIKELY (transport_type == TransportType::kIntra || (transport_type == TransportType::kDds && is_cdr_type)) { - + - + + + ] 332 [ + - + - ]: 6 : VLOG_W("Security::Config will ignore intra/dds(cdr) transport."); 333 : 3 : return false; 334 : : } 335 : : 336 [ + - ]: 44 : if VLIKELY (cfg.advanced.aad_context.empty()) { 337 [ + - ]: 44 : cfg.advanced.aad_context = url; 338 [ + - ]: 44 : cfg.advanced.aad_context += "|"; 339 [ + - ]: 44 : cfg.advanced.aad_context += ser_type; 340 [ + - ]: 44 : cfg.advanced.aad_context += "|"; 341 [ + - + - ]: 44 : cfg.advanced.aad_context += std::to_string(static_cast<uint32_t>(schema_type)); 342 : : } 343 : : 344 [ + - ]: 44 : auto candidate = std::make_unique<Security>(std::move(cfg)); 345 : : 346 [ - + ]: 44 : if VUNLIKELY (!candidate->is_configured()) { 347 : : VLOG_W("Security::Config has no usable slot."); // LCOV_EXCL_LINE GCOVR_EXCL_LINE 348 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE 349 : : } 350 : : 351 [ + + + + ]: 44 : bool needs_encrypt = (impl_type == kPublisher || impl_type == kSetter); 352 [ + + + + ]: 44 : bool needs_decrypt = (impl_type == kSubscriber || impl_type == kGetter); 353 : : 354 : : #if defined(NDEBUG) || defined(__ANDROID__) 355 : : if (impl_type == kClient) { 356 : : needs_encrypt = true; 357 : : const auto* client_impl = static_cast<const ClientImpl*>(this); 358 : : needs_decrypt = client_impl != nullptr && client_impl->is_resp_type; 359 : : } else if (impl_type == kServer) { 360 : : needs_decrypt = true; 361 : : const auto* server_impl = static_cast<const ServerImpl*>(this); 362 : : needs_encrypt = server_impl != nullptr && server_impl->is_resp_type; 363 : : } 364 : : #else 365 [ + + ]: 44 : if (impl_type == kClient) { 366 : 6 : needs_encrypt = true; 367 [ + - ]: 6 : const auto* client_impl = dynamic_cast<const ClientImpl*>(this); 368 [ + - + - ]: 6 : needs_decrypt = client_impl != nullptr && client_impl->is_resp_type; 369 [ + + ]: 38 : } else if (impl_type == kServer) { 370 : 7 : needs_decrypt = true; 371 [ + - ]: 7 : const auto* server_impl = dynamic_cast<const ServerImpl*>(this); 372 [ + - + - ]: 7 : needs_encrypt = server_impl != nullptr && server_impl->is_resp_type; 373 : : } 374 : : #endif 375 : : 376 [ + + - + : 44 : if VUNLIKELY (needs_encrypt && !candidate->can_encrypt()) { - + ] 377 : : VLOG_W("Security::Config cannot encrypt for this sender role."); // LCOV_EXCL_LINE GCOVR_EXCL_LINE 378 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE 379 : : } 380 : : 381 [ + + - + : 44 : if VUNLIKELY (needs_decrypt && !candidate->can_decrypt()) { - + ] 382 : : VLOG_W("Security::Config cannot decrypt for this receiver role."); // LCOV_EXCL_LINE GCOVR_EXCL_LINE 383 : : return false; // LCOV_EXCL_LINE GCOVR_EXCL_LINE 384 : : } 385 : : 386 : 44 : security = std::move(candidate); 387 : : 388 : 44 : return true; 389 : 44 : } 390 : : 391 : 1 : void NodeImpl::set_ssl_options(const SslOptions& options) { 392 [ + - ]: 1 : std::lock_guard lock(helper_->mtx); 393 : 1 : options.parse_to(helper_->property_map); 394 : 1 : } 395 : : 396 : 2620 : void NodeImpl::try_record(ActionType action_type, const Bytes& data) { 397 [ + - ]: 2620 : auto* global_recorder = BagWriter::global_get(); 398 : 2614 : const bool data_recorder_enabled = helper_->data_recorder_enabled.load(std::memory_order_acquire); 399 : : 400 [ + - + - : 2621 : if VLIKELY (!global_recorder && !data_recorder_enabled) { + - ] 401 : 2621 : return; 402 : : } 403 : : 404 : 0 : std::shared_ptr<BagWriter> data_recorder; 405 : : 406 [ # # ]: 0 : if (data_recorder_enabled) { 407 [ # # ]: 0 : std::shared_lock lock(helper_->mtx); 408 : 0 : data_recorder = helper_->data_recorder; 409 : 0 : } 410 : : 411 [ # # # # : 0 : if VLIKELY (!global_recorder && !data_recorder) { # # ] 412 : 0 : return; 413 : : } 414 : : 415 : : if constexpr (kIgnoreIntraUrl) { 416 : : if (transport_type == TransportType::kIntra) { 417 : : return; 418 : : } 419 : : } 420 : : 421 : 0 : Frame frame; 422 : 0 : frame.timestamp = -1; 423 [ # # ]: 0 : frame.url = url; 424 [ # # ]: 0 : frame.ser_type = ser_type; 425 : 0 : frame.schema_type = schema_type; 426 : 0 : frame.action_type = action_type; 427 : 0 : frame.data = Bytes::shallow_copy(data.data(), data.size()); 428 : : 429 [ # # ]: 0 : if VUNLIKELY (global_recorder) { 430 [ # # ]: 0 : global_recorder->push(frame); 431 : : } 432 : : 433 [ # # ]: 0 : if VUNLIKELY (data_recorder) { 434 [ # # ]: 0 : data_recorder->push(frame); 435 : : } 436 [ # # ]: 0 : } 437 : : 438 : 118 : void NodeImpl::reset_interrupted() { helper_->is_interrupted.store(false, std::memory_order_release); } 439 : : 440 : 235 : bool NodeImpl::is_interrupted() const { return helper_->is_interrupted.load(std::memory_order_acquire); } 441 : : 442 : 1023 : void NodeImpl::init_ext() { 443 [ + + ]: 1023 : if (should_report_discovery(*this)) { 444 : 454 : auto* global_reporter = GlobalDiscoveryReporter::get(); 445 : : 446 [ + - ]: 454 : if (global_reporter) { 447 [ - + - - : 454 : if (CpuProfiler::is_global_enabled() && !profiler) { - + ] 448 : : profiler = std::make_unique<CpuProfiler>(); // LCOV_EXCL_LINE GCOVR_EXCL_LINE 449 : : } 450 : : 451 : 454 : global_reporter->add(this); 452 : : } 453 : : } 454 : : 455 : : #ifdef VLINK_ENABLE_CHECK_LICENSE 456 : : [[maybe_unused]] static LicenseCheck license; 457 : : #endif 458 : 1023 : } 459 : : 460 : 1023 : void NodeImpl::deinit_ext() { 461 [ + + ]: 1023 : if (!should_report_discovery(*this)) { 462 : 569 : return; 463 : : } 464 : : 465 : 454 : auto* global_reporter = GlobalDiscoveryReporter::get(false); 466 : : 467 [ + - ]: 454 : if (global_reporter) { 468 : 454 : global_reporter->remove(this); 469 : : 470 [ - + - - : 454 : if (CpuProfiler::is_global_enabled() && profiler) { - + ] 471 : : profiler->restart(); // LCOV_EXCL_LINE GCOVR_EXCL_LINE 472 : : } 473 : : } 474 : : } 475 : : 476 : 1269 : void NodeImpl::global_init() { 477 : 1269 : Logger::get(); 478 : : 479 : 1269 : Bytes::init_memory_pool(); 480 : : 481 : 1269 : BagWriter::global_get(); 482 : : 483 : 1269 : GlobalDiscoveryReporter::get(false); 484 : 1269 : } 485 : : 486 [ + - + - ]: 1266 : NodeImpl::NodeImpl(ImplType type) : impl_type(type), helper_(std::make_unique<NodeImplHelper>()) { global_init(); } 487 : : 488 : 1266 : NodeImpl::~NodeImpl() = default; 489 : : 490 : : } // namespace vlink