1  
//
1  
//
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
2  
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
 
3 +
// Copyright (c) 2026 Steve Gerbino
3  
//
4  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
5  
// 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  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
//
7  
//
7  
// Official repository: https://github.com/cppalliance/corosio
8  
// Official repository: https://github.com/cppalliance/corosio
8  
//
9  
//
9  

10  

10  
#ifndef BOOST_COROSIO_TEST_SOCKET_PAIR_HPP
11  
#ifndef BOOST_COROSIO_TEST_SOCKET_PAIR_HPP
11  
#define BOOST_COROSIO_TEST_SOCKET_PAIR_HPP
12  
#define BOOST_COROSIO_TEST_SOCKET_PAIR_HPP
12  

13  

13 -
#include <boost/corosio/detail/config.hpp>
14 +
#include <boost/corosio/io_context.hpp>
14 -
#include <boost/corosio/basic_io_context.hpp>
15 +
#include <boost/corosio/tcp_acceptor.hpp>
15  
#include <boost/corosio/tcp_socket.hpp>
16  
#include <boost/corosio/tcp_socket.hpp>
 
17 +
#include <boost/capy/ex/run_async.hpp>
 
18 +
#include <boost/capy/task.hpp>
16  

19  

 
20 +
#include <cstdio>
 
21 +
#include <stdexcept>
 
22 +
#include <system_error>
17  
#include <utility>
23  
#include <utility>
18  

24  

19  
namespace boost::corosio::test {
25  
namespace boost::corosio::test {
20  

26  

21  
/** Create a connected pair of sockets.
27  
/** Create a connected pair of sockets.
22  

28  

23  
    Creates two sockets connected via loopback TCP sockets.
29  
    Creates two sockets connected via loopback TCP sockets.
24  
    Data written to one socket can be read from the other.
30  
    Data written to one socket can be read from the other.
25  

31  

 
32 +
    @tparam Socket The socket type (default `tcp_socket`).
 
33 +
    @tparam Acceptor The acceptor type (default `tcp_acceptor`).
 
34 +

26  
    @param ctx The I/O context for the sockets.
35  
    @param ctx The I/O context for the sockets.
27  

36  

28  
    @return A pair of connected sockets.
37  
    @return A pair of connected sockets.
29  
*/
38  
*/
30 -
BOOST_COROSIO_DECL
39 +
template<class Socket = tcp_socket, class Acceptor = tcp_acceptor>
31 -
std::pair<tcp_socket, tcp_socket> make_socket_pair(basic_io_context& ctx);
40 +
std::pair<Socket, Socket>
 
41 +
make_socket_pair(io_context& ctx)
 
42 +
{
 
43 +
    auto ex = ctx.get_executor();
 
44 +

 
45 +
    std::error_code accept_ec;
 
46 +
    std::error_code connect_ec;
 
47 +
    bool accept_done  = false;
 
48 +
    bool connect_done = false;
 
49 +

 
50 +
    Acceptor acc(ctx);
 
51 +
    if (auto ec = acc.listen(endpoint(ipv4_address::loopback(), 0)))
 
52 +
        throw std::runtime_error("socket_pair listen failed: " + ec.message());
 
53 +
    auto port = acc.local_endpoint().port();
 
54 +

 
55 +
    Socket s1(ctx);
 
56 +
    Socket s2(ctx);
 
57 +
    s2.open();
 
58 +

 
59 +
    capy::run_async(ex)(
 
60 +
        [](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 +
    capy::run_async(ex)(
 
68 +
        [](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 +
    ctx.run();
 
77 +
    ctx.restart();
 
78 +

 
79 +
    if (!accept_done || accept_ec)
 
80 +
    {
 
81 +
        std::fprintf(
 
82 +
            stderr, "socket_pair: accept failed (done=%d, ec=%s)\n",
 
83 +
            accept_done, accept_ec.message().c_str());
 
84 +
        acc.close();
 
85 +
        throw std::runtime_error("socket_pair accept failed");
 
86 +
    }
 
87 +

 
88 +
    if (!connect_done || connect_ec)
 
89 +
    {
 
90 +
        std::fprintf(
 
91 +
            stderr, "socket_pair: connect failed (done=%d, ec=%s)\n",
 
92 +
            connect_done, connect_ec.message().c_str());
 
93 +
        acc.close();
 
94 +
        s1.close();
 
95 +
        throw std::runtime_error("socket_pair connect failed");
 
96 +
    }
 
97 +

 
98 +
    acc.close();
 
99 +

 
100 +
    s1.set_linger(true, 0);
 
101 +
    s2.set_linger(true, 0);
 
102 +

 
103 +
    return {std::move(s1), std::move(s2)};
 
104 +
}
32  

105  

33  
} // namespace boost::corosio::test
106  
} // namespace boost::corosio::test
34  

107  

35  
#endif
108  
#endif