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 +
    explicit boolean_option( bool v ) noexcept : value_( v ? 1 : 0 ) {}
 
54 +

 
55 +
    /// Assign a new value.
 
56 +
    boolean_option& operator=( bool v ) noexcept
 
57 +
    {
 
58 +
        value_ = v ? 1 : 0;
 
59 +
        return *this;
 
60 +
    }
 
61 +

 
62 +
    /// Return the option value.
 
63 +
    bool value() const noexcept { return value_ != 0; }
 
64 +

 
65 +
    /// Return the option value.
 
66 +
    explicit operator bool() const noexcept { return value_ != 0; }
 
67 +

 
68 +
    /// Return the negated option value.
 
69 +
    bool operator!() const noexcept { return value_ == 0; }
 
70 +

 
71 +
    /// Return a pointer to the underlying storage.
 
72 +
    void* data() noexcept { return &value_; }
 
73 +

 
74 +
    /// Return a pointer to the underlying storage.
 
75 +
    void const* data() const noexcept { return &value_; }
 
76 +

 
77 +
    /// Return the size of the underlying storage.
 
78 +
    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 +
    void resize( std::size_t s ) noexcept
 
87 +
    {
 
88 +
        if ( s == sizeof( char ) )
 
89 +
            value_ = *reinterpret_cast<unsigned char*>( &value_ ) ? 1 : 0;
 
90 +
    }
 
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 +
    explicit integer_option( int v ) noexcept : value_( v ) {}
 
111 +

 
112 +
    /// Assign a new value.
 
113 +
    integer_option& operator=( int v ) noexcept
 
114 +
    {
 
115 +
        value_ = v;
 
116 +
        return *this;
 
117 +
    }
 
118 +

 
119 +
    /// Return the option value.
 
120 +
    int value() const noexcept { return value_; }
 
121 +

 
122 +
    /// Return a pointer to the underlying storage.
 
123 +
    void* data() noexcept { return &value_; }
 
124 +

 
125 +
    /// Return a pointer to the underlying storage.
 
126 +
    void const* data() const noexcept { return &value_; }
 
127 +

 
128 +
    /// Return the size of the underlying storage.
 
129 +
    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 +
    void resize( std::size_t s ) noexcept
 
136 +
    {
 
137 +
        if ( s == sizeof( char ) )
 
138 +
            value_ = static_cast<int>(
 
139 +
                *reinterpret_cast<unsigned char*>( &value_ ) );
 
140 +
    }
 
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 +
    void* data() noexcept { return storage_; }
 
351 +

 
352 +
    /// Return a pointer to the underlying storage.
 
353 +
    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 +
    void resize( std::size_t ) noexcept {}
 
365 +
};
 
366 +

 
367 +
} // namespace boost::corosio::socket_option
 
368 +

 
369 +
#endif // BOOST_COROSIO_SOCKET_OPTION_HPP