include/boost/corosio/endpoint.hpp

98.5% Lines (65/0/66) 100.0% List of functions (13/0/13)
endpoint.hpp
f(x) Functions (13)
Line TLA Hits 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 225262x endpoint() noexcept
70 225262x : v4_address_(ipv4_address::any())
71 225262x , v6_address_{}
72 225262x , port_(0)
73 225262x , is_v4_(true)
74 {
75 225262x }
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 22184x endpoint(ipv4_address addr, std::uint16_t p) noexcept
83 22184x : v4_address_(addr)
84 22184x , v6_address_{}
85 22184x , port_(p)
86 22184x , is_v4_(true)
87 {
88 22184x }
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 152x endpoint(ipv6_address addr, std::uint16_t p) noexcept
96 152x : v4_address_(ipv4_address::any())
97 152x , v6_address_(addr)
98 152x , port_(p)
99 152x , is_v4_(false)
100 {
101 152x }
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 20x explicit endpoint(std::uint16_t p) noexcept
111 20x : v4_address_(ipv4_address::any())
112 20x , v6_address_{}
113 20x , port_(p)
114 20x , is_v4_(true)
115 {
116 20x }
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 2x endpoint(endpoint const& ep, std::uint16_t p) noexcept
127 2x : v4_address_(ep.v4_address_)
128 2x , v6_address_(ep.v6_address_)
129 2x , port_(p)
130 2x , is_v4_(ep.is_v4_)
131 {
132 2x }
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 14967x bool is_v4() const noexcept
155 {
156 14967x 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 174x bool is_v6() const noexcept
164 {
165 174x 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 7652x ipv4_address v4_address() const noexcept
174 {
175 7652x 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 64x ipv6_address v6_address() const noexcept
184 {
185 64x return v6_address_;
186 }
187
188 /** Get the port number.
189
190 @return The port number in host byte order.
191 */
192 8152x std::uint16_t port() const noexcept
193 {
194 8152x 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 93x friend bool operator==(endpoint const& a, endpoint const& b) noexcept
205 {
206 93x if (a.is_v4_ != b.is_v4_)
207 1x return false;
208 92x if (a.port_ != b.port_)
209 3x return false;
210 89x if (a.is_v4_)
211 89x return a.v4_address_ == b.v4_address_;
212 else
213 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 25x operator<=>(endpoint const& a, endpoint const& b) noexcept
229 {
230 25x if (a.is_v4_ != b.is_v4_)
231 9x return a.is_v4_ ? std::strong_ordering::less
232 9x : std::strong_ordering::greater;
233 16x if (a.is_v4_)
234 {
235 13x if (auto c = a.v4_address_.to_uint() <=> b.v4_address_.to_uint();
236 13x c != 0)
237 2x return c;
238 }
239 else
240 {
241 3x if (auto c = a.v6_address_.to_bytes() <=> b.v6_address_.to_bytes();
242 3x c != 0)
243 1x return c;
244 }
245 13x 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 25x inline endpoint::endpoint(std::string_view s)
309 {
310 25x auto [ec, ep] = make_endpoint(s);
311 25x if (ec)
312 16x detail::throw_system_error(ec);
313 9x *this = ep;
314 9x }
315
316 } // namespace boost::corosio
317
318 #endif
319