client_connection_unittests.cc 6.0 KB

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