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