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