TLA Line data Source code
1 : //
2 : // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3 : // Copyright (c) 2026 Steve Gerbino
4 : //
5 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 : //
8 : // Official repository: https://github.com/cppalliance/corosio
9 : //
10 :
11 : #ifndef BOOST_COROSIO_TEST_SOCKET_PAIR_HPP
12 : #define BOOST_COROSIO_TEST_SOCKET_PAIR_HPP
13 :
14 : #include <boost/corosio/io_context.hpp>
15 : #include <boost/corosio/tcp_acceptor.hpp>
16 : #include <boost/corosio/tcp_socket.hpp>
17 : #include <boost/corosio/socket_option.hpp>
18 : #include <boost/capy/ex/run_async.hpp>
19 : #include <boost/capy/task.hpp>
20 :
21 : #include <cstdio>
22 : #include <stdexcept>
23 : #include <system_error>
24 : #include <utility>
25 :
26 : namespace boost::corosio::test {
27 :
28 : /** Create a connected pair of sockets.
29 :
30 : Creates two sockets connected via loopback TCP sockets.
31 : Data written to one socket can be read from the other.
32 :
33 : @tparam Socket The socket type (default `tcp_socket`).
34 : @tparam Acceptor The acceptor type (default `tcp_acceptor`).
35 :
36 : @param ctx The I/O context for the sockets.
37 :
38 : @return A pair of connected sockets.
39 : */
40 : template<
41 : class Socket = tcp_socket,
42 : class Acceptor = tcp_acceptor,
43 : bool Linger = true>
44 : std::pair<Socket, Socket>
45 HIT 161 : make_socket_pair(io_context& ctx)
46 : {
47 161 : auto ex = ctx.get_executor();
48 :
49 161 : std::error_code accept_ec;
50 161 : std::error_code connect_ec;
51 161 : bool accept_done = false;
52 161 : bool connect_done = false;
53 :
54 161 : Acceptor acc(ctx);
55 161 : if (auto open_ec = acc.open())
56 MIS 0 : throw std::runtime_error("socket_pair open failed: " + open_ec.message());
57 HIT 161 : acc.set_option(socket_option::reuse_address(true));
58 161 : if (auto ec = acc.bind(endpoint(ipv4_address::loopback(), 0)))
59 MIS 0 : throw std::runtime_error("socket_pair bind failed: " + ec.message());
60 HIT 161 : if (auto ec = acc.listen())
61 MIS 0 : throw std::runtime_error("socket_pair listen failed: " + ec.message());
62 HIT 161 : auto port = acc.local_endpoint().port();
63 :
64 161 : Socket s1(ctx);
65 161 : Socket s2(ctx);
66 161 : if (auto open_ec = s2.open())
67 MIS 0 : throw std::runtime_error("socket_pair open failed: " + open_ec.message());
68 :
69 HIT 161 : capy::run_async(ex)(
70 322 : [](Acceptor& a, Socket& s, std::error_code& ec_out,
71 : bool& done_out) -> capy::task<> {
72 : auto [ec] = co_await a.accept(s);
73 : ec_out = ec;
74 : done_out = true;
75 : }(acc, s1, accept_ec, accept_done));
76 :
77 161 : capy::run_async(ex)(
78 322 : [](Socket& s, endpoint ep, std::error_code& ec_out,
79 : bool& done_out) -> capy::task<> {
80 : auto [ec] = co_await s.connect(ep);
81 : ec_out = ec;
82 : done_out = true;
83 : }(s2, endpoint(ipv4_address::loopback(), port), connect_ec,
84 : connect_done));
85 :
86 161 : ctx.run();
87 161 : ctx.restart();
88 :
89 161 : if (!accept_done || accept_ec)
90 : {
91 MIS 0 : std::fprintf(
92 : stderr, "socket_pair: accept failed (done=%d, ec=%s)\n",
93 : accept_done, accept_ec.message().c_str());
94 0 : acc.close();
95 0 : throw std::runtime_error("socket_pair accept failed");
96 : }
97 :
98 HIT 161 : if (!connect_done || connect_ec)
99 : {
100 MIS 0 : std::fprintf(
101 : stderr, "socket_pair: connect failed (done=%d, ec=%s)\n",
102 : connect_done, connect_ec.message().c_str());
103 0 : acc.close();
104 0 : s1.close();
105 0 : throw std::runtime_error("socket_pair connect failed");
106 : }
107 :
108 HIT 161 : acc.close();
109 :
110 : if constexpr (Linger)
111 : {
112 70 : s1.set_option(socket_option::linger(true, 0));
113 70 : s2.set_option(socket_option::linger(true, 0));
114 : }
115 :
116 322 : return {std::move(s1), std::move(s2)};
117 161 : }
118 :
119 : } // namespace boost::corosio::test
120 :
121 : #endif
|