sock_tests.cpp
1 // Copyright (c) 2021-2022 The Bitcoin Core developers 2 // Distributed under the MIT software license, see the accompanying 3 // file COPYING or http://www.opensource.org/licenses/mit-license.php. 4 5 #include <common/system.h> 6 #include <compat/compat.h> 7 #include <test/util/setup_common.h> 8 #include <util/sock.h> 9 #include <util/threadinterrupt.h> 10 11 #include <boost/test/unit_test.hpp> 12 13 #include <cassert> 14 #include <thread> 15 16 using namespace std::chrono_literals; 17 18 BOOST_FIXTURE_TEST_SUITE(sock_tests, BasicTestingSetup) 19 20 static bool SocketIsClosed(const SOCKET& s) 21 { 22 // Notice that if another thread is running and creates its own socket after `s` has been 23 // closed, it may be assigned the same file descriptor number. In this case, our test will 24 // wrongly pretend that the socket is not closed. 25 int type; 26 socklen_t len = sizeof(type); 27 return getsockopt(s, SOL_SOCKET, SO_TYPE, (sockopt_arg_type)&type, &len) == SOCKET_ERROR; 28 } 29 30 static SOCKET CreateSocket() 31 { 32 const SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 33 BOOST_REQUIRE(s != static_cast<SOCKET>(SOCKET_ERROR)); 34 return s; 35 } 36 37 BOOST_AUTO_TEST_CASE(constructor_and_destructor) 38 { 39 const SOCKET s = CreateSocket(); 40 Sock* sock = new Sock(s); 41 BOOST_CHECK(*sock == s); 42 BOOST_CHECK(!SocketIsClosed(s)); 43 delete sock; 44 BOOST_CHECK(SocketIsClosed(s)); 45 } 46 47 BOOST_AUTO_TEST_CASE(move_constructor) 48 { 49 const SOCKET s = CreateSocket(); 50 Sock* sock1 = new Sock(s); 51 Sock* sock2 = new Sock(std::move(*sock1)); 52 delete sock1; 53 BOOST_CHECK(!SocketIsClosed(s)); 54 BOOST_CHECK(*sock2 == s); 55 delete sock2; 56 BOOST_CHECK(SocketIsClosed(s)); 57 } 58 59 BOOST_AUTO_TEST_CASE(move_assignment) 60 { 61 const SOCKET s1 = CreateSocket(); 62 const SOCKET s2 = CreateSocket(); 63 Sock* sock1 = new Sock(s1); 64 Sock* sock2 = new Sock(s2); 65 66 BOOST_CHECK(!SocketIsClosed(s1)); 67 BOOST_CHECK(!SocketIsClosed(s2)); 68 69 *sock2 = std::move(*sock1); 70 BOOST_CHECK(!SocketIsClosed(s1)); 71 BOOST_CHECK(SocketIsClosed(s2)); 72 BOOST_CHECK(*sock2 == s1); 73 74 delete sock1; 75 BOOST_CHECK(!SocketIsClosed(s1)); 76 BOOST_CHECK(SocketIsClosed(s2)); 77 BOOST_CHECK(*sock2 == s1); 78 79 delete sock2; 80 BOOST_CHECK(SocketIsClosed(s1)); 81 BOOST_CHECK(SocketIsClosed(s2)); 82 } 83 84 #ifndef WIN32 // Windows does not have socketpair(2). 85 86 static void CreateSocketPair(int s[2]) 87 { 88 BOOST_REQUIRE_EQUAL(socketpair(AF_UNIX, SOCK_STREAM, 0, s), 0); 89 } 90 91 static void SendAndRecvMessage(const Sock& sender, const Sock& receiver) 92 { 93 const char* msg = "abcd"; 94 constexpr ssize_t msg_len = 4; 95 char recv_buf[10]; 96 97 BOOST_CHECK_EQUAL(sender.Send(msg, msg_len, 0), msg_len); 98 BOOST_CHECK_EQUAL(receiver.Recv(recv_buf, sizeof(recv_buf), 0), msg_len); 99 BOOST_CHECK_EQUAL(strncmp(msg, recv_buf, msg_len), 0); 100 } 101 102 BOOST_AUTO_TEST_CASE(send_and_receive) 103 { 104 int s[2]; 105 CreateSocketPair(s); 106 107 Sock* sock0 = new Sock(s[0]); 108 Sock* sock1 = new Sock(s[1]); 109 110 SendAndRecvMessage(*sock0, *sock1); 111 112 Sock* sock0moved = new Sock(std::move(*sock0)); 113 Sock* sock1moved = new Sock(INVALID_SOCKET); 114 *sock1moved = std::move(*sock1); 115 116 delete sock0; 117 delete sock1; 118 119 SendAndRecvMessage(*sock1moved, *sock0moved); 120 121 delete sock0moved; 122 delete sock1moved; 123 124 BOOST_CHECK(SocketIsClosed(s[0])); 125 BOOST_CHECK(SocketIsClosed(s[1])); 126 } 127 128 BOOST_AUTO_TEST_CASE(wait) 129 { 130 int s[2]; 131 CreateSocketPair(s); 132 133 Sock sock0(s[0]); 134 Sock sock1(s[1]); 135 136 std::thread waiter([&sock0]() { (void)sock0.Wait(24h, Sock::RECV); }); 137 138 BOOST_REQUIRE_EQUAL(sock1.Send("a", 1, 0), 1); 139 140 waiter.join(); 141 } 142 143 BOOST_AUTO_TEST_CASE(recv_until_terminator_limit) 144 { 145 constexpr auto timeout = 1min; // High enough so that it is never hit. 146 CThreadInterrupt interrupt; 147 int s[2]; 148 CreateSocketPair(s); 149 150 Sock sock_send(s[0]); 151 Sock sock_recv(s[1]); 152 153 std::thread receiver([&sock_recv, &timeout, &interrupt]() { 154 constexpr size_t max_data{10}; 155 bool threw_as_expected{false}; 156 // BOOST_CHECK_EXCEPTION() writes to some variables shared with the main thread which 157 // creates a data race. So mimic it manually. 158 try { 159 (void)sock_recv.RecvUntilTerminator('\n', timeout, interrupt, max_data); 160 } catch (const std::runtime_error& e) { 161 threw_as_expected = HasReason("too many bytes without a terminator")(e); 162 } 163 assert(threw_as_expected); 164 }); 165 166 BOOST_REQUIRE_NO_THROW(sock_send.SendComplete("1234567", timeout, interrupt)); 167 BOOST_REQUIRE_NO_THROW(sock_send.SendComplete("89a\n", timeout, interrupt)); 168 169 receiver.join(); 170 } 171 172 #endif /* WIN32 */ 173 174 BOOST_AUTO_TEST_SUITE_END()