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