libs/corosio/include/boost/corosio/endpoint.hpp

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