client_connection_unittests.cc 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  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/testutils/test_server_unix_socket.h>
  10. #include <cc/json_feed.h>
  11. #include <config/client_connection.h>
  12. #include <gtest/gtest.h>
  13. #include <cstdlib>
  14. #include <sstream>
  15. #include <string>
  16. using namespace isc::asiolink;
  17. using namespace isc::config;
  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. class ClientConnectionTest : public ::testing::Test {
  24. public:
  25. /// @brief Constructor.
  26. ///
  27. /// Removes unix socket descriptor before the test.
  28. ClientConnectionTest() :
  29. io_service_(),
  30. test_socket_(new test::TestServerUnixSocket(io_service_,
  31. unixSocketFilePath())) {
  32. removeUnixSocketFile();
  33. }
  34. /// @brief Destructor.
  35. ///
  36. /// Removes unix socket descriptor after the test.
  37. virtual ~ClientConnectionTest() {
  38. removeUnixSocketFile();
  39. }
  40. /// @brief Returns socket file path.
  41. ///
  42. /// If the KEA_SOCKET_TEST_DIR environment variable is specified, the
  43. /// socket file is created in the location pointed to by this variable.
  44. /// Otherwise, it is created in the build directory.
  45. static std::string unixSocketFilePath() {
  46. std::ostringstream s;
  47. const char* env = getenv("KEA_SOCKET_TEST_DIR");
  48. if (env) {
  49. s << std::string(env);
  50. } else {
  51. s << TEST_DATA_BUILDDIR;
  52. }
  53. s << "/" << TEST_SOCKET;
  54. return (s.str());
  55. }
  56. /// @brief Removes unix socket descriptor.
  57. void removeUnixSocketFile() {
  58. static_cast<void>(remove(unixSocketFilePath().c_str()));
  59. }
  60. /// @brief IO service used by the tests.
  61. IOService io_service_;
  62. /// @brief Server side unix socket used in these tests.
  63. test::TestServerUnixSocketPtr test_socket_;
  64. };
  65. // Tests successful transaction: connect, send command and receive a
  66. // response.
  67. TEST_F(ClientConnectionTest, success) {
  68. // Start timer protecting against test timeouts.
  69. test_socket_->startTimer(TEST_TIMEOUT);
  70. // Start the server.
  71. test_socket_->bindServerSocket();
  72. test_socket_->generateCustomResponse(2048);
  73. // Create some valid command.
  74. std::string command = "{ \"command\": \"list-commands\" }";
  75. ClientConnection conn(io_service_);
  76. // This boolean value will indicate when the callback function is invoked
  77. // at the end of the transaction (whether it is successful or unsuccessful).
  78. bool handler_invoked = false;
  79. conn.start(ClientConnection::SocketPath(unixSocketFilePath()),
  80. ClientConnection::ControlCommand(command),
  81. [this, &handler_invoked](const boost::system::error_code& ec,
  82. const ConstJSONFeedPtr& feed) {
  83. // Indicate that the handler has been called to break from the
  84. // while loop below.
  85. handler_invoked = true;
  86. // The ec should contain no error.
  87. ASSERT_FALSE(ec);
  88. // The JSONFeed should be present and it should contain a valid
  89. // response.
  90. ASSERT_TRUE(feed);
  91. EXPECT_TRUE(feed->feedOk()) << feed->getErrorMessage();
  92. });
  93. // Run the connection.
  94. while (!handler_invoked && !test_socket_->isStopped()) {
  95. io_service_.run_one();
  96. }
  97. }
  98. // This test checks that a timeout is signalled when the communication
  99. // takes too long.
  100. TEST_F(ClientConnectionTest, timeout) {
  101. // The server will return only partial JSON response (lacking closing
  102. // brace). The client will wait for closing brace and eventually the
  103. // connection should time out.
  104. test_socket_.reset(new test::TestServerUnixSocket(io_service_,
  105. unixSocketFilePath(),
  106. "{ \"command\": \"foo\""));
  107. test_socket_->startTimer(TEST_TIMEOUT);
  108. // Start the server.
  109. test_socket_->bindServerSocket();
  110. // Command to be sent to the server.
  111. std::string command = "{ \"command\": \"list-commands\" }";
  112. ClientConnection conn(io_service_);
  113. // This boolean value will be set to true when the callback is invoked.
  114. bool handler_invoked = false;
  115. conn.start(ClientConnection::SocketPath(unixSocketFilePath()),
  116. ClientConnection::ControlCommand(command),
  117. [this, &handler_invoked](const boost::system::error_code& ec,
  118. const ConstJSONFeedPtr& feed) {
  119. // Indicate that the callback has been invoked to break the loop
  120. // below.
  121. handler_invoked = true;
  122. ASSERT_TRUE(ec);
  123. EXPECT_TRUE(ec.value() == boost::asio::error::timed_out);
  124. }, ClientConnection::Timeout(1000));
  125. while (!handler_invoked && !test_socket_->isStopped()) {
  126. io_service_.run_one();
  127. }
  128. }
  129. // This test checks that an error is returned when the client is unable
  130. // to connect to the server.
  131. TEST_F(ClientConnectionTest, connectionError) {
  132. // Create the new connection but do not bind the server socket.
  133. // The connection should be refused and an error returned.
  134. ClientConnection conn(io_service_);
  135. std::string command = "{ \"command\": \"list-commands\" }";
  136. bool handler_invoked = false;
  137. conn.start(ClientConnection::SocketPath(unixSocketFilePath()),
  138. ClientConnection::ControlCommand(command),
  139. [this, &handler_invoked](const boost::system::error_code& ec,
  140. const ConstJSONFeedPtr& feed) {
  141. handler_invoked = true;
  142. ASSERT_TRUE(ec);
  143. });
  144. while (!handler_invoked && !test_socket_->isStopped()) {
  145. io_service_.run_one();
  146. }
  147. }
  148. } // end of anonymous namespace