TLA Line data Source code
1 : //
2 : // Copyright (c) 2026 Steve Gerbino
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_SOCKET_OPTION_HPP
11 : #define BOOST_COROSIO_SOCKET_OPTION_HPP
12 :
13 : #include <boost/corosio/detail/config.hpp>
14 :
15 : #include <cstddef>
16 :
17 : /** @file socket_option.hpp
18 :
19 : Type-erased socket option types that avoid platform-specific
20 : headers. The protocol level and option name for each type are
21 : resolved at link time via the compiled library.
22 :
23 : For an inline (zero-overhead) alternative that includes platform
24 : headers, use `<boost/corosio/native/native_socket_option.hpp>`
25 : (`boost::corosio::native_socket_option`).
26 :
27 : Both variants satisfy the same option-type interface and work
28 : interchangeably with `tcp_socket::set_option` /
29 : `tcp_socket::get_option` and the corresponding acceptor methods.
30 :
31 : @see native_socket_option
32 : */
33 :
34 : namespace boost::corosio::socket_option {
35 :
36 : /** Base class for concrete boolean socket options.
37 :
38 : Stores a boolean as an `int` suitable for `setsockopt`/`getsockopt`.
39 : Derived types provide `level()` and `name()` for the specific option.
40 : */
41 : class boolean_option
42 : {
43 : int value_ = 0;
44 :
45 : public:
46 : /// Construct with default value (disabled).
47 : boolean_option() = default;
48 :
49 : /** Construct with an explicit value.
50 :
51 : @param v `true` to enable the option, `false` to disable.
52 : */
53 HIT 169 : explicit boolean_option(bool v) noexcept : value_(v ? 1 : 0) {}
54 :
55 : /// Assign a new value.
56 4 : boolean_option& operator=(bool v) noexcept
57 : {
58 4 : value_ = v ? 1 : 0;
59 4 : return *this;
60 : }
61 :
62 : /// Return the option value.
63 34 : bool value() const noexcept
64 : {
65 34 : return value_ != 0;
66 : }
67 :
68 : /// Return the option value.
69 4 : explicit operator bool() const noexcept
70 : {
71 4 : return value_ != 0;
72 : }
73 :
74 : /// Return the negated option value.
75 4 : bool operator!() const noexcept
76 : {
77 4 : return value_ == 0;
78 : }
79 :
80 : /// Return a pointer to the underlying storage.
81 36 : void* data() noexcept
82 : {
83 36 : return &value_;
84 : }
85 :
86 : /// Return a pointer to the underlying storage.
87 171 : void const* data() const noexcept
88 : {
89 171 : return &value_;
90 : }
91 :
92 : /// Return the size of the underlying storage.
93 207 : std::size_t size() const noexcept
94 : {
95 207 : return sizeof(value_);
96 : }
97 :
98 : /** Normalize after `getsockopt` returns fewer bytes than expected.
99 :
100 : Windows Vista+ may write only 1 byte for boolean options.
101 :
102 : @param s The number of bytes actually written by `getsockopt`.
103 : */
104 36 : void resize(std::size_t s) noexcept
105 : {
106 36 : if (s == sizeof(char))
107 MIS 0 : value_ = *reinterpret_cast<unsigned char*>(&value_) ? 1 : 0;
108 HIT 36 : }
109 : };
110 :
111 : /** Base class for concrete integer socket options.
112 :
113 : Stores an integer suitable for `setsockopt`/`getsockopt`.
114 : Derived types provide `level()` and `name()` for the specific option.
115 : */
116 : class integer_option
117 : {
118 : int value_ = 0;
119 :
120 : public:
121 : /// Construct with default value (zero).
122 : integer_option() = default;
123 :
124 : /** Construct with an explicit value.
125 :
126 : @param v The option value.
127 : */
128 8 : explicit integer_option(int v) noexcept : value_(v) {}
129 :
130 : /// Assign a new value.
131 2 : integer_option& operator=(int v) noexcept
132 : {
133 2 : value_ = v;
134 2 : return *this;
135 : }
136 :
137 : /// Return the option value.
138 18 : int value() const noexcept
139 : {
140 18 : return value_;
141 : }
142 :
143 : /// Return a pointer to the underlying storage.
144 16 : void* data() noexcept
145 : {
146 16 : return &value_;
147 : }
148 :
149 : /// Return a pointer to the underlying storage.
150 8 : void const* data() const noexcept
151 : {
152 8 : return &value_;
153 : }
154 :
155 : /// Return the size of the underlying storage.
156 24 : std::size_t size() const noexcept
157 : {
158 24 : return sizeof(value_);
159 : }
160 :
161 : /** Normalize after `getsockopt` returns fewer bytes than expected.
162 :
163 : @param s The number of bytes actually written by `getsockopt`.
164 : */
165 16 : void resize(std::size_t s) noexcept
166 : {
167 16 : if (s == sizeof(char))
168 MIS 0 : value_ =
169 0 : static_cast<int>(*reinterpret_cast<unsigned char*>(&value_));
170 HIT 16 : }
171 : };
172 :
173 : /** Disable Nagle's algorithm (TCP_NODELAY).
174 :
175 : @par Example
176 : @code
177 : sock.set_option( socket_option::no_delay( true ) );
178 : auto nd = sock.get_option<socket_option::no_delay>();
179 : if ( nd.value() )
180 : // Nagle's algorithm is disabled
181 : @endcode
182 : */
183 : class BOOST_COROSIO_DECL no_delay : public boolean_option
184 : {
185 : public:
186 : using boolean_option::boolean_option;
187 : using boolean_option::operator=;
188 :
189 : /// Return the protocol level.
190 : static int level() noexcept;
191 :
192 : /// Return the option name.
193 : static int name() noexcept;
194 : };
195 :
196 : /** Enable periodic keepalive probes (SO_KEEPALIVE).
197 :
198 : @par Example
199 : @code
200 : sock.set_option( socket_option::keep_alive( true ) );
201 : @endcode
202 : */
203 : class BOOST_COROSIO_DECL keep_alive : public boolean_option
204 : {
205 : public:
206 : using boolean_option::boolean_option;
207 : using boolean_option::operator=;
208 :
209 : /// Return the protocol level.
210 : static int level() noexcept;
211 :
212 : /// Return the option name.
213 : static int name() noexcept;
214 : };
215 :
216 : /** Restrict an IPv6 socket to IPv6 only (IPV6_V6ONLY).
217 :
218 : When enabled, the socket only accepts IPv6 connections.
219 : When disabled, the socket accepts both IPv4 and IPv6
220 : connections (dual-stack mode).
221 :
222 : @par Example
223 : @code
224 : sock.set_option( socket_option::v6_only( true ) );
225 : @endcode
226 : */
227 : class BOOST_COROSIO_DECL v6_only : public boolean_option
228 : {
229 : public:
230 : using boolean_option::boolean_option;
231 : using boolean_option::operator=;
232 :
233 : /// Return the protocol level.
234 : static int level() noexcept;
235 :
236 : /// Return the option name.
237 : static int name() noexcept;
238 : };
239 :
240 : /** Allow local address reuse (SO_REUSEADDR).
241 :
242 : @par Example
243 : @code
244 : acc.set_option( socket_option::reuse_address( true ) );
245 : @endcode
246 : */
247 : class BOOST_COROSIO_DECL reuse_address : public boolean_option
248 : {
249 : public:
250 : using boolean_option::boolean_option;
251 : using boolean_option::operator=;
252 :
253 : /// Return the protocol level.
254 : static int level() noexcept;
255 :
256 : /// Return the option name.
257 : static int name() noexcept;
258 : };
259 :
260 : /** Allow multiple sockets to bind to the same port (SO_REUSEPORT).
261 :
262 : Not available on all platforms. On unsupported platforms,
263 : `set_option` will return an error.
264 :
265 : @par Example
266 : @code
267 : acc.open( tcp::v6() );
268 : acc.set_option( socket_option::reuse_port( true ) );
269 : acc.bind( endpoint( ipv6_address::any(), 8080 ) );
270 : acc.listen();
271 : @endcode
272 : */
273 : class BOOST_COROSIO_DECL reuse_port : public boolean_option
274 : {
275 : public:
276 : using boolean_option::boolean_option;
277 : using boolean_option::operator=;
278 :
279 : /// Return the protocol level.
280 : static int level() noexcept;
281 :
282 : /// Return the option name.
283 : static int name() noexcept;
284 : };
285 :
286 : /** Set the receive buffer size (SO_RCVBUF).
287 :
288 : @par Example
289 : @code
290 : sock.set_option( socket_option::receive_buffer_size( 65536 ) );
291 : auto opt = sock.get_option<socket_option::receive_buffer_size>();
292 : int sz = opt.value();
293 : @endcode
294 : */
295 : class BOOST_COROSIO_DECL receive_buffer_size : public integer_option
296 : {
297 : public:
298 : using integer_option::integer_option;
299 : using integer_option::operator=;
300 :
301 : /// Return the protocol level.
302 : static int level() noexcept;
303 :
304 : /// Return the option name.
305 : static int name() noexcept;
306 : };
307 :
308 : /** Set the send buffer size (SO_SNDBUF).
309 :
310 : @par Example
311 : @code
312 : sock.set_option( socket_option::send_buffer_size( 65536 ) );
313 : @endcode
314 : */
315 : class BOOST_COROSIO_DECL send_buffer_size : public integer_option
316 : {
317 : public:
318 : using integer_option::integer_option;
319 : using integer_option::operator=;
320 :
321 : /// Return the protocol level.
322 : static int level() noexcept;
323 :
324 : /// Return the option name.
325 : static int name() noexcept;
326 : };
327 :
328 : /** The SO_LINGER socket option.
329 :
330 : Controls behavior when closing a socket with unsent data.
331 : When enabled, `close()` blocks until pending data is sent
332 : or the timeout expires.
333 :
334 : @par Example
335 : @code
336 : sock.set_option( socket_option::linger( true, 5 ) );
337 : auto opt = sock.get_option<socket_option::linger>();
338 : if ( opt.enabled() )
339 : std::cout << "linger timeout: " << opt.timeout() << "s\n";
340 : @endcode
341 : */
342 : class BOOST_COROSIO_DECL linger
343 : {
344 : // Opaque storage for the platform's struct linger.
345 : // POSIX: { int, int } = 8 bytes.
346 : // Windows: { u_short, u_short } = 4 bytes.
347 : static constexpr std::size_t max_storage_ = 8;
348 : alignas(4) unsigned char storage_[max_storage_]{};
349 :
350 : public:
351 : /// Construct with default values (disabled, zero timeout).
352 : linger() noexcept = default;
353 :
354 : /** Construct with explicit values.
355 :
356 : @param enabled `true` to enable linger behavior on close.
357 : @param timeout The linger timeout in seconds.
358 : */
359 : linger(bool enabled, int timeout) noexcept;
360 :
361 : /// Return whether linger is enabled.
362 : bool enabled() const noexcept;
363 :
364 : /// Set whether linger is enabled.
365 : void enabled(bool v) noexcept;
366 :
367 : /// Return the linger timeout in seconds.
368 : int timeout() const noexcept;
369 :
370 : /// Set the linger timeout in seconds.
371 : void timeout(int v) noexcept;
372 :
373 : /// Return the protocol level.
374 : static int level() noexcept;
375 :
376 : /// Return the option name.
377 : static int name() noexcept;
378 :
379 : /// Return a pointer to the underlying storage.
380 10 : void* data() noexcept
381 : {
382 10 : return storage_;
383 : }
384 :
385 : /// Return a pointer to the underlying storage.
386 18 : void const* data() const noexcept
387 : {
388 18 : return storage_;
389 : }
390 :
391 : /// Return the size of the underlying storage.
392 : std::size_t size() const noexcept;
393 :
394 : /** Normalize after `getsockopt`.
395 :
396 : No-op — `struct linger` is always returned at full size.
397 :
398 : @param s The number of bytes actually written by `getsockopt`.
399 : */
400 10 : void resize(std::size_t) noexcept {}
401 : };
402 :
403 : } // namespace boost::corosio::socket_option
404 :
405 : #endif // BOOST_COROSIO_SOCKET_OPTION_HPP
|