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/capy/ex/run_async.hpp>
18 : #include <boost/capy/task.hpp>
19 :
20 : #include <cstdio>
21 : #include <stdexcept>
22 : #include <system_error>
23 : #include <utility>
24 :
25 : namespace boost::corosio::test {
26 :
27 : /** Create a connected pair of sockets.
28 :
29 : Creates two sockets connected via loopback TCP sockets.
30 : Data written to one socket can be read from the other.
31 :
32 : @tparam Socket The socket type (default `tcp_socket`).
33 : @tparam Acceptor The acceptor type (default `tcp_acceptor`).
34 :
35 : @param ctx The I/O context for the sockets.
36 :
37 : @return A pair of connected sockets.
38 : */
39 : template<class Socket = tcp_socket, class Acceptor = tcp_acceptor>
40 : std::pair<Socket, Socket>
41 HIT 4 : make_socket_pair(io_context& ctx)
42 : {
43 4 : auto ex = ctx.get_executor();
44 :
45 4 : std::error_code accept_ec;
46 4 : std::error_code connect_ec;
47 4 : bool accept_done = false;
48 4 : bool connect_done = false;
49 :
50 4 : Acceptor acc(ctx);
51 4 : if (auto ec = acc.listen(endpoint(ipv4_address::loopback(), 0)))
52 MIS 0 : throw std::runtime_error("socket_pair listen failed: " + ec.message());
53 HIT 4 : auto port = acc.local_endpoint().port();
54 :
55 4 : Socket s1(ctx);
56 4 : Socket s2(ctx);
57 4 : s2.open();
58 :
59 4 : capy::run_async(ex)(
60 8 : [](Acceptor& a, Socket& s, std::error_code& ec_out,
61 : bool& done_out) -> capy::task<> {
62 : auto [ec] = co_await a.accept(s);
63 : ec_out = ec;
64 : done_out = true;
65 : }(acc, s1, accept_ec, accept_done));
66 :
67 4 : capy::run_async(ex)(
68 8 : [](Socket& s, endpoint ep, std::error_code& ec_out,
69 : bool& done_out) -> capy::task<> {
70 : auto [ec] = co_await s.connect(ep);
71 : ec_out = ec;
72 : done_out = true;
73 : }(s2, endpoint(ipv4_address::loopback(), port), connect_ec,
74 : connect_done));
75 :
76 4 : ctx.run();
77 4 : ctx.restart();
78 :
79 4 : if (!accept_done || accept_ec)
80 : {
81 MIS 0 : std::fprintf(
82 : stderr, "socket_pair: accept failed (done=%d, ec=%s)\n",
83 : accept_done, accept_ec.message().c_str());
84 0 : acc.close();
85 0 : throw std::runtime_error("socket_pair accept failed");
86 : }
87 :
88 HIT 4 : if (!connect_done || connect_ec)
89 : {
90 MIS 0 : std::fprintf(
91 : stderr, "socket_pair: connect failed (done=%d, ec=%s)\n",
92 : connect_done, connect_ec.message().c_str());
93 0 : acc.close();
94 0 : s1.close();
95 0 : throw std::runtime_error("socket_pair connect failed");
96 : }
97 :
98 HIT 4 : acc.close();
99 :
100 4 : s1.set_linger(true, 0);
101 4 : s2.set_linger(true, 0);
102 :
103 8 : return {std::move(s1), std::move(s2)};
104 4 : }
105 :
106 : } // namespace boost::corosio::test
107 :
108 : #endif
|