TLA Line data Source code
1 : //
2 : // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3 : // Copyright (c) 2026 Steve Gerbino
4 : // Copyright (c) 2026 Michael Vandeberg
5 : //
6 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
7 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
8 : //
9 : // Official repository: https://github.com/cppalliance/corosio
10 : //
11 :
12 : #ifndef BOOST_COROSIO_TCP_ACCEPTOR_HPP
13 : #define BOOST_COROSIO_TCP_ACCEPTOR_HPP
14 :
15 : #include <boost/corosio/family.hpp>
16 : #include <boost/corosio/detail/config.hpp>
17 : #include <boost/corosio/detail/native_handle.hpp>
18 : #include <boost/corosio/detail/op_base.hpp>
19 : #include <boost/corosio/error.hpp>
20 : #include <boost/corosio/wait_type.hpp>
21 : #include <boost/corosio/io/io_object.hpp>
22 : #include <boost/capy/results.hpp>
23 : #include <boost/corosio/endpoint.hpp>
24 : #include <boost/corosio/tcp_socket.hpp>
25 : #include <boost/capy/ex/executor_ref.hpp>
26 : #include <boost/capy/ex/execution_context.hpp>
27 : #include <boost/capy/ex/await_context.hpp>
28 : #include <boost/capy/concept/executor.hpp>
29 :
30 : #include <system_error>
31 :
32 : #include <concepts>
33 : #include <boost/capy/continuation.hpp>
34 :
35 : #include <coroutine>
36 : #include <cstddef>
37 : #include <stop_token>
38 : #include <type_traits>
39 : #include <utility>
40 :
41 : namespace boost::corosio {
42 :
43 : /** Accepts inbound TCP connections, from a coroutine.
44 :
45 : This class provides asynchronous TCP accept operations that return
46 : awaitable types. The acceptor binds to a local endpoint and listens
47 : for incoming connections.
48 :
49 : Each accept operation participates in the affine awaitable protocol.
50 : It resumes the awaiting coroutine through that coroutine's executor,
51 : not through the executor of the context that owns the acceptor.
52 :
53 : @par Thread Safety
54 : Distinct objects: Safe.@n
55 : Shared objects: Unsafe. An acceptor must not have concurrent accept
56 : operations.
57 :
58 : @par Semantics
59 : Wraps the platform TCP listener. Operations dispatch to
60 : OS accept APIs via the `io_context` reactor.
61 :
62 : @par Example
63 : @par !example convenience_construction
64 :
65 : @par Example
66 : @par !example fine_grained_setup
67 : */
68 : class BOOST_COROSIO_DECL tcp_acceptor : public io_object
69 : {
70 : struct wait_awaitable : detail::void_op_base<wait_awaitable>
71 : {
72 : private:
73 : friend tcp_acceptor;
74 :
75 HIT 31 : wait_awaitable(tcp_acceptor& acc, wait_type w) noexcept
76 62 : : acc_(acc)
77 31 : , w_(w)
78 : {
79 31 : }
80 :
81 : friend detail::void_op_base<wait_awaitable>;
82 :
83 : tcp_acceptor& acc_;
84 : wait_type w_;
85 :
86 : std::coroutine_handle<>
87 27 : dispatch(capy::continuation& cont, capy::executor_ref ex) const
88 : {
89 27 : return acc_.get().wait(cont, ex, w_, token_, &ec_);
90 : }
91 : };
92 :
93 : struct accept_awaitable : detail::void_op_base<accept_awaitable>
94 : {
95 : private:
96 : friend tcp_acceptor;
97 : friend detail::void_op_base<accept_awaitable>;
98 :
99 : tcp_acceptor& acc_;
100 : tcp_socket& peer_;
101 : mutable io_object::implementation* peer_impl_ = nullptr;
102 :
103 :
104 7455 : accept_awaitable(tcp_acceptor& acc, tcp_socket& peer) noexcept
105 14910 : : acc_(acc)
106 7455 : , peer_(peer)
107 : {
108 7455 : }
109 :
110 : std::coroutine_handle<>
111 7443 : dispatch(capy::continuation& cont, capy::executor_ref ex) const
112 : {
113 22329 : return acc_.get().accept(
114 22329 : cont, ex, this->token_, &this->ec_, &peer_impl_);
115 : }
116 :
117 : public:
118 : // A peer accepted for an awaiter that never resumed is closed
119 : // here, or nothing would own it.
120 14910 : ~accept_awaitable()
121 : {
122 14910 : if (peer_impl_)
123 2 : discard_peer(acc_, peer_impl_);
124 14910 : }
125 :
126 6643 : [[nodiscard]] capy::results<> await_resume() const noexcept
127 : {
128 6643 : if (!this->ec_ && peer_impl_)
129 6480 : peer_.h_.reset(std::exchange(peer_impl_, nullptr));
130 6643 : return {this->ec_};
131 : }
132 : };
133 :
134 : struct accept_value_awaitable : detail::void_op_base<accept_value_awaitable>
135 : {
136 : private:
137 : friend tcp_acceptor;
138 : friend detail::void_op_base<accept_value_awaitable>;
139 :
140 : tcp_acceptor& acc_;
141 : mutable io_object::implementation* peer_impl_ = nullptr;
142 :
143 :
144 41 : explicit accept_value_awaitable(tcp_acceptor& acc) noexcept : acc_(acc)
145 : {
146 41 : }
147 :
148 : std::coroutine_handle<>
149 37 : dispatch(capy::continuation& cont, capy::executor_ref ex) const
150 : {
151 111 : return acc_.get().accept(
152 111 : cont, ex, this->token_, &this->ec_, &peer_impl_);
153 : }
154 :
155 : public:
156 : // A peer accepted for an awaiter that never resumed is closed
157 : // here, or nothing would own it.
158 82 : ~accept_value_awaitable()
159 : {
160 82 : if (peer_impl_)
161 2 : discard_peer(acc_, peer_impl_);
162 82 : }
163 :
164 31 : [[nodiscard]] capy::results<tcp_socket> await_resume() noexcept
165 : {
166 31 : return take_peer(acc_, this->ec_, peer_impl_);
167 : }
168 : };
169 :
170 : public:
171 : /** Closes the acceptor if open, cancelling any pending operations.
172 : */
173 : ~tcp_acceptor() override;
174 :
175 : /** Construct an acceptor from an execution context.
176 :
177 : @param ctx The execution context that owns this acceptor.
178 : */
179 : explicit tcp_acceptor(capy::execution_context& ctx);
180 :
181 : /** Convenience constructor: open + configure + bind + listen.
182 :
183 : Creates a fully bound listening acceptor in a single
184 : expression, throwing the codes the piecewise `open()` +
185 : `set_option()` + `bind()` + `listen()` path reports. The
186 : address family is deduced from @p ep.
187 :
188 : Before binding, the constructor configures address reuse so a
189 : server can rebind its port immediately after a restart. It
190 : sets `SO_REUSEADDR` on POSIX and `SO_EXCLUSIVEADDRUSE` on
191 : Windows. Windows does not use `SO_REUSEADDR` because it
192 : instead grants other sockets bind-over rights. A second
193 : listener on an occupied endpoint therefore throws
194 : `errc::address_in_use` on every platform.
195 :
196 : @param ctx The execution context that owns this acceptor.
197 : @param ep The local endpoint to bind to.
198 : @param backlog The maximum pending connection queue length.
199 :
200 : @throws std::system_error on open, configuration, bind, or
201 : listen failure.
202 : */
203 : tcp_acceptor(capy::execution_context& ctx, endpoint ep, int backlog = 128);
204 :
205 : /** Construct an acceptor from an executor.
206 :
207 : The acceptor is associated with the executor's context. `Ex`
208 : must satisfy `capy::Executor`.
209 :
210 : @param ex The executor whose context owns the acceptor.
211 : */
212 : template<class Ex>
213 : requires(!std::same_as<std::remove_cvref_t<Ex>, tcp_acceptor>) &&
214 : capy::Executor<Ex>
215 1 : explicit tcp_acceptor(Ex const& ex) : tcp_acceptor(ex.context())
216 : {
217 1 : }
218 :
219 : /** Convenience constructor from an executor.
220 :
221 : Creates a fully bound listening acceptor in a single
222 : expression, throwing the codes the piecewise `open()` +
223 : `set_option()` + `bind()` + `listen()` path reports. The
224 : address family is deduced from @p ep.
225 :
226 : Before binding, the constructor configures address reuse so a
227 : server can rebind its port immediately after a restart. It
228 : sets `SO_REUSEADDR` on POSIX and `SO_EXCLUSIVEADDRUSE` on
229 : Windows. Windows does not use `SO_REUSEADDR` because it
230 : instead grants other sockets bind-over rights. A second
231 : listener on an occupied endpoint therefore throws
232 : `errc::address_in_use` on every platform.
233 :
234 : `Ex` must satisfy `capy::Executor`.
235 :
236 : @param ex The executor whose context owns the acceptor.
237 : @param ep The local endpoint to bind to.
238 : @param backlog The maximum pending connection queue length.
239 :
240 : @throws std::system_error on open, configuration, bind, or
241 : listen failure.
242 : */
243 : template<class Ex>
244 : requires capy::Executor<Ex>
245 : tcp_acceptor(Ex const& ex, endpoint ep, int backlog = 128)
246 : : tcp_acceptor(ex.context(), ep, backlog)
247 : {
248 : }
249 :
250 : /** Transfers ownership of the acceptor resources.
251 :
252 : @param other The acceptor to move from.
253 :
254 : @pre No awaitables returned by @p other's methods exist.
255 : @pre The execution context associated with @p other must
256 : outlive this acceptor.
257 : */
258 182 : tcp_acceptor(tcp_acceptor&& other) noexcept : io_object(std::move(other)) {}
259 :
260 : /** Closes any existing acceptor and transfers ownership.
261 :
262 : @param other The acceptor to move from.
263 :
264 : @pre No awaitables returned by either `*this` or @p other's
265 : methods exist.
266 : @pre The execution context associated with @p other must
267 : outlive this acceptor.
268 :
269 : @return Reference to this acceptor.
270 : */
271 3 : tcp_acceptor& operator=(tcp_acceptor&& other) noexcept
272 : {
273 3 : if (this != &other)
274 : {
275 3 : close();
276 3 : h_ = std::move(other.h_);
277 : }
278 3 : return *this;
279 : }
280 :
281 : /// Copy construction is disabled; the handle is uniquely owned.
282 : tcp_acceptor(tcp_acceptor const&) = delete;
283 : /// Copy assignment is disabled; the handle is uniquely owned.
284 : tcp_acceptor& operator=(tcp_acceptor const&) = delete;
285 :
286 : /** Create the acceptor socket without binding or listening.
287 :
288 : Creates a TCP socket with dual-stack enabled for IPv6.
289 : Does not set SO_REUSEADDR. Call `set_option` explicitly
290 : if needed.
291 :
292 : If the acceptor is already open, this function is a no-op.
293 :
294 : Failures such as descriptor exhaustion are normal runtime
295 : conditions and are reported through the returned error code.
296 :
297 : @param f The address family (IPv4 or IPv6). Defaults to
298 : `family::v4`.
299 :
300 : @par Example
301 : @par !example open
302 :
303 : @see @ref bind, @ref listen
304 :
305 : @return The error code, empty on success.
306 : */
307 : [[nodiscard]] std::error_code open(family f = family::v4) noexcept;
308 :
309 : /** Bind to a local endpoint.
310 :
311 : The acceptor must be open. Binds the socket to @p ep and
312 : caches the resolved local endpoint (useful when port 0 is
313 : used to request an ephemeral port).
314 :
315 : @param ep The local endpoint to bind to.
316 :
317 : @return An error code indicating success or the reason for
318 : failure.
319 :
320 : @par Error Conditions
321 : @li `errc::address_in_use`: The endpoint is already in use.
322 : @li `errc::address_not_available`: The address is not available
323 : on any local interface.
324 : @li `errc::permission_denied`: Insufficient privileges to bind
325 : to the endpoint (e.g., privileged port).
326 : @li `errc::bad_file_descriptor`: The acceptor is not open.
327 : */
328 : [[nodiscard]] std::error_code bind(endpoint ep) noexcept;
329 :
330 : /** Start listening for incoming connections.
331 :
332 : The acceptor must be open and bound. Registers the acceptor
333 : with the platform reactor.
334 :
335 : @param backlog The maximum length of the queue of pending
336 : connections. Defaults to 128.
337 :
338 : @return An error code indicating success or the reason for
339 : failure.
340 :
341 : A closed acceptor reports `errc::bad_file_descriptor`.
342 : */
343 : [[nodiscard]] std::error_code listen(int backlog = 128) noexcept;
344 :
345 : /** Close the acceptor.
346 :
347 : Releases acceptor resources. Any pending operations complete
348 : with `errc::operation_canceled`.
349 : */
350 : void close() noexcept;
351 :
352 : /** Check if the acceptor is listening.
353 :
354 : @return `true` if the acceptor is open and listening.
355 : */
356 21262 : bool is_open() const noexcept
357 : {
358 21262 : return h_ && get().is_open();
359 : }
360 :
361 : /** Initiate an asynchronous accept operation.
362 :
363 : Accepts an incoming connection and initializes the provided
364 : socket with the new connection. The acceptor must be listening
365 : before calling this function.
366 :
367 : The operation supports cancellation via `std::stop_token` through
368 : the affine awaitable protocol. If the associated stop token is
369 : triggered, the operation completes immediately with
370 : `errc::operation_canceled`.
371 :
372 : @param peer The socket to receive the accepted connection. Any
373 : existing connection on this socket is closed.
374 :
375 : @return An awaitable that completes with `results<>`.
376 : Returns success on successful accept, or an error code on
377 : failure including:
378 : - `operation_canceled`: Cancelled via stop_token or cancel().
379 : Check `ec == cond::canceled` for portable comparison.
380 :
381 : A closed acceptor completes with `errc::bad_file_descriptor`.
382 :
383 : @pre The peer socket must be associated with the same execution context.
384 :
385 : Both this acceptor and @p peer must outlive the returned
386 : awaitable.
387 :
388 : @par Example
389 : @par !example accept_into_a_reused_socket
390 :
391 : @see @ref accept()
392 : */
393 7455 : [[nodiscard]] auto accept(tcp_socket& peer)
394 : {
395 7455 : accept_awaitable aw(*this, peer);
396 7455 : if (!is_open())
397 2 : aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
398 7455 : return aw;
399 : }
400 :
401 : /** Initiate an asynchronous accept operation, returning the peer.
402 :
403 : Accepts an incoming connection and returns a newly constructed
404 : socket for it, associated with this acceptor's execution context.
405 : The acceptor must be listening before calling this function.
406 :
407 : The caller does not pre-construct the peer socket. The returned
408 : socket shares this acceptor's execution context.
409 :
410 : The operation supports cancellation via `std::stop_token` through
411 : the affine awaitable protocol. If the associated stop token is
412 : triggered, the operation completes immediately with
413 : `errc::operation_canceled`.
414 :
415 : @return An awaitable that completes with `results<tcp_socket>`.
416 : On success the payload is the connected peer socket; on failure
417 : (including cancellation) the error code is set and the payload
418 : socket is unconnected. Errors include:
419 : - `operation_canceled`: Cancelled via stop_token or cancel().
420 : Check `ec == cond::canceled` for portable comparison.
421 :
422 : A closed acceptor completes with `errc::bad_file_descriptor`.
423 : On failure the returned socket is default-constructed and not
424 : open. It has no context, so it may only be destroyed, assigned,
425 : or checked with `is_open()`.
426 :
427 : @pre This acceptor must outlive the returned awaitable.
428 :
429 : @par Example
430 : @par !example accept_returning_a_new_socket
431 :
432 : @see @ref accept(tcp_socket&)
433 : */
434 41 : [[nodiscard]] auto accept()
435 : {
436 41 : accept_value_awaitable aw(*this);
437 41 : if (!is_open())
438 4 : aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
439 41 : return aw;
440 : }
441 :
442 : /** Wait for an incoming connection or readiness condition.
443 :
444 : Suspends until the listen socket is ready in the
445 : requested direction, or an error condition is reported.
446 : For `wait_type::read`, completion signals that a
447 : subsequent @ref accept succeeds without blocking. A
448 : connection already queued when the wait begins completes
449 : it immediately. No connection is consumed.
450 :
451 : @note `wait_type::write` is not usable on an acceptor:
452 : writability carries no meaning for a listening socket, so
453 : the wait fails with `errc::operation_not_supported` on
454 : every backend.
455 :
456 : @param w The wait direction.
457 :
458 : @return An awaitable that completes with `results<>`.
459 :
460 : A closed acceptor completes with `errc::bad_file_descriptor`.
461 :
462 : @pre This acceptor must outlive the returned awaitable.
463 : */
464 31 : [[nodiscard]] auto wait(wait_type w)
465 : {
466 31 : wait_awaitable aw(*this, w);
467 31 : if (!is_open())
468 2 : aw.ec_ = make_error_code(std::errc::bad_file_descriptor);
469 31 : return aw;
470 : }
471 :
472 : /** Cancel any pending asynchronous operations.
473 :
474 : Accept and wait transfer no bytes, so a cancellation always wins:
475 : an operation reports `errc::operation_canceled` even when it had
476 : already succeeded when the cancellation landed. Check
477 : `ec == cond::canceled` for portable comparison.
478 : */
479 : void cancel() noexcept;
480 :
481 : /** Get the native socket handle.
482 :
483 : Returns the underlying platform-specific socket descriptor.
484 : On POSIX systems this is an `int` file descriptor.
485 : On Windows this is a `SOCKET` handle.
486 :
487 : @return The native socket handle, or -1/INVALID_SOCKET if not open.
488 :
489 : @pre None. May be called on closed acceptors.
490 : */
491 : native_handle_type native_handle() const noexcept;
492 :
493 : /** Assign an existing native socket to this acceptor.
494 :
495 : Adopts a listening socket created outside the library. The
496 : socket may come from a service manager, be inherited, or be
497 : created natively. Adoption registers the socket with the
498 : backend. The socket must be a listening stream socket in the
499 : `AF_INET` or `AF_INET6` family.
500 : Adoption never alters the descriptor's flags or options: on
501 : POSIX the fd must already be non-blocking, and on Windows the
502 : socket must be overlapped-capable.
503 :
504 : Adoption does not verify listen state; @ref accept reports the
505 : error if the socket is not listening.
506 :
507 : The object must be closed. To replace a held socket, `close()`
508 : or `release()` it first.
509 :
510 : @par Exception Safety
511 : Throws nothing. On failure the object is unchanged and the
512 : caller retains ownership of `fd`.
513 :
514 : @param fd The native socket to adopt. On success the object
515 : owns it and closes it.
516 :
517 : @return `error::already_open` if this object is open.
518 : Otherwise the error code, empty on success. Validation and
519 : registration failures are normal runtime conditions when
520 : adopting foreign descriptors.
521 : */
522 : [[nodiscard]] std::error_code assign(native_handle_type fd) noexcept;
523 :
524 : /** Release ownership of the native socket handle.
525 :
526 : Deregisters the socket from the backend and cancels pending
527 : operations without closing the descriptor. The caller takes
528 : ownership of the returned handle.
529 :
530 : A closed object reports `errc::bad_file_descriptor` and the
531 : returned handle is the invalid sentinel ( -1, or
532 : `INVALID_SOCKET` on Windows ), never zero.
533 :
534 : @note With the IOCP backend, a socket released while operations
535 : on it are in flight stays bound to this context's completion
536 : port for good. Windows refuses to unbind a handle with I/O
537 : outstanding, so no context, this one included, can assign it
538 : afterwards. Its cancelled operations still complete normally.
539 :
540 : @return The error code and the native handle.
541 :
542 : @post is_open() == false
543 : */
544 : [[nodiscard]] capy::results<native_handle_type> release() noexcept;
545 :
546 : /** Get the local endpoint of the acceptor.
547 :
548 : Returns the local address and port to which the acceptor is bound.
549 : This is useful when binding to port 0 (ephemeral port) to discover
550 : the OS-assigned port number. The endpoint is cached when bind()
551 : is called.
552 :
553 : @return The local endpoint, or a default endpoint (0.0.0.0:0) if
554 : the acceptor is not open.
555 :
556 : @par Thread Safety
557 : The cached endpoint value is set during bind() and cleared
558 : during close(). This function may be called concurrently with
559 : accept operations, but must not be called concurrently with
560 : bind() or close().
561 : */
562 : endpoint local_endpoint() const noexcept;
563 :
564 : /** Set a socket option on the acceptor.
565 :
566 : Applies a type-safe socket option to the underlying listening
567 : socket. The socket must be open (via `open()` or `listen()`).
568 : This is useful for setting options between `open()` and
569 : `listen()`, such as `socket_option::reuse_port`.
570 :
571 : Fails with `errc::bad_file_descriptor` if the acceptor is not
572 : open, or with the error the operating system reports.
573 :
574 : @par Example
575 : @par !example set_option
576 :
577 : @param opt The option to set.
578 :
579 : @return The error code, empty on success.
580 : */
581 : template<class Option>
582 1436 : [[nodiscard]] std::error_code set_option(Option const& opt) noexcept
583 : {
584 1436 : if (!is_open())
585 2 : return make_error_code(std::errc::bad_file_descriptor);
586 1434 : auto const fam = get().family();
587 1434 : return get().set_option(
588 1434 : opt.level(fam), opt.name(fam), opt.data(fam), opt.size(fam));
589 : }
590 :
591 : /** Get a socket option from the acceptor.
592 :
593 : Retrieves the current value of a type-safe socket option.
594 :
595 : Fails with `errc::bad_file_descriptor` if the acceptor is not
596 : open, or with the error the operating system reports. On
597 : failure the option is value-initialized.
598 :
599 : @par Example
600 : @par !example get_option
601 :
602 : @return The error code and the option value.
603 : */
604 : template<class Option>
605 23 : [[nodiscard]] capy::results<Option> get_option() const noexcept
606 : {
607 23 : if (!is_open())
608 2 : return {make_error_code(std::errc::bad_file_descriptor), Option{}};
609 21 : Option opt{};
610 21 : auto const fam = get().family();
611 21 : std::size_t sz = opt.size(fam);
612 21 : if (auto ec = get().get_option(
613 : opt.level(fam), opt.name(fam), opt.data(fam), &sz))
614 8 : return {ec, Option{}};
615 13 : opt.resize(fam, sz);
616 13 : return {std::error_code{}, std::move(opt)};
617 : }
618 :
619 : /** Define backend hooks for TCP acceptor operations.
620 :
621 : Platform backends derive from this to implement
622 : accept, endpoint query, open-state checks, cancellation,
623 : and socket-option management.
624 : */
625 : struct implementation : io_object::implementation
626 : {
627 : /** Initiate an asynchronous accept operation.
628 :
629 : @param cont Continuation to resume on completion; it lives in
630 : the awaiting frame until then.
631 : @param ex Executor for dispatching the completion.
632 : @param token Stop token for cancellation.
633 : @param ec Output error code.
634 : @param impl_out Output implementation for the accepted peer.
635 :
636 : @return Coroutine handle to resume immediately.
637 : */
638 : virtual std::coroutine_handle<> accept(
639 : capy::continuation& cont,
640 : capy::executor_ref ex,
641 : std::stop_token token,
642 : std::error_code* ec,
643 : io_object::implementation** impl_out) = 0;
644 :
645 : /** Initiate an asynchronous wait for acceptor readiness.
646 :
647 : Completes when the listen socket becomes ready for
648 : the specified direction (typically `wait_type::read`
649 : for an incoming connection), or an error condition is
650 : reported. No connection is consumed.
651 :
652 : @param cont Continuation to resume on completion; it lives in
653 : the awaiting frame until then.
654 : @param ex Executor for dispatching the completion.
655 : @param w The direction to wait on.
656 : @param token Stop token for cancellation.
657 : @param ec Output error code.
658 :
659 : @return Coroutine handle to resume immediately.
660 : */
661 : virtual std::coroutine_handle<> wait(
662 : capy::continuation& cont,
663 : capy::executor_ref ex,
664 : wait_type w,
665 : std::stop_token token,
666 : std::error_code* ec) = 0;
667 :
668 : /** Returns the cached local endpoint.
669 :
670 : @return The cached local endpoint.
671 : */
672 : virtual endpoint local_endpoint() const noexcept = 0;
673 :
674 : /** Return true if the acceptor has a kernel resource open.
675 :
676 : @return true if the acceptor has a kernel resource open.
677 : */
678 : virtual bool is_open() const noexcept = 0;
679 :
680 : /** Return the native handle, or the platform sentinel if closed.
681 :
682 : @return The native handle, or the platform sentinel if closed.
683 : */
684 : virtual native_handle_type native_handle() const noexcept = 0;
685 :
686 : /** Return the socket's address family.
687 :
688 : Socket options render for this family.
689 :
690 : @return The socket's address family.
691 : */
692 : virtual corosio::family family() const noexcept = 0;
693 :
694 : /** Release and return the native handle without closing.
695 :
696 : @return The native handle.
697 : */
698 : virtual native_handle_type release_socket() noexcept = 0;
699 :
700 : /** Cancel any pending asynchronous operations.
701 :
702 : Accept and wait transfer no bytes, so a cancellation always
703 : wins: an operation reports `operation_canceled` even when it
704 : had already succeeded when the cancellation landed.
705 : */
706 : virtual void cancel() noexcept = 0;
707 :
708 : /** Set a socket option.
709 :
710 : @param level The protocol level.
711 : @param optname The option name.
712 : @param data Pointer to the option value.
713 : @param size Size of the option value in bytes.
714 : @return Error code on failure, empty on success.
715 : */
716 : virtual std::error_code set_option(
717 : int level,
718 : int optname,
719 : void const* data,
720 : std::size_t size) noexcept = 0;
721 :
722 : /** Get a socket option.
723 :
724 : @param level The protocol level.
725 : @param optname The option name.
726 : @param data Pointer to receive the option value.
727 : @param size On entry, the size of the buffer. On exit,
728 : the size of the option value.
729 : @return Error code on failure, empty on success.
730 : */
731 : virtual std::error_code
732 : get_option(int level, int optname, void* data, std::size_t* size)
733 : const noexcept = 0;
734 : };
735 :
736 : protected:
737 : /** Adopt an existing handle.
738 :
739 : @param h The handle the acceptor takes ownership of.
740 : */
741 47 : explicit tcp_acceptor(handle h) noexcept : io_object(std::move(h)) {}
742 :
743 : /** Adopt an existing handle, then open, configure, bind, and listen.
744 :
745 : Lets a derived acceptor offer the convenience constructor while
746 : supplying its own service.
747 :
748 : @param h The handle the acceptor takes ownership of.
749 : @param ep The local endpoint to bind to.
750 : @param backlog The maximum pending connection queue length.
751 :
752 : @throws std::system_error on open, configuration, bind, or
753 : listen failure.
754 : */
755 : tcp_acceptor(handle h, endpoint ep, int backlog);
756 :
757 : /** Transfer the accepted peer implementation to the peer socket.
758 :
759 : @param peer The socket that receives the transferred implementation.
760 : @param impl The accepted peer implementation, or null to do nothing.
761 : */
762 : static void
763 15 : reset_peer_impl(tcp_socket& peer, io_object::implementation* impl) noexcept
764 : {
765 15 : if (impl)
766 15 : peer.h_.reset(impl);
767 15 : }
768 :
769 : /** Return the result of an accept that yields the peer.
770 :
771 : The peer socket is built only on success, so a failed accept
772 : never touches the acceptor's context, which a moved-from
773 : acceptor lacks. On failure the peer is default-constructed.
774 :
775 : @param acc The acceptor that performed the accept.
776 : @param ec The completion code.
777 : @param impl The accepted peer implementation; taken on success.
778 :
779 : @return The completion code and the peer.
780 : */
781 39 : static capy::results<tcp_socket> take_peer(
782 : tcp_acceptor& acc,
783 : std::error_code ec,
784 : io_object::implementation*& impl) noexcept
785 : {
786 39 : if (ec || !impl)
787 12 : return {ec, tcp_socket()};
788 :
789 27 : tcp_socket peer(acc.context());
790 27 : peer.h_.reset(std::exchange(impl, nullptr));
791 27 : return {ec, std::move(peer)};
792 27 : }
793 :
794 : /// Close and release a non-null accepted peer no socket took over.
795 : static void
796 : discard_peer(tcp_acceptor& acc, io_object::implementation* impl) noexcept;
797 :
798 : private:
799 32921 : inline implementation& get() const noexcept
800 : {
801 32921 : return *static_cast<implementation*>(h_.get());
802 : }
803 : };
804 :
805 : } // namespace boost::corosio
806 :
807 : #endif
|