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