71.79% Lines (28/39) 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   161 make_socket_pair(io_context& ctx) 45   161 make_socket_pair(io_context& ctx)
46   { 46   {
HITCBC 47   161 auto ex = ctx.get_executor(); 47   161 auto ex = ctx.get_executor();
48   48  
HITCBC 49   161 std::error_code accept_ec; 49   161 std::error_code accept_ec;
HITCBC 50   161 std::error_code connect_ec; 50   161 std::error_code connect_ec;
HITCBC 51   161 bool accept_done = false; 51   161 bool accept_done = false;
HITCBC 52   161 bool connect_done = false; 52   161 bool connect_done = false;
53   53  
HITCBC 54   161 Acceptor acc(ctx); 54   161 Acceptor acc(ctx);
HITCBC 55 - 161 acc.open(); 55 + 161 if (auto open_ec = acc.open())
MISUNC   56 + throw std::runtime_error("socket_pair open failed: " + open_ec.message());
HITCBC 56   161 acc.set_option(socket_option::reuse_address(true)); 57   161 acc.set_option(socket_option::reuse_address(true));
HITCBC 57   161 if (auto ec = acc.bind(endpoint(ipv4_address::loopback(), 0))) 58   161 if (auto ec = acc.bind(endpoint(ipv4_address::loopback(), 0)))
MISUBC 58   throw std::runtime_error("socket_pair bind failed: " + ec.message()); 59   throw std::runtime_error("socket_pair bind failed: " + ec.message());
HITCBC 59   161 if (auto ec = acc.listen()) 60   161 if (auto ec = acc.listen())
MISUBC 60   throw std::runtime_error("socket_pair listen failed: " + ec.message()); 61   throw std::runtime_error("socket_pair listen failed: " + ec.message());
HITCBC 61   161 auto port = acc.local_endpoint().port(); 62   161 auto port = acc.local_endpoint().port();
62   63  
HITCBC 63   161 Socket s1(ctx); 64   161 Socket s1(ctx);
HITCBC 64   161 Socket s2(ctx); 65   161 Socket s2(ctx);
HITCBC 65 - 161 s2.open(); 66 + 161 if (auto open_ec = s2.open())
MISUNC   67 + throw std::runtime_error("socket_pair open failed: " + open_ec.message());
66   68  
HITCBC 67   161 capy::run_async(ex)( 69   161 capy::run_async(ex)(
HITCBC 68   322 [](Acceptor& a, Socket& s, std::error_code& ec_out, 70   322 [](Acceptor& a, Socket& s, std::error_code& ec_out,
69   bool& done_out) -> capy::task<> { 71   bool& done_out) -> capy::task<> {
70   auto [ec] = co_await a.accept(s); 72   auto [ec] = co_await a.accept(s);
71   ec_out = ec; 73   ec_out = ec;
72   done_out = true; 74   done_out = true;
73   }(acc, s1, accept_ec, accept_done)); 75   }(acc, s1, accept_ec, accept_done));
74   76  
HITCBC 75   161 capy::run_async(ex)( 77   161 capy::run_async(ex)(
HITCBC 76   322 [](Socket& s, endpoint ep, std::error_code& ec_out, 78   322 [](Socket& s, endpoint ep, std::error_code& ec_out,
77   bool& done_out) -> capy::task<> { 79   bool& done_out) -> capy::task<> {
78   auto [ec] = co_await s.connect(ep); 80   auto [ec] = co_await s.connect(ep);
79   ec_out = ec; 81   ec_out = ec;
80   done_out = true; 82   done_out = true;
81   }(s2, endpoint(ipv4_address::loopback(), port), connect_ec, 83   }(s2, endpoint(ipv4_address::loopback(), port), connect_ec,
82   connect_done)); 84   connect_done));
83   85  
HITCBC 84   161 ctx.run(); 86   161 ctx.run();
HITCBC 85   161 ctx.restart(); 87   161 ctx.restart();
86   88  
HITCBC 87   161 if (!accept_done || accept_ec) 89   161 if (!accept_done || accept_ec)
88   { 90   {
MISUBC 89   std::fprintf( 91   std::fprintf(
90   stderr, "socket_pair: accept failed (done=%d, ec=%s)\n", 92   stderr, "socket_pair: accept failed (done=%d, ec=%s)\n",
91   accept_done, accept_ec.message().c_str()); 93   accept_done, accept_ec.message().c_str());
MISUBC 92   acc.close(); 94   acc.close();
MISUBC 93   throw std::runtime_error("socket_pair accept failed"); 95   throw std::runtime_error("socket_pair accept failed");
94   } 96   }
95   97  
HITCBC 96   161 if (!connect_done || connect_ec) 98   161 if (!connect_done || connect_ec)
97   { 99   {
MISUBC 98   std::fprintf( 100   std::fprintf(
99   stderr, "socket_pair: connect failed (done=%d, ec=%s)\n", 101   stderr, "socket_pair: connect failed (done=%d, ec=%s)\n",
100   connect_done, connect_ec.message().c_str()); 102   connect_done, connect_ec.message().c_str());
MISUBC 101   acc.close(); 103   acc.close();
MISUBC 102   s1.close(); 104   s1.close();
MISUBC 103   throw std::runtime_error("socket_pair connect failed"); 105   throw std::runtime_error("socket_pair connect failed");
104   } 106   }
105   107  
HITCBC 106   161 acc.close(); 108   161 acc.close();
107   109  
108   if constexpr (Linger) 110   if constexpr (Linger)
109   { 111   {
HITCBC 110   70 s1.set_option(socket_option::linger(true, 0)); 112   70 s1.set_option(socket_option::linger(true, 0));
HITCBC 111   70 s2.set_option(socket_option::linger(true, 0)); 113   70 s2.set_option(socket_option::linger(true, 0));
112   } 114   }
113   115  
HITCBC 114   322 return {std::move(s1), std::move(s2)}; 116   322 return {std::move(s1), std::move(s2)};
HITCBC 115   161 } 117   161 }
116   118  
117   } // namespace boost::corosio::test 119   } // namespace boost::corosio::test
118   120  
119   #endif 121   #endif