unix_domain_socket_unittest.cc 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. // Copyright (C) 2017 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this
  5. // file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6. #include <config.h>
  7. #include <asiolink/asio_wrapper.h>
  8. #include <asiolink/io_service.h>
  9. #include <asiolink/unix_domain_socket.h>
  10. #include <asiolink/testutils/test_server_unix_socket.h>
  11. #include <gtest/gtest.h>
  12. #include <array>
  13. #include <cstdio>
  14. #include <cstdlib>
  15. #include <sstream>
  16. #include <string>
  17. using namespace isc::asiolink;
  18. namespace {
  19. /// @brief Test unix socket file name.
  20. const std::string TEST_SOCKET = "test-socket";
  21. /// @brief Test timeout in ms.
  22. const long TEST_TIMEOUT = 10000;
  23. /// @brief Test fixture class for @ref UnixDomainSocket class.
  24. class UnixDomainSocketTest : public ::testing::Test {
  25. public:
  26. /// @brief Constructor.
  27. ///
  28. /// Removes unix socket descriptor before the test.
  29. UnixDomainSocketTest() :
  30. io_service_(),
  31. test_socket_(new test::TestServerUnixSocket(io_service_,
  32. unixSocketFilePath())) {
  33. test_socket_->startTimer(TEST_TIMEOUT);
  34. removeUnixSocketFile();
  35. }
  36. /// @brief Destructor.
  37. ///
  38. /// Removes unix socket descriptor after the test.
  39. virtual ~UnixDomainSocketTest() {
  40. removeUnixSocketFile();
  41. }
  42. /// @brief Returns socket file path.
  43. ///
  44. /// If the KEA_SOCKET_TEST_DIR environment variable is specified, the
  45. /// socket file is created in the location pointed to by this variable.
  46. /// Otherwise, it is created in the build directory.
  47. static std::string unixSocketFilePath() {
  48. std::ostringstream s;
  49. const char* env = getenv("KEA_SOCKET_TEST_DIR");
  50. if (env) {
  51. s << std::string(env);
  52. } else {
  53. s << TEST_DATA_BUILDDIR;
  54. }
  55. s << "/" << TEST_SOCKET;
  56. return (s.str());
  57. }
  58. /// @brief Removes unix socket descriptor.
  59. void removeUnixSocketFile() {
  60. static_cast<void>(remove(unixSocketFilePath().c_str()));
  61. }
  62. /// @brief IO service used by the tests.
  63. IOService io_service_;
  64. /// @brief Server side unix socket used in these tests.
  65. test::TestServerUnixSocketPtr test_socket_;
  66. };
  67. // This test verifies that the client can send data over the unix
  68. // domain socket and receive a response.
  69. TEST_F(UnixDomainSocketTest, sendReceive) {
  70. // Start the server.
  71. test_socket_->bindServerSocket();
  72. // Setup client side.
  73. UnixDomainSocket socket(io_service_);
  74. ASSERT_NO_THROW(socket.connect(unixSocketFilePath()));
  75. // Send "foo".
  76. const std::string outbound_data = "foo";
  77. size_t sent_size = 0;
  78. ASSERT_NO_THROW(sent_size = socket.write(outbound_data.c_str(),
  79. outbound_data.size()));
  80. // Make sure all data have been sent.
  81. ASSERT_EQ(outbound_data.size(), sent_size);
  82. // Run IO service to generate server's response.
  83. while ((test_socket_->getResponseNum() < 1) &&
  84. (!test_socket_->isStopped())) {
  85. io_service_.run_one();
  86. }
  87. // Receive response from the socket.
  88. std::array<char, 1024> read_buf;
  89. size_t bytes_read = 0;
  90. ASSERT_NO_THROW(bytes_read = socket.receive(&read_buf[0], read_buf.size()));
  91. std::string response(&read_buf[0], bytes_read);
  92. // The server should prepend "received" to the data we had sent.
  93. EXPECT_EQ("received foo", response);
  94. }
  95. // This test verifies that the client can send the data over the unix
  96. // domain socket and receive a response asynchronously.
  97. TEST_F(UnixDomainSocketTest, asyncSendReceive) {
  98. // Start the server.
  99. test_socket_->bindServerSocket();
  100. // Setup client side.
  101. UnixDomainSocket socket(io_service_);
  102. // We're going to asynchronously connect to the server. The boolean value
  103. // below will be modified by the connect handler function (lambda) invoked
  104. // when the connection is established or if an error occurs.
  105. bool connect_handler_invoked = false;
  106. ASSERT_NO_THROW(socket.asyncConnect(unixSocketFilePath(),
  107. [this, &connect_handler_invoked](const boost::system::error_code& ec) {
  108. // Indicate that the handler has been called so as the loop below gets
  109. // interrupted.
  110. connect_handler_invoked = true;
  111. // Operation aborted indicates that IO service has been stopped. This
  112. // shouldn't happen here.
  113. if (ec && (ec.value() != boost::asio::error::operation_aborted)) {
  114. ADD_FAILURE() << "error occurred while asynchronously connecting"
  115. " via unix domain socket: " << ec.message();
  116. }
  117. }
  118. ));
  119. // Run IO service until connect handler is invoked.
  120. while (!connect_handler_invoked && (!test_socket_->isStopped())) {
  121. io_service_.run_one();
  122. }
  123. // We are going to asynchronously send the 'foo' over the unix socket.
  124. const std::string outbound_data = "foo";
  125. size_t sent_size = 0;
  126. ASSERT_NO_THROW(socket.asyncSend(outbound_data.c_str(), outbound_data.size(),
  127. [this, &sent_size](const boost::system::error_code& ec, size_t length) {
  128. // If we have been successful sending the data, record the number of
  129. // bytes we have sent.
  130. if (!ec) {
  131. sent_size = length;
  132. } else if (ec.value() != boost::asio::error::operation_aborted) {
  133. ADD_FAILURE() << "error occurred while asynchronously sending the"
  134. " data over unix domain socket: " << ec.message();
  135. }
  136. }
  137. ));
  138. // Run IO service to generate server's response.
  139. while ((test_socket_->getResponseNum() < 1) &&
  140. (!test_socket_->isStopped())) {
  141. io_service_.run_one();
  142. }
  143. // There is no guarantee that all data have been sent so we only check that
  144. // some data have been sent.
  145. ASSERT_GT(sent_size, 0);
  146. // Receive response from the socket. Very small receive buffer ensures that
  147. // we will read the response in chunks.
  148. std::array<char, 2> read_buf;
  149. size_t bytes_read = 0;
  150. std::string response;
  151. std::string expected_response = "received foo";
  152. // Run IO service until we get the full response from the server.
  153. while ((bytes_read < expected_response.size()) && !test_socket_->isStopped()) {
  154. ASSERT_NO_THROW(socket.asyncReceive(&read_buf[0], read_buf.size(),
  155. [this, &read_buf, &response, &bytes_read]
  156. (const boost::system::error_code& ec, size_t length) {
  157. // If we have been successful receiving the data, record the number of
  158. // bytes received.
  159. if (!ec) {
  160. bytes_read += length;
  161. response.append(&read_buf[0], length);
  162. } else if (ec.value() != boost::asio::error::operation_aborted) {
  163. ADD_FAILURE() << "error occurred while asynchronously receiving"
  164. " data via unix domain socket: " << ec.message();
  165. }
  166. }));
  167. io_service_.run_one();
  168. }
  169. // Make sure we have received something.
  170. ASSERT_GT(bytes_read, 0);
  171. // Check that the entire response has been received and is correct.
  172. EXPECT_EQ(expected_response, response);
  173. }
  174. // This test verifies that UnixDomainSocketError exception is thrown
  175. // on attempt to connect, write or receive when the server socket
  176. // is not available.
  177. TEST_F(UnixDomainSocketTest, clientErrors) {
  178. UnixDomainSocket socket(io_service_);
  179. ASSERT_THROW(socket.connect(unixSocketFilePath()), UnixDomainSocketError);
  180. const std::string outbound_data = "foo";
  181. ASSERT_THROW(socket.write(outbound_data.c_str(), outbound_data.size()),
  182. UnixDomainSocketError);
  183. std::array<char, 1024> read_buf;
  184. ASSERT_THROW(socket.receive(&read_buf[0], read_buf.size()),
  185. UnixDomainSocketError);
  186. }
  187. // This test verifies that an error is returned on attempt to asynchronously
  188. // connect, write or receive when the server socket is not available.
  189. TEST_F(UnixDomainSocketTest, asyncClientErrors) {
  190. UnixDomainSocket socket(io_service_);
  191. // Asynchronous operations signal errors through boost::system::error_code
  192. // object passed to the handler function. This object casts to boolean.
  193. // In case of success the object casts to false. In case of an error it
  194. // casts to true. The actual error codes can be retrieved by comparing the
  195. // ec objects to predefined error objects. We don't check for the actual
  196. // errors here, because it is not certain that the same error codes would
  197. // be returned on various operating systems.
  198. // In the following tests we use C++11 lambdas as callbacks.
  199. // Connect
  200. bool connect_handler_invoked = false;
  201. socket.asyncConnect(unixSocketFilePath(),
  202. [this, &connect_handler_invoked](const boost::system::error_code& ec) {
  203. connect_handler_invoked = true;
  204. EXPECT_TRUE(ec);
  205. });
  206. while (!connect_handler_invoked && !test_socket_->isStopped()) {
  207. io_service_.run_one();
  208. }
  209. // Send
  210. const std::string outbound_data = "foo";
  211. bool send_handler_invoked = false;
  212. socket.asyncSend(outbound_data.c_str(), outbound_data.size(),
  213. [this, &send_handler_invoked]
  214. (const boost::system::error_code& ec, size_t length) {
  215. send_handler_invoked = true;
  216. EXPECT_TRUE(ec);
  217. });
  218. while (!send_handler_invoked && !test_socket_->isStopped()) {
  219. io_service_.run_one();
  220. }
  221. // Receive
  222. bool receive_handler_invoked = false;
  223. std::array<char, 1024> read_buf;
  224. socket.asyncReceive(&read_buf[0], read_buf.size(),
  225. [this, &receive_handler_invoked]
  226. (const boost::system::error_code& ec, size_t length) {
  227. receive_handler_invoked = true;
  228. EXPECT_TRUE(ec);
  229. });
  230. while (!receive_handler_invoked && !test_socket_->isStopped()) {
  231. io_service_.run_one();
  232. }
  233. }
  234. // Check that native socket descriptor is returned correctly when
  235. // the socket is connected.
  236. TEST_F(UnixDomainSocketTest, getNative) {
  237. // Start the server.
  238. test_socket_->bindServerSocket();
  239. // Setup client side.
  240. UnixDomainSocket socket(io_service_);
  241. ASSERT_NO_THROW(socket.connect(unixSocketFilePath()));
  242. ASSERT_GE(socket.getNative(), 0);
  243. }
  244. // Check that protocol returned is 0.
  245. TEST_F(UnixDomainSocketTest, getProtocol) {
  246. UnixDomainSocket socket(io_service_);
  247. EXPECT_EQ(0, socket.getProtocol());
  248. }
  249. }