LCOV - code coverage report
Current view: top level - src/impl - url.cc (source / functions) Hit Total Coverage
Test: vlink Lines: 173 192 90.1 %
Date: 2026-06-27 19:56:04 Functions: 29 30 96.7 %
Branches: 174 260 66.9 %

           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/url.h"
      25                 :            : 
      26                 :            : #include <atomic>
      27                 :            : #include <map>
      28                 :            : #include <memory>
      29                 :            : #include <mutex>
      30                 :            : #include <shared_mutex>
      31                 :            : #include <string>
      32                 :            : #include <unordered_map>
      33                 :            : #include <utility>
      34                 :            : 
      35                 :            : #include "./base/helpers.h"
      36                 :            : #include "./base/plugin.h"
      37                 :            : #include "./base/utils.h"
      38                 :            : #include "./extension/url_remap.h"
      39                 :            : #include "./impl/conf_plugin_interface.h"
      40                 :            : #include "./impl/url_parser.h"
      41                 :            : 
      42                 :            : #define VLINK_URL_USE_REMAP 1
      43                 :            : #define VLINK_URL_USE_PLUGIN 1
      44                 :            : 
      45                 :            : namespace vlink {
      46                 :            : 
      47                 :            : [[maybe_unused]] static constexpr uint16_t kMaxUrlLength = 120U;
      48                 :            : 
      49                 :        572 : [[maybe_unused]] inline static TransportType get_transport_for_str(const std::string& str) noexcept {
      50         [ +  + ]:        572 :   if (str == "intra") {
      51                 :        213 :     return TransportType::kIntra;
      52                 :            :   }
      53                 :            : 
      54                 :            : #if !defined(__ANDROID__)
      55                 :            : 
      56         [ +  + ]:        359 :   if (str == "shm") {
      57                 :         96 :     return TransportType::kShm;
      58                 :            :   }
      59                 :            : 
      60         [ +  + ]:        263 :   if (str == "shm2") {
      61                 :          1 :     return TransportType::kShm2;
      62                 :            :   }
      63                 :            : #endif
      64                 :            : 
      65         [ +  + ]:        262 :   if (str == "zenoh") {
      66                 :          1 :     return TransportType::kZenoh;
      67                 :            :   }
      68                 :            : 
      69   [ +  +  -  +  :        261 :   if (str == "dds" || str == "ddsf") {
                   +  + ]
      70                 :        136 :     return TransportType::kDds;
      71                 :            :   }
      72                 :            : 
      73         [ +  + ]:        125 :   if (str == "ddsc") {
      74                 :         91 :     return TransportType::kDdsc;
      75                 :            :   }
      76                 :            : 
      77         [ +  + ]:         34 :   if (str == "ddsr") {
      78                 :          1 :     return TransportType::kDdsr;
      79                 :            :   }
      80                 :            : 
      81         [ +  + ]:         33 :   if (str == "ddst") {
      82                 :          1 :     return TransportType::kDdst;
      83                 :            :   }
      84                 :            : 
      85         [ +  + ]:         32 :   if (str == "someip") {
      86                 :          1 :     return TransportType::kSomeip;
      87                 :            :   }
      88                 :            : 
      89         [ +  + ]:         31 :   if (str == "mqtt") {
      90                 :          1 :     return TransportType::kMqtt;
      91                 :            :   }
      92                 :            : 
      93         [ +  + ]:         30 :   if (str == "fdbus") {
      94                 :          1 :     return TransportType::kFdbus;
      95                 :            :   }
      96                 :            : 
      97                 :            : #if defined(__QNX__)
      98                 :            : 
      99                 :            :   if (str == "qnx") {
     100                 :            :     return TransportType::kQnx;
     101                 :            :   }
     102                 :            : #endif
     103                 :            : 
     104                 :         29 :   return TransportType::kUnknown;
     105                 :            : }
     106                 :            : 
     107                 :          1 : [[maybe_unused]] inline static TransportType get_dds_transport_for_str(const std::string& str) noexcept {
     108   [ +  -  -  +  :          1 :   if (str == "dds" || str == "ddsf") {
                   -  + ]
     109                 :          0 :     return TransportType::kDds;
     110                 :            :   }
     111                 :            : 
     112         [ +  - ]:          1 :   if (str == "ddsc") {
     113                 :          1 :     return TransportType::kDdsc;
     114                 :            :   }
     115                 :            : 
     116         [ #  # ]:          0 :   if (str == "ddsr") {
     117                 :          0 :     return TransportType::kDdsr;
     118                 :            :   }
     119                 :            : 
     120         [ #  # ]:          0 :   if (str == "ddst") {
     121                 :          0 :     return TransportType::kDdst;
     122                 :            :   }
     123                 :            : 
     124                 :          0 :   return TransportType::kUnknown;
     125                 :            : }
     126                 :            : 
     127                 :        571 : [[maybe_unused]] inline static bool is_dds_type(const TransportType& transport) noexcept {
     128   [ +  +  +  +  :        915 :   return transport == TransportType::kDds || transport == TransportType::kDdsc || transport == TransportType::kDdsr ||
                   +  + ]
     129         [ +  + ]:        915 :          transport == TransportType::kDdst;
     130                 :            : }
     131                 :            : 
     132                 :          3 : [[maybe_unused]] inline static Url::TransportEnableFlag get_transport_enable_for_str(const std::string& str) noexcept {
     133         [ +  + ]:          3 :   if (str == "intra") {
     134                 :          1 :     return Url::kEnableIntra;
     135                 :            :   }
     136                 :            : 
     137                 :            : #if !defined(__ANDROID__)
     138                 :            : 
     139         [ -  + ]:          2 :   if (str == "shm") {
     140                 :          0 :     return Url::kEnableShm;
     141                 :            :   }
     142                 :            : 
     143         [ -  + ]:          2 :   if (str == "shm2") {
     144                 :          0 :     return Url::kEnableShm2;
     145                 :            :   }
     146                 :            : #endif
     147                 :            : 
     148         [ -  + ]:          2 :   if (str == "zenoh") {
     149                 :          0 :     return Url::kEnableZenoh;
     150                 :            :   }
     151                 :            : 
     152   [ +  -  -  +  :          2 :   if (str == "dds" || str == "ddsf") {
                   -  + ]
     153                 :          0 :     return Url::kEnableDds;
     154                 :            :   }
     155                 :            : 
     156         [ +  + ]:          2 :   if (str == "ddsc") {
     157                 :          1 :     return Url::kEnableDdsc;
     158                 :            :   }
     159                 :            : 
     160         [ -  + ]:          1 :   if (str == "ddsr") {
     161                 :          0 :     return Url::kEnableDdsr;
     162                 :            :   }
     163                 :            : 
     164         [ -  + ]:          1 :   if (str == "ddst") {
     165                 :          0 :     return Url::kEnableDdst;
     166                 :            :   }
     167                 :            : 
     168         [ -  + ]:          1 :   if (str == "someip") {
     169                 :          0 :     return Url::kEnableSomeip;
     170                 :            :   }
     171                 :            : 
     172         [ -  + ]:          1 :   if (str == "mqtt") {
     173                 :          0 :     return Url::kEnableMqtt;
     174                 :            :   }
     175                 :            : 
     176         [ -  + ]:          1 :   if (str == "fdbus") {
     177                 :          0 :     return Url::kEnableFdbus;
     178                 :            :   }
     179                 :            : 
     180                 :            : #if defined(__QNX__)
     181                 :            : 
     182                 :            :   if (str == "qnx") {
     183                 :            :     return Url::kEnableQnx;
     184                 :            :   }
     185                 :            : #endif
     186                 :            : 
     187                 :          1 :   return Url::kEnableEmpty;
     188                 :            : }
     189                 :            : 
     190                 :         15 : [[maybe_unused]] inline static bool is_intra_url(std::string_view url) noexcept {
     191                 :         15 :   return Helpers::has_startwith(url, "intra://");
     192                 :            : }
     193                 :            : 
     194                 :         15 : [[maybe_unused]] inline static bool is_shm_url(std::string_view url) noexcept {
     195   [ +  +  +  + ]:         15 :   return Helpers::has_startwith(url, "shm://") || Helpers::has_startwith(url, "shm2://");
     196                 :            : }
     197                 :            : 
     198                 :        539 : [[maybe_unused]] inline static int get_sort_index_for_url(std::string_view url) {
     199         [ +  + ]:        539 :   if (url.empty()) {
     200                 :          3 :     return -1;
     201                 :            :   }
     202                 :            : 
     203         [ +  + ]:        536 :   if (Helpers::has_startwith(url, "intra://")) {
     204                 :         30 :     return static_cast<int>(TransportType::kIntra);
     205                 :            :   }
     206                 :            : 
     207                 :            : #if !defined(__ANDROID__)
     208                 :            : 
     209         [ +  + ]:        506 :   if (Helpers::has_startwith(url, "shm://")) {
     210                 :         42 :     return static_cast<int>(TransportType::kShm);
     211                 :            :   }
     212                 :            : 
     213         [ +  + ]:        464 :   if (Helpers::has_startwith(url, "shm2://")) {
     214                 :          2 :     return static_cast<int>(TransportType::kShm2);
     215                 :            :   }
     216                 :            : #endif
     217                 :            : 
     218         [ +  + ]:        462 :   if (Helpers::has_startwith(url, "zenoh://")) {
     219                 :          3 :     return static_cast<int>(TransportType::kZenoh);
     220                 :            :   }
     221                 :            : 
     222   [ +  +  +  +  :        459 :   if (Helpers::has_startwith(url, "dds://") || Helpers::has_startwith(url, "ddsf://")) {
                   +  + ]
     223                 :        423 :     return static_cast<int>(TransportType::kDds);
     224                 :            :   }
     225                 :            : 
     226         [ +  + ]:         36 :   if (Helpers::has_startwith(url, "ddsc://")) {
     227                 :         27 :     return static_cast<int>(TransportType::kDdsc);
     228                 :            :   }
     229                 :            : 
     230         [ +  + ]:          9 :   if (Helpers::has_startwith(url, "ddsr://")) {
     231                 :          1 :     return static_cast<int>(TransportType::kDdsr);
     232                 :            :   }
     233                 :            : 
     234         [ +  + ]:          8 :   if (Helpers::has_startwith(url, "ddst://")) {
     235                 :          1 :     return static_cast<int>(TransportType::kDdst);
     236                 :            :   }
     237                 :            : 
     238         [ +  + ]:          7 :   if (Helpers::has_startwith(url, "someip://")) {
     239                 :          2 :     return static_cast<int>(TransportType::kSomeip);
     240                 :            :   }
     241                 :            : 
     242         [ +  + ]:          5 :   if (Helpers::has_startwith(url, "mqtt://")) {
     243                 :          1 :     return static_cast<int>(TransportType::kMqtt);
     244                 :            :   }
     245                 :            : 
     246         [ +  + ]:          4 :   if (Helpers::has_startwith(url, "fdbus://")) {
     247                 :          2 :     return static_cast<int>(TransportType::kFdbus);
     248                 :            :   }
     249                 :            : 
     250                 :            : #if defined(__QNX__)
     251                 :            : 
     252                 :            :   if (Helpers::has_startwith(url, "qnx://")) {
     253                 :            :     return static_cast<int>(TransportType::kQnx);
     254                 :            :   }
     255                 :            : #endif
     256                 :            : 
     257                 :          2 :   return 0;
     258                 :            : }
     259                 :            : 
     260                 :            : // GlobalModulesManager
     261                 :            : class GlobalModulesManager final {
     262                 :            :  public:
     263                 :         58 :   static GlobalModulesManager& get() {
     264   [ +  +  +  -  :         58 :     static GlobalModulesManager manager;
             +  -  -  - ]
     265                 :         58 :     return manager;
     266                 :            :   }
     267                 :            : 
     268                 :          7 :   std::shared_ptr<ConfPluginInterface> get_interface(TransportType type) const {
     269         [ +  - ]:          7 :     std::shared_lock lock(mtx_);
     270                 :            : 
     271         [ +  - ]:          7 :     auto iter = ptr_map_.find(type);
     272                 :            : 
     273         [ +  - ]:          7 :     if VUNLIKELY (iter == ptr_map_.end()) {
     274                 :          7 :       return nullptr;
     275                 :            :     }
     276                 :            : 
     277                 :            :     return iter->second;  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     278                 :          7 :   }
     279                 :            : 
     280                 :         51 :   void init(uint16_t transport_enable_flags) {
     281                 :         51 :     transport_enable_flags_ = transport_enable_flags;
     282                 :            : 
     283   [ +  -  +  - ]:         51 :     const auto& url_plugins_env = Utils::get_env("VLINK_URL_PLUGINS", "");
     284                 :            : 
     285         [ +  + ]:         51 :     if (url_plugins_env.empty()) {
     286                 :         50 :       return;
     287                 :            :     }
     288                 :            : 
     289                 :          1 :     auto plugins_list = Helpers::split_any(url_plugins_env);
     290                 :            : 
     291         [ +  - ]:          1 :     std::lock_guard lock(mtx_);
     292                 :            : 
     293                 :            :     // plugin_.set_log_level(Logger::kWarn);
     294                 :            : 
     295   [ +  -  +  + ]:          4 :     for (auto libname : plugins_list) {
     296   [ +  -  +  - ]:          3 :       Helpers::replace_string(libname, "vlink-", "");
     297                 :            : 
     298                 :          3 :       auto transport_enable = get_transport_enable_for_str(libname);
     299                 :            : 
     300         [ +  + ]:          3 :       if VUNLIKELY (transport_enable == Url::TransportEnableFlag::kEnableEmpty) {
     301   [ +  -  +  - ]:          2 :         VLOG_E("Unsupported plugin module, libname: ", libname, ".");
     302                 :          1 :         continue;
     303                 :          1 :       }
     304                 :            : 
     305         [ +  + ]:          2 :       if (transport_enable_flags_ & transport_enable) {
     306   [ +  -  +  - ]:          2 :         VLOG_T("Ignore linked modules, libname: ", libname, ".");
     307                 :          1 :         continue;
     308                 :          1 :       }
     309                 :            : 
     310   [ +  -  +  -  :          2 :       auto ptr = plugin_.load<ConfPluginInterface>("vlink-" + libname, 1, 0);
          +  -  +  -  +  
                      - ]
     311                 :            : 
     312         [ +  - ]:          1 :       if VLIKELY (ptr) {
     313   [ +  -  +  - ]:          1 :         ptr_map_.emplace(ptr->get_transport_type(), std::move(ptr));
     314                 :            :       }
     315         [ +  + ]:          3 :     }
     316         [ +  + ]:         51 :   }
     317                 :            : 
     318                 :            :  private:
     319                 :         51 :   GlobalModulesManager() = default;
     320                 :            : 
     321                 :         51 :   ~GlobalModulesManager() {
     322                 :         51 :     std::lock_guard lock(mtx_);
     323                 :         51 :     ptr_map_.clear();
     324                 :         51 :     plugin_.clear();
     325                 :         51 :   }
     326                 :            : 
     327                 :            :   uint16_t transport_enable_flags_{0};
     328                 :            :   Plugin plugin_;
     329                 :            :   std::unordered_map<TransportType, std::shared_ptr<ConfPluginInterface>> ptr_map_;
     330                 :            :   mutable std::shared_mutex mtx_;
     331                 :            : 
     332                 :            :   VLINK_DISALLOW_COPY_AND_ASSIGN(GlobalModulesManager)
     333                 :            : };
     334                 :            : 
     335                 :            : // GlobalUrlRemap
     336                 :            : class GlobalUrlRemap final : public UrlRemap {
     337                 :            :  public:
     338                 :        574 :   static GlobalUrlRemap& get() {
     339   [ +  +  +  -  :        574 :     static GlobalUrlRemap remap;
             +  -  -  - ]
     340                 :        574 :     return remap;
     341                 :            :   }
     342                 :            : 
     343                 :        574 :   std::string convert_thread_safe(const std::string& url) noexcept {
     344                 :        574 :     std::lock_guard lock(mtx_);
     345                 :        574 :     return convert(url);
     346                 :        574 :   }
     347                 :            : 
     348                 :            :  private:
     349                 :         50 :   GlobalUrlRemap() {
     350   [ +  -  +  - ]:         50 :     const auto& url_remap_env = Utils::get_env("VLINK_URL_REMAP", "");
     351                 :            : 
     352         [ +  - ]:         50 :     if (url_remap_env.empty()) {
     353                 :         50 :       return;
     354                 :            :     }
     355                 :            : 
     356                 :          0 :     set_enable_log(true);
     357                 :            : 
     358                 :          0 :     load(url_remap_env);
     359         [ -  + ]:         50 :   }
     360                 :            : 
     361                 :         50 :   ~GlobalUrlRemap() = default;
     362                 :            : 
     363                 :            :   std::mutex mtx_;
     364                 :            : 
     365                 :            :   VLINK_DISALLOW_COPY_AND_ASSIGN(GlobalUrlRemap)
     366                 :            : };
     367                 :            : 
     368                 :            : // Protocol
     369                 :        574 : Protocol::Protocol(const std::string& address) {
     370         [ -  + ]:        574 :   if VUNLIKELY (address.length() > kMaxUrlLength) {
     371   [ #  #  #  # ]:          0 :     CLOG_F("The URL length exceeds the character limit (Target length: %zu, Max length: %u).", address.length(),
     372                 :            :            kMaxUrlLength);
     373                 :          0 :     return;
     374                 :            :   }
     375                 :            : 
     376                 :            : #if VLINK_URL_USE_REMAP
     377         [ +  - ]:        574 :   std::string real_address = GlobalUrlRemap::get().convert_thread_safe(address);
     378                 :            : 
     379         [ +  + ]:        574 :   UrlParser parser(real_address);
     380                 :        571 :   str = std::move(real_address);
     381                 :            : #else
     382                 :            :   UrlParser parser(address);
     383                 :            :   str = address;
     384                 :            : #endif
     385                 :            : 
     386         [ +  - ]:        571 :   transport = get_transport_for_str(parser.get_transport());
     387                 :            : 
     388         [ +  + ]:        571 :   if (is_dds_type(transport)) {
     389   [ +  -  +  - ]:        456 :     const auto dds_bind_transport_str = Utils::get_env("VLINK_DDS_BIND", "");
     390                 :            : 
     391         [ +  + ]:        228 :     if VUNLIKELY (!dds_bind_transport_str.empty()) {
     392                 :          1 :       TransportType dds_bind_transport = get_dds_transport_for_str(dds_bind_transport_str);
     393                 :            : 
     394         [ +  - ]:          1 :       if VLIKELY (dds_bind_transport != TransportType::kUnknown) {
     395                 :            :         static std::atomic_bool dds_bind_print{false};
     396                 :            : 
     397         [ +  - ]:          1 :         if (!dds_bind_print.exchange(true, std::memory_order_relaxed)) {
     398   [ +  -  +  - ]:          2 :           CLOG_I("Bind [dds] to [%s].", dds_bind_transport_str.c_str());
     399                 :            :         }
     400                 :            : 
     401                 :          1 :         transport = dds_bind_transport;
     402                 :            :       }
     403                 :            :     }
     404         [ +  + ]:        571 :   } else if (transport == TransportType::kIntra) {
     405   [ +  -  +  - ]:        426 :     const auto intra_bind_transport_str = Utils::get_env("VLINK_INTRA_BIND", "");
     406                 :            : 
     407         [ +  + ]:        213 :     if (!intra_bind_transport_str.empty()) {
     408                 :          1 :       TransportType intra_bind_transport = get_transport_for_str(intra_bind_transport_str);
     409                 :            : 
     410   [ +  -  +  -  :          1 :       if VLIKELY (intra_bind_transport != TransportType::kIntra && intra_bind_transport != TransportType::kUnknown) {
                   +  - ]
     411                 :            :         static std::atomic_bool intra_bind_print{false};
     412                 :            : 
     413         [ +  - ]:          1 :         if (!intra_bind_print.exchange(true, std::memory_order_relaxed)) {
     414   [ +  -  +  - ]:          2 :           CLOG_I("Bind [intra] to [%s].", intra_bind_transport_str.c_str());
     415                 :            :         }
     416                 :            : 
     417                 :          1 :         transport = intra_bind_transport;
     418                 :            :       }
     419                 :            :     }
     420                 :        213 :   }
     421                 :            : 
     422         [ +  - ]:        571 :   host = std::move(const_cast<std::string&>(parser.get_host()));
     423         [ +  - ]:        571 :   path = std::move(const_cast<std::string&>(parser.get_path()));
     424         [ +  - ]:        571 :   dictionary = std::move(const_cast<std::map<std::string, std::string>&>(parser.get_query_dictionary()));
     425         [ +  - ]:        571 :   fragment = std::move(const_cast<std::string&>(parser.get_fragment()));
     426                 :        589 : }
     427                 :            : 
     428                 :            : // Url
     429                 :        574 : void Url::init_plugins(uint16_t transport_enable_flags) {
     430                 :            : #if VLINK_URL_USE_PLUGIN
     431   [ +  +  +  -  :        574 :   static auto& manager_instance = GlobalModulesManager::get();
             +  -  -  - ]
     432                 :            : 
     433                 :            :   static std::once_flag flag;
     434                 :            : 
     435         [ +  - ]:        625 :   std::call_once(flag, [transport_enable_flags]() { manager_instance.init(transport_enable_flags); });
     436                 :            : #else
     437                 :            :   (void)transport_enable_flags;
     438                 :            : #endif
     439                 :        574 : }
     440                 :            : 
     441                 :         38 : std::unique_ptr<Conf> Url::load_for_plugin(TransportType type) {
     442                 :            : #if VLINK_URL_USE_PLUGIN
     443                 :            : 
     444         [ +  + ]:         38 :   if VUNLIKELY (type == TransportType::kUnknown) {
     445                 :         31 :     return nullptr;
     446                 :            :   }
     447                 :            : 
     448   [ +  -  +  - ]:          7 :   auto ptr = GlobalModulesManager::get().get_interface(type);
     449                 :            : 
     450         [ +  - ]:          7 :   if VUNLIKELY (!ptr) {
     451                 :          7 :     return nullptr;
     452                 :            :   }
     453                 :            : 
     454                 :            :   return ptr->create();  // LCOV_EXCL_LINE GCOVR_EXCL_LINE
     455                 :            : #else
     456                 :            :   (void)type;
     457                 :            : 
     458                 :            :   return nullptr;
     459                 :            : #endif
     460                 :          7 : }
     461                 :            : 
     462                 :        539 : int Url::get_sort_index(std::string_view url) { return get_sort_index_for_url(url); }
     463                 :            : 
     464   [ +  +  +  + ]:         10 : bool Url::is_local_type(std::string_view url) { return is_intra_url(url) || is_shm_url(url); }
     465                 :            : 
     466                 :          5 : bool Url::is_intra_type(std::string_view url) { return is_intra_url(url); }
     467                 :            : 
     468                 :          6 : bool Url::is_shm_type(std::string_view url) { return is_shm_url(url); }
     469                 :            : 
     470                 :          2 : std::ostream& operator<<(std::ostream& ostream, const Url& conf) noexcept {
     471                 :            :   ostream << "Url:"
     472                 :          2 :           << "[type]" << +conf.get_impl_type() << "[str]" << conf.protocol_.str;
     473                 :            : 
     474                 :          2 :   return ostream;
     475                 :            : }
     476                 :            : 
     477                 :            : }  // namespace vlink

Generated by: LCOV version 1.14