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