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

10  

11  
#ifndef BOOST_COROSIO_TEST_SOCKET_PAIR_HPP
11  
#ifndef BOOST_COROSIO_TEST_SOCKET_PAIR_HPP
12  
#define BOOST_COROSIO_TEST_SOCKET_PAIR_HPP
12  
#define BOOST_COROSIO_TEST_SOCKET_PAIR_HPP
13  

13  

14  
#include <boost/corosio/io_context.hpp>
14  
#include <boost/corosio/io_context.hpp>
15  
#include <boost/corosio/tcp_acceptor.hpp>
15  
#include <boost/corosio/tcp_acceptor.hpp>
16  
#include <boost/corosio/tcp_socket.hpp>
16  
#include <boost/corosio/tcp_socket.hpp>
 
17 +
#include <boost/corosio/socket_option.hpp>
17  
#include <boost/capy/ex/run_async.hpp>
18  
#include <boost/capy/ex/run_async.hpp>
18  
#include <boost/capy/task.hpp>
19  
#include <boost/capy/task.hpp>
19  

20  

20  
#include <cstdio>
21  
#include <cstdio>
21  
#include <stdexcept>
22  
#include <stdexcept>
22  
#include <system_error>
23  
#include <system_error>
23  
#include <utility>
24  
#include <utility>
24  

25  

25  
namespace boost::corosio::test {
26  
namespace boost::corosio::test {
26  

27  

27  
/** Create a connected pair of sockets.
28  
/** Create a connected pair of sockets.
28  

29  

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

32  

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

35  

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

37  

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

45  

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

50  

50  
    Acceptor acc(ctx);
51  
    Acceptor acc(ctx);
51 -
    if (auto ec = acc.listen(endpoint(ipv4_address::loopback(), 0)))
52 +
    acc.open();
 
53 +
    acc.set_option(socket_option::reuse_address(true));
 
54 +
    if (auto ec = acc.bind(endpoint(ipv4_address::loopback(), 0)))
 
55 +
        throw std::runtime_error("socket_pair bind failed: " + ec.message());
 
56 +
    if (auto ec = acc.listen())
52  
        throw std::runtime_error("socket_pair listen failed: " + ec.message());
57  
        throw std::runtime_error("socket_pair listen failed: " + ec.message());
53  
    auto port = acc.local_endpoint().port();
58  
    auto port = acc.local_endpoint().port();
54  

59  

55  
    Socket s1(ctx);
60  
    Socket s1(ctx);
56  
    Socket s2(ctx);
61  
    Socket s2(ctx);
57  
    s2.open();
62  
    s2.open();
58  

63  

59  
    capy::run_async(ex)(
64  
    capy::run_async(ex)(
60  
        [](Acceptor& a, Socket& s, std::error_code& ec_out,
65  
        [](Acceptor& a, Socket& s, std::error_code& ec_out,
61  
           bool& done_out) -> capy::task<> {
66  
           bool& done_out) -> capy::task<> {
62  
            auto [ec] = co_await a.accept(s);
67  
            auto [ec] = co_await a.accept(s);
63  
            ec_out    = ec;
68  
            ec_out    = ec;
64  
            done_out  = true;
69  
            done_out  = true;
65  
        }(acc, s1, accept_ec, accept_done));
70  
        }(acc, s1, accept_ec, accept_done));
66  

71  

67  
    capy::run_async(ex)(
72  
    capy::run_async(ex)(
68  
        [](Socket& s, endpoint ep, std::error_code& ec_out,
73  
        [](Socket& s, endpoint ep, std::error_code& ec_out,
69  
           bool& done_out) -> capy::task<> {
74  
           bool& done_out) -> capy::task<> {
70  
            auto [ec] = co_await s.connect(ep);
75  
            auto [ec] = co_await s.connect(ep);
71  
            ec_out    = ec;
76  
            ec_out    = ec;
72  
            done_out  = true;
77  
            done_out  = true;
73  
        }(s2, endpoint(ipv4_address::loopback(), port), connect_ec,
78  
        }(s2, endpoint(ipv4_address::loopback(), port), connect_ec,
74  
                           connect_done));
79  
                           connect_done));
75  

80  

76  
    ctx.run();
81  
    ctx.run();
77  
    ctx.restart();
82  
    ctx.restart();
78  

83  

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

92  

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

102  

98  
    acc.close();
103  
    acc.close();
99  

104  

100 -
    s1.set_linger(true, 0);
105 +
    s1.set_option(socket_option::linger(true, 0));
101 -
    s2.set_linger(true, 0);
106 +
    s2.set_option(socket_option::linger(true, 0));
102  

107  

103  
    return {std::move(s1), std::move(s2)};
108  
    return {std::move(s1), std::move(s2)};
104  
}
109  
}
105  

110  

106  
} // namespace boost::corosio::test
111  
} // namespace boost::corosio::test
107  

112  

108  
#endif
113  
#endif