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