test_server_unix_socket.cc 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  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 <asiolink/asio_wrapper.h>
  7. #include <asiolink/testutils/test_server_unix_socket.h>
  8. #include <boost/bind.hpp>
  9. #include <boost/enable_shared_from_this.hpp>
  10. #include <boost/shared_ptr.hpp>
  11. #include <functional>
  12. #include <set>
  13. #include <sstream>
  14. using namespace boost::asio::local;
  15. namespace isc {
  16. namespace asiolink {
  17. namespace test {
  18. /// @brief ASIO unix domain socket.
  19. typedef stream_protocol::socket UnixSocket;
  20. /// @brief Pointer to the ASIO unix domain socket.
  21. typedef boost::shared_ptr<UnixSocket> UnixSocketPtr;
  22. /// @brief Callback function invoked when response is sent from the server.
  23. typedef std::function<void()> SentResponseCallback;
  24. /// @brief Connection to the server over unix domain socket.
  25. ///
  26. /// It reads the data over the socket, sends responses and closes a socket.
  27. class Connection : public boost::enable_shared_from_this<Connection> {
  28. public:
  29. /// @brief Constructor.
  30. ///
  31. /// It starts asynchronous read operation.
  32. ///
  33. /// @param unix_socket Pointer to the unix domain socket into which
  34. /// connection has been accepted.
  35. /// @param custom_response Custom response that the server should send.
  36. /// @param sent_response_callback Callback function to be invoked when
  37. /// server sends a response.
  38. Connection(const UnixSocketPtr& unix_socket,
  39. const std::string custom_response,
  40. SentResponseCallback sent_response_callback)
  41. : socket_(unix_socket), custom_response_(custom_response),
  42. sent_response_callback_(sent_response_callback) {
  43. }
  44. void start() {
  45. socket_->async_read_some(boost::asio::buffer(&raw_buf_[0], raw_buf_.size()),
  46. boost::bind(&Connection::readHandler, shared_from_this(),
  47. boost::asio::placeholders::error,
  48. boost::asio::placeholders::bytes_transferred));
  49. }
  50. /// @brief Handler invoked when data have been received over the socket.
  51. ///
  52. /// This is the handler invoked when the data have been received over the
  53. /// socket. If custom response has been specified, this response is sent
  54. /// back to the client. Otherwise, the handler echoes back the request
  55. /// and prepends the word "received ". Finally, it calls a custom
  56. /// callback function (specified in the constructor) to notify that the
  57. /// response has been sent over the socket.
  58. ///
  59. /// @param bytes_transferred Number of bytes received.
  60. void
  61. readHandler(const boost::system::error_code& ec,
  62. size_t bytes_transferred) {
  63. // This is most likely due to the abort.
  64. if (ec) {
  65. return;
  66. }
  67. if (!custom_response_.empty()) {
  68. boost::asio::write(*socket_,
  69. boost::asio::buffer(custom_response_.c_str(), custom_response_.size()));
  70. } else {
  71. std::string received(&raw_buf_[0], bytes_transferred);
  72. std::string response("received " + received);
  73. boost::asio::write(*socket_,
  74. boost::asio::buffer(response.c_str(), response.size()));
  75. }
  76. start();
  77. // Invoke callback function to notify that the response has been sent.
  78. sent_response_callback_();
  79. }
  80. private:
  81. /// @brief Pointer to the unix domain socket.
  82. UnixSocketPtr socket_;
  83. /// @brief Custom response to be sent to the client.
  84. std::string custom_response_;
  85. /// @brief Receive buffer.
  86. std::array<char, 1024> raw_buf_;
  87. /// @brief Pointer to the callback function to be invoked when response
  88. /// has been sent.
  89. SentResponseCallback sent_response_callback_;
  90. };
  91. /// @brief Pointer to a Connection object.
  92. typedef boost::shared_ptr<Connection> ConnectionPtr;
  93. /// @brief Connection pool.
  94. ///
  95. /// Holds all connections established with the server and gracefully
  96. /// terminates these connections.
  97. class ConnectionPool {
  98. public:
  99. /// @brief Constructor.
  100. ///
  101. /// @param io_service Reference to the IO service.
  102. ConnectionPool(IOService& io_service)
  103. : io_service_(io_service), next_socket_(),
  104. response_num_(0) {
  105. }
  106. /// @brief Destructor.
  107. ~ConnectionPool() {
  108. }
  109. /// @brief Creates new unix domain socket and returns it.
  110. ///
  111. /// This convenience method creates a socket which can be used to accept
  112. /// new connections. If such socket already exists, it is returned.
  113. ///
  114. /// @return Pointer to the socket.
  115. UnixSocketPtr getSocket() {
  116. if (!next_socket_) {
  117. next_socket_.reset(new UnixSocket(io_service_.get_io_service()));
  118. }
  119. return (next_socket_);
  120. }
  121. /// @brief Starts new connection.
  122. ///
  123. /// The socket returned by the @ref ConnectionPool::getSocket is used to
  124. /// create new connection. Then, the @ref next_socket_ is reset, to force
  125. /// the @ref ConnectionPool::getSocket to generate a new socket for a
  126. /// next connection.
  127. ///
  128. /// @param custom_response Custom response to be sent to the client.
  129. void start(const std::string& custom_response) {
  130. ConnectionPtr conn(new Connection(next_socket_, custom_response, [this] {
  131. ++response_num_;
  132. }));
  133. conn->start();
  134. next_socket_.reset();
  135. }
  136. /// @brief Returns number of responses sent so far.
  137. size_t getResponseNum() const {
  138. return (response_num_);
  139. }
  140. private:
  141. /// @brief Reference to the IO service.
  142. IOService& io_service_;
  143. /// @brief Holds pointer to the generated socket.
  144. ///
  145. /// This socket will be used by the next connection.
  146. UnixSocketPtr next_socket_;
  147. /// @brief Holds the number of sent responses.
  148. size_t response_num_;
  149. };
  150. TestServerUnixSocket::TestServerUnixSocket(IOService& io_service,
  151. const std::string& socket_file_path,
  152. const std::string& custom_response)
  153. : io_service_(io_service),
  154. server_endpoint_(socket_file_path),
  155. server_acceptor_(io_service_.get_io_service()),
  156. test_timer_(io_service_),
  157. custom_response_(custom_response),
  158. connection_pool_(new ConnectionPool(io_service)),
  159. stopped_(false) {
  160. }
  161. TestServerUnixSocket::~TestServerUnixSocket() {
  162. }
  163. void
  164. TestServerUnixSocket::generateCustomResponse(const uint64_t response_size) {
  165. std::ostringstream s;
  166. s << "{";
  167. while (s.tellp() < response_size) {
  168. s << "\"param\": \"value\",";
  169. }
  170. s << "}";
  171. custom_response_ = s.str();
  172. }
  173. void
  174. TestServerUnixSocket::startTimer(const long test_timeout) {
  175. test_timer_.setup(boost::bind(&TestServerUnixSocket::timeoutHandler, this),
  176. test_timeout, IntervalTimer::ONE_SHOT);
  177. }
  178. void
  179. TestServerUnixSocket::bindServerSocket() {
  180. server_acceptor_.open();
  181. server_acceptor_.bind(server_endpoint_);
  182. server_acceptor_.listen();
  183. accept();
  184. }
  185. void
  186. TestServerUnixSocket::acceptHandler(const boost::system::error_code& ec) {
  187. if (ec) {
  188. return;
  189. }
  190. connection_pool_->start(custom_response_);
  191. accept();
  192. }
  193. void
  194. TestServerUnixSocket::accept() {
  195. server_acceptor_.async_accept(*(connection_pool_->getSocket()),
  196. boost::bind(&TestServerUnixSocket::acceptHandler, this,
  197. boost::asio::placeholders::error));
  198. }
  199. void
  200. TestServerUnixSocket::timeoutHandler() {
  201. ADD_FAILURE() << "Timeout occurred while running the test!";
  202. io_service_.stop();
  203. stopped_ = true;
  204. }
  205. size_t
  206. TestServerUnixSocket::getResponseNum() const {
  207. return (connection_pool_->getResponseNum());
  208. }
  209. } // end of namespace isc::asiolink::test
  210. } // end of namespace isc::asiolink
  211. } // end of namespace isc