LCOV - code coverage report
Current view: top level - corosio - endpoint.hpp (source / functions) Coverage Total Hit Missed
Test: coverage_remapped.info Lines: 98.5 % 66 65 1
Test Date: 2026-08-21 20:48:07 Functions: 100.0 % 13 13

           TLA  Line data    Source code
       1                 : //
       2                 : // Copyright (c) 2026 Vinnie Falco (vinnie.falco@gmail.com)
       3                 : //
       4                 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
       5                 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
       6                 : //
       7                 : // Official repository: https://github.com/cppalliance/corosio
       8                 : //
       9                 : 
      10                 : #ifndef BOOST_COROSIO_ENDPOINT_HPP
      11                 : #define BOOST_COROSIO_ENDPOINT_HPP
      12                 : 
      13                 : #include <boost/corosio/detail/config.hpp>
      14                 : #include <boost/corosio/detail/except.hpp>
      15                 : #include <boost/corosio/ipv4_address.hpp>
      16                 : #include <boost/corosio/ipv6_address.hpp>
      17                 : 
      18                 : #include <boost/capy/io_result.hpp>
      19                 : 
      20                 : #include <compare>
      21                 : #include <cstdint>
      22                 : #include <string_view>
      23                 : #include <system_error>
      24                 : 
      25                 : namespace boost::corosio {
      26                 : 
      27                 : /** An IP endpoint (address + port) supporting both IPv4 and IPv6.
      28                 : 
      29                 :     This class represents an endpoint for IP communication,
      30                 :     consisting of either an IPv4 or IPv6 address and a port number.
      31                 :     Endpoints are used to specify connection targets and bind addresses.
      32                 : 
      33                 :     The endpoint holds both address types as separate members (not a union),
      34                 :     with a discriminator to track which address type is active.
      35                 : 
      36                 :     @par Thread Safety
      37                 :     Distinct objects: Safe.@n
      38                 :     Shared objects: Safe.
      39                 : 
      40                 :     @par Example
      41                 :     @code
      42                 :     // IPv4 endpoint
      43                 :     endpoint ep4(ipv4_address::loopback(), 8080);
      44                 : 
      45                 :     // IPv6 endpoint
      46                 :     endpoint ep6(ipv6_address::loopback(), 8080);
      47                 : 
      48                 :     // Port only (defaults to IPv4 any address)
      49                 :     endpoint bind_addr(8080);
      50                 : 
      51                 :     // Create from string
      52                 :     auto [ec, ep] = make_endpoint("192.168.1.1:8080");
      53                 :     if (ec)
      54                 :         return;
      55                 :     @endcode
      56                 : */
      57                 : class endpoint
      58                 : {
      59                 :     ipv4_address v4_address_;
      60                 :     ipv6_address v6_address_;
      61                 :     std::uint16_t port_ = 0;
      62                 :     bool is_v4_         = true;
      63                 : 
      64                 : public:
      65                 :     /** Default constructor.
      66                 : 
      67                 :         Creates an endpoint with the IPv4 any address (0.0.0.0) and port 0.
      68                 :     */
      69 HIT      225262 :     endpoint() noexcept
      70          225262 :         : v4_address_(ipv4_address::any())
      71          225262 :         , v6_address_{}
      72          225262 :         , port_(0)
      73          225262 :         , is_v4_(true)
      74                 :     {
      75          225262 :     }
      76                 : 
      77                 :     /** Construct from IPv4 address and port.
      78                 : 
      79                 :         @param addr The IPv4 address.
      80                 :         @param p The port number in host byte order.
      81                 :     */
      82           22184 :     endpoint(ipv4_address addr, std::uint16_t p) noexcept
      83           22184 :         : v4_address_(addr)
      84           22184 :         , v6_address_{}
      85           22184 :         , port_(p)
      86           22184 :         , is_v4_(true)
      87                 :     {
      88           22184 :     }
      89                 : 
      90                 :     /** Construct from IPv6 address and port.
      91                 : 
      92                 :         @param addr The IPv6 address.
      93                 :         @param p The port number in host byte order.
      94                 :     */
      95             152 :     endpoint(ipv6_address addr, std::uint16_t p) noexcept
      96             152 :         : v4_address_(ipv4_address::any())
      97             152 :         , v6_address_(addr)
      98             152 :         , port_(p)
      99             152 :         , is_v4_(false)
     100                 :     {
     101             152 :     }
     102                 : 
     103                 :     /** Construct from port only.
     104                 : 
     105                 :         Uses the IPv4 any address (0.0.0.0), which binds to all
     106                 :         available network interfaces.
     107                 : 
     108                 :         @param p The port number in host byte order.
     109                 :     */
     110              20 :     explicit endpoint(std::uint16_t p) noexcept
     111              20 :         : v4_address_(ipv4_address::any())
     112              20 :         , v6_address_{}
     113              20 :         , port_(p)
     114              20 :         , is_v4_(true)
     115                 :     {
     116              20 :     }
     117                 : 
     118                 :     /** Construct from an endpoint's address with a different port.
     119                 : 
     120                 :         Creates a new endpoint using the address from an existing
     121                 :         endpoint but with a different port number.
     122                 : 
     123                 :         @param ep The endpoint whose address to use.
     124                 :         @param p The port number in host byte order.
     125                 :     */
     126               2 :     endpoint(endpoint const& ep, std::uint16_t p) noexcept
     127               2 :         : v4_address_(ep.v4_address_)
     128               2 :         , v6_address_(ep.v6_address_)
     129               2 :         , port_(p)
     130               2 :         , is_v4_(ep.is_v4_)
     131                 :     {
     132               2 :     }
     133                 : 
     134                 :     /** Construct from a string.
     135                 : 
     136                 :         Parses an endpoint string in one of the following formats:
     137                 :         @li IPv4 without port: `192.168.1.1`
     138                 :         @li IPv4 with port: `192.168.1.1:8080`
     139                 :         @li IPv6 without port: `::1` or `2001:db8::1`
     140                 :         @li IPv6 with port (bracketed): `[::1]:8080`
     141                 : 
     142                 :         @param s The string to parse.
     143                 : 
     144                 :         @throws std::system_error on parse failure.
     145                 : 
     146                 :         @see make_endpoint for the non-throwing form.
     147                 :     */
     148                 :     explicit endpoint(std::string_view s);
     149                 : 
     150                 :     /** Check if this endpoint uses an IPv4 address.
     151                 : 
     152                 :         @return `true` if the endpoint uses IPv4, `false` if IPv6.
     153                 :     */
     154           14967 :     bool is_v4() const noexcept
     155                 :     {
     156           14967 :         return is_v4_;
     157                 :     }
     158                 : 
     159                 :     /** Check if this endpoint uses an IPv6 address.
     160                 : 
     161                 :         @return `true` if the endpoint uses IPv6, `false` if IPv4.
     162                 :     */
     163             174 :     bool is_v6() const noexcept
     164                 :     {
     165             174 :         return !is_v4_;
     166                 :     }
     167                 : 
     168                 :     /** Get the IPv4 address.
     169                 : 
     170                 :         @return The IPv4 address. The value is valid even if
     171                 :         the endpoint is using IPv6 (it will be the default any address).
     172                 :     */
     173            7652 :     ipv4_address v4_address() const noexcept
     174                 :     {
     175            7652 :         return v4_address_;
     176                 :     }
     177                 : 
     178                 :     /** Get the IPv6 address.
     179                 : 
     180                 :         @return The IPv6 address. The value is valid even if
     181                 :         the endpoint is using IPv4 (it will be the default any address).
     182                 :     */
     183              64 :     ipv6_address v6_address() const noexcept
     184                 :     {
     185              64 :         return v6_address_;
     186                 :     }
     187                 : 
     188                 :     /** Get the port number.
     189                 : 
     190                 :         @return The port number in host byte order.
     191                 :     */
     192            8152 :     std::uint16_t port() const noexcept
     193                 :     {
     194            8152 :         return port_;
     195                 :     }
     196                 : 
     197                 :     /** Compare endpoints for equality.
     198                 : 
     199                 :         Two endpoints are equal if they have the same address type,
     200                 :         the same address value, and the same port.
     201                 : 
     202                 :         @return `true` if both endpoints are equal.
     203                 :     */
     204              93 :     friend bool operator==(endpoint const& a, endpoint const& b) noexcept
     205                 :     {
     206              93 :         if (a.is_v4_ != b.is_v4_)
     207               1 :             return false;
     208              92 :         if (a.port_ != b.port_)
     209               3 :             return false;
     210              89 :         if (a.is_v4_)
     211              89 :             return a.v4_address_ == b.v4_address_;
     212                 :         else
     213 MIS           0 :             return a.v6_address_ == b.v6_address_;
     214                 :     }
     215                 : 
     216                 :     /** Order two endpoints.
     217                 : 
     218                 :         Establishes a strict total ordering consistent with
     219                 :         @ref operator==: equal endpoints compare equivalent.
     220                 :         Endpoints are ordered first by address family (IPv4
     221                 :         before IPv6), then by address value, then by port. This
     222                 :         makes `endpoint` usable as a key in ordered containers
     223                 :         such as `std::map` and `std::set`.
     224                 : 
     225                 :         @return The relative order of @p a and @p b.
     226                 :     */
     227                 :     friend std::strong_ordering
     228 HIT          25 :     operator<=>(endpoint const& a, endpoint const& b) noexcept
     229                 :     {
     230              25 :         if (a.is_v4_ != b.is_v4_)
     231               9 :             return a.is_v4_ ? std::strong_ordering::less
     232               9 :                             : std::strong_ordering::greater;
     233              16 :         if (a.is_v4_)
     234                 :         {
     235              13 :             if (auto c = a.v4_address_.to_uint() <=> b.v4_address_.to_uint();
     236              13 :                 c != 0)
     237               2 :                 return c;
     238                 :         }
     239                 :         else
     240                 :         {
     241               3 :             if (auto c = a.v6_address_.to_bytes() <=> b.v6_address_.to_bytes();
     242               3 :                 c != 0)
     243               1 :                 return c;
     244                 :         }
     245              13 :         return a.port_ <=> b.port_;
     246                 :     }
     247                 : };
     248                 : 
     249                 : /** Endpoint format detection result.
     250                 : 
     251                 :     Used internally by make_endpoint to determine
     252                 :     the format of an endpoint string.
     253                 : */
     254                 : enum class endpoint_format
     255                 : {
     256                 :     ipv4_no_port,   ///< "192.168.1.1"
     257                 :     ipv4_with_port, ///< "192.168.1.1:8080"
     258                 :     ipv6_no_port,   ///< "::1" or "1:2:3:4:5:6:7:8"
     259                 :     ipv6_bracketed  ///< "[::1]" or "[::1]:8080"
     260                 : };
     261                 : 
     262                 : /** Detect the format of an endpoint string.
     263                 : 
     264                 :     This helper function determines the endpoint format
     265                 :     based on simple rules:
     266                 :     1. Starts with `[` -> `ipv6_bracketed`
     267                 :     2. Else count `:` characters:
     268                 :        - 0 colons -> `ipv4_no_port`
     269                 :        - 1 colon -> `ipv4_with_port`
     270                 :        - 2+ colons -> `ipv6_no_port`
     271                 : 
     272                 :     @param s The string to analyze.
     273                 :     @return The detected endpoint format.
     274                 : */
     275                 : BOOST_COROSIO_DECL
     276                 : endpoint_format detect_endpoint_format(std::string_view s) noexcept;
     277                 : 
     278                 : /** Create an endpoint from a string.
     279                 : 
     280                 :     This function parses an endpoint string in one of
     281                 :     the following formats:
     282                 : 
     283                 :     @li IPv4 without port: `192.168.1.1`
     284                 :     @li IPv4 with port: `192.168.1.1:8080`
     285                 :     @li IPv6 without port: `::1` or `2001:db8::1`
     286                 :     @li IPv6 with port (bracketed): `[::1]:8080`
     287                 : 
     288                 :     @par Example
     289                 :     @code
     290                 :     auto [ec, ep] = make_endpoint("192.168.1.1:8080");
     291                 :     if (ec)
     292                 :         return;
     293                 :     assert( ep.is_v4() && ep.port() == 8080 );
     294                 : 
     295                 :     auto [ec6, ep6] = make_endpoint("[::1]:443");
     296                 :     if (ec6)
     297                 :         return;
     298                 :     assert( ep6.is_v6() && ep6.port() == 443 );
     299                 :     @endcode
     300                 : 
     301                 :     @param s The string to parse.
     302                 :     @return The error code, empty on success, and the parsed
     303                 :         endpoint — default-constructed on failure.
     304                 : */
     305                 : [[nodiscard]] BOOST_COROSIO_DECL capy::io_result<endpoint>
     306                 : make_endpoint(std::string_view s) noexcept;
     307                 : 
     308              25 : inline endpoint::endpoint(std::string_view s)
     309                 : {
     310              25 :     auto [ec, ep] = make_endpoint(s);
     311              25 :     if (ec)
     312              16 :         detail::throw_system_error(ec);
     313               9 :     *this = ep;
     314               9 : }
     315                 : 
     316                 : } // namespace boost::corosio
     317                 : 
     318                 : #endif
        

Generated by: LCOV version 2.3