From: Razvan Becheriu Date: Tue, 16 Feb 2021 16:56:56 +0000 (+0200) Subject: [#899] added missing files X-Git-Tag: Kea-1.9.5~59 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=37d0c660db16874bf37e0f33a9c98bd84044fa0a;p=thirdparty%2Fkea.git [#899] added missing files --- diff --git a/src/lib/asiolink/tests/.gitignore b/src/lib/asiolink/tests/.gitignore index d6d1ec87a1..6a0c5b5de1 100644 --- a/src/lib/asiolink/tests/.gitignore +++ b/src/lib/asiolink/tests/.gitignore @@ -1 +1,2 @@ /run_unittests +/process_spawn_app.sh diff --git a/src/lib/asiolink/testutils/timed_signal.cc b/src/lib/asiolink/testutils/timed_signal.cc new file mode 100644 index 0000000000..7f7007d018 --- /dev/null +++ b/src/lib/asiolink/testutils/timed_signal.cc @@ -0,0 +1,331 @@ +// Copyright (C) 2017-2020 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace boost::asio::local; +namespace ph = std::placeholders; + +namespace isc { +namespace asiolink { +namespace test { + +/// @brief ASIO unix domain socket. +typedef stream_protocol::socket UnixSocket; + +/// @brief Pointer to the ASIO unix domain socket. +typedef boost::shared_ptr UnixSocketPtr; + +/// @brief Callback function invoked when response is sent from the server. +typedef std::function SentResponseCallback; + +/// @brief Connection to the server over unix domain socket. +/// +/// It reads the data over the socket, sends responses and closes a socket. +class Connection : public boost::enable_shared_from_this { +public: + + /// @brief Constructor. + /// + /// It starts asynchronous read operation. + /// + /// @param unix_socket Pointer to the unix domain socket into which + /// connection has been accepted. + /// @param custom_response Custom response that the server should send. + /// @param sent_response_callback Callback function to be invoked when + /// server sends a response. + Connection(const UnixSocketPtr& unix_socket, + const std::string custom_response, + SentResponseCallback sent_response_callback) + : socket_(unix_socket), custom_response_(custom_response), + sent_response_callback_(sent_response_callback) { + } + + /// @brief Starts asynchronous read from the socket. + void start() { + socket_->async_read_some(boost::asio::buffer(&raw_buf_[0], raw_buf_.size()), + std::bind(&Connection::readHandler, shared_from_this(), + ph::_1, // error + ph::_2)); // bytes_transferred + } + + /// @brief Closes the socket. + void stop() { + try { + socket_->close(); + + } catch (...) { + // ignore errors when closing the socket. + } + } + + /// @brief Handler invoked when data have been received over the socket. + /// + /// This is the handler invoked when the data have been received over the + /// socket. If custom response has been specified, this response is sent + /// back to the client. Otherwise, the handler echoes back the request + /// and prepends the word "received ". Finally, it calls a custom + /// callback function (specified in the constructor) to notify that the + /// response has been sent over the socket. + /// + /// @param bytes_transferred Number of bytes received. + void + readHandler(const boost::system::error_code& ec, + size_t bytes_transferred) { + // This is most likely due to the abort. + if (ec) { + // An error occurred so let's close the socket. + stop(); + return; + } + + if (!custom_response_.empty()) { + boost::asio::write(*socket_, + boost::asio::buffer(custom_response_.c_str(), custom_response_.size())); + + } else { + std::string received(&raw_buf_[0], bytes_transferred); + std::string response("received " + received); + boost::asio::write(*socket_, + boost::asio::buffer(response.c_str(), response.size())); + } + + /// @todo We're taking simplistic approach and send a response right away + /// after receiving data over the socket. Therefore, after responding we + /// do not schedule another read. We could extend this logic slightly to + /// parse the received data and see when we've got enough data before we + /// send a response. However, the current unit tests don't really require + /// that. + + // Invoke callback function to notify that the response has been sent. + sent_response_callback_(); + } + +private: + + /// @brief Pointer to the unix domain socket. + UnixSocketPtr socket_; + + /// @brief Custom response to be sent to the client. + std::string custom_response_; + + /// @brief Receive buffer. + std::array raw_buf_; + + /// @brief Pointer to the callback function to be invoked when response + /// has been sent. + SentResponseCallback sent_response_callback_; + +}; + +/// @brief Pointer to a Connection object. +typedef boost::shared_ptr ConnectionPtr; + +/// @brief Connection pool. +/// +/// Holds all connections established with the server and gracefully +/// terminates these connections. +class ConnectionPool { +public: + + /// @brief Constructor. + /// + /// @param io_service Reference to the IO service. + ConnectionPool(IOService& io_service) + : io_service_(io_service), connections_(), next_socket_(), + response_num_(0) { + } + + /// @brief Destructor. + ~ConnectionPool() { + stopAll(); + } + + /// @brief Creates new unix domain socket and returns it. + /// + /// This convenience method creates a socket which can be used to accept + /// new connections. If such socket already exists, it is returned. + /// + /// @return Pointer to the socket. + UnixSocketPtr getSocket() { + if (!next_socket_) { + next_socket_.reset(new UnixSocket(io_service_.get_io_service())); + } + return (next_socket_); + } + + /// @brief Starts new connection. + /// + /// The socket returned by the @ref ConnectionPool::getSocket is used to + /// create new connection. Then, the @ref next_socket_ is reset, to force + /// the @ref ConnectionPool::getSocket to generate a new socket for a + /// next connection. + /// + /// @param custom_response Custom response to be sent to the client. + void start(const std::string& custom_response) { + ConnectionPtr conn(new Connection(next_socket_, custom_response, [this] { + ++response_num_; + })); + conn->start(); + + connections_.insert(conn); + next_socket_.reset(); + } + + /// @brief Stops the given connection. + /// + /// @param conn Pointer to the connection to be stopped. + void stop(const ConnectionPtr& conn) { + conn->stop(); + connections_.erase(conn); + } + + /// @brief Stops all connections. + void stopAll() { + for (auto conn = connections_.begin(); conn != connections_.end(); + ++conn) { + (*conn)->stop(); + } + connections_.clear(); + } + + /// @brief Returns number of responses sent so far. + size_t getResponseNum() const { + return (response_num_); + } + +private: + + /// @brief Reference to the IO service. + IOService& io_service_; + + /// @brief Container holding established connections. + std::set connections_; + + /// @brief Holds pointer to the generated socket. + /// + /// This socket will be used by the next connection. + UnixSocketPtr next_socket_; + + /// @brief Holds the number of sent responses. + size_t response_num_; +}; + + +TestServerUnixSocket::TestServerUnixSocket(IOService& io_service, + const std::string& socket_file_path, + const std::string& custom_response) + : io_service_(io_service), + server_endpoint_(socket_file_path), + server_acceptor_(io_service_.get_io_service()), + test_timer_(io_service_), + custom_response_(custom_response), + connection_pool_(new ConnectionPool(io_service)), + stopped_(false), + running_(false) { +} + +TestServerUnixSocket::~TestServerUnixSocket() { + server_acceptor_.close(); +} + +void +TestServerUnixSocket::generateCustomResponse(const uint64_t response_size) { + std::ostringstream s; + s << "{"; + while (s.tellp() < response_size) { + s << "\"param\": \"value\","; + } + s << "}"; + custom_response_ = s.str(); +} + +void +TestServerUnixSocket::startTimer(const long test_timeout) { + test_timer_.setup(std::bind(&TestServerUnixSocket::timeoutHandler, this), + test_timeout, IntervalTimer::ONE_SHOT); +} + +void +TestServerUnixSocket::stopServer() { + test_timer_.cancel(); + server_acceptor_.cancel(); + connection_pool_->stopAll(); +} + +void +TestServerUnixSocket::bindServerSocket(const bool use_thread) { + server_acceptor_.open(); + server_acceptor_.bind(server_endpoint_); + server_acceptor_.listen(); + accept(); + + // When threads are in use, we need to post a handler which will be invoked + // when the thread has already started and the IO service is running. The + // main thread can move forward when it receives this signal from the handler. + if (use_thread) { + io_service_.post(std::bind(&TestServerUnixSocket::signalRunning, + this)); + } +} + +void +TestServerUnixSocket::acceptHandler(const boost::system::error_code& ec) { + if (ec) { + return; + } + + connection_pool_->start(custom_response_); + accept(); +} + +void +TestServerUnixSocket::accept() { + server_acceptor_.async_accept(*(connection_pool_->getSocket()), + std::bind(&TestServerUnixSocket::acceptHandler, this, + ph::_1)); // error +} + +void +TestServerUnixSocket::signalRunning() { + { + std::lock_guard lock(mutex_); + running_ = true; + } + condvar_.notify_one(); +} + +void +TestServerUnixSocket::waitForRunning() { + std::unique_lock lock(mutex_); + while (!running_) { + condvar_.wait(lock); + } +} + +void +TestServerUnixSocket::timeoutHandler() { + ADD_FAILURE() << "Timeout occurred while running the test!"; + io_service_.stop(); + stopped_ = true; +} + +size_t +TestServerUnixSocket::getResponseNum() const { + return (connection_pool_->getResponseNum()); +} + +} // end of namespace isc::asiolink::test +} // end of namespace isc::asiolink +} // end of namespace isc diff --git a/src/lib/asiolink/testutils/timed_signal.h b/src/lib/asiolink/testutils/timed_signal.h new file mode 100644 index 0000000000..3af545df38 --- /dev/null +++ b/src/lib/asiolink/testutils/timed_signal.h @@ -0,0 +1,86 @@ +// Copyright (C) 2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef TIMED_SIGNAL_H +#define TIMED_SIGNAL_H + +#include + +#include +#include +#include + +namespace isc { +namespace asiolink { +namespace test { + +/// @brief Implements a time-delayed signal +/// +/// Given an IOService, a signal number, and a time period, this class will +/// send (raise) the signal to the current process. +class TimedSignal { +public: + /// @brief Constructor + /// + /// @param io_service IOService to run the timer + /// @param signum OS signal value (e.g. SIGHUP, SIGUSR1 ...) + /// @param milliseconds time in milliseconds to wait until the signal is + /// raised. + /// @param mode selects between a one-shot signal or a signal which repeats + /// at "milliseconds" interval. + TimedSignal(asiolink::IOService& io_service, int signum, int milliseconds, + const asiolink::IntervalTimer::Mode& mode = + asiolink::IntervalTimer::ONE_SHOT) + : timer_(new asiolink::IntervalTimer(io_service)) { + timer_->setup(SendSignalCallback(signum), milliseconds, mode); + } + + /// @brief Cancels the given timer. + void cancel() { + if (timer_) { + timer_->cancel(); + } + } + + /// @brief Destructor. + ~TimedSignal() { + cancel(); + } + + /// @brief Callback for the TimeSignal's internal timer. + class SendSignalCallback: public std::unary_function { + public: + + /// @brief Constructor + /// + /// @param signum OS signal value of the signal to send + SendSignalCallback(int signum) : signum_(signum) { + } + + /// @brief Callback method invoked when the timer expires + /// + /// Calls raise with the given signal which should generate that + /// signal to the given process. + void operator()() { + ASSERT_EQ(0, raise(signum_)); + return; + } + + private: + /// @brief Stores the OS signal value to send. + int signum_; + }; + +private: + /// @brief Timer which controls when the signal is sent. + asiolink::IntervalTimerPtr timer_; +}; + +} // end of namespace isc::asiolink::test +} // end of namespace isc::asiolink +} // end of namespace isc + +#endif // TIMED_SIGNAL_H diff --git a/src/lib/util/tests/.gitignore b/src/lib/util/tests/.gitignore index 6a0c5b5de1..d6d1ec87a1 100644 --- a/src/lib/util/tests/.gitignore +++ b/src/lib/util/tests/.gitignore @@ -1,2 +1 @@ /run_unittests -/process_spawn_app.sh