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