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