mirror of
https://gitlab.isc.org/isc-projects/kea
synced 2025-09-01 06:25:34 +00:00
[5318] Implemented timeouts for CommandMgr.
This commit is contained in:
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
|
|
||||||
|
#include <asiolink/interval_timer.h>
|
||||||
#include <asiolink/io_service.h>
|
#include <asiolink/io_service.h>
|
||||||
#include <cc/command_interpreter.h>
|
#include <cc/command_interpreter.h>
|
||||||
#include <config/command_mgr.h>
|
#include <config/command_mgr.h>
|
||||||
@@ -1301,5 +1302,54 @@ TEST_F(CtrlChannelDhcpv4SrvTest, longResponse) {
|
|||||||
EXPECT_EQ(reference_response, response.str());
|
EXPECT_EQ(reference_response, response.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This test verifies that the server signals timeout if the transmission
|
||||||
|
// takes too long.
|
||||||
|
TEST_F(CtrlChannelDhcpv4SrvTest, connectionTimeout) {
|
||||||
|
createUnixChannelServer();
|
||||||
|
|
||||||
|
// Server's response will be assigned to this variable.
|
||||||
|
std::string response;
|
||||||
|
|
||||||
|
// It is useful to create a thread and run the server and the client
|
||||||
|
// at the same time and independently.
|
||||||
|
std::thread th([this, &response]() {
|
||||||
|
|
||||||
|
// IO service will be stopped automatically when this object goes
|
||||||
|
// out of scope and is destroyed. This is useful because we use
|
||||||
|
// asserts which may break the thread in various exit points.
|
||||||
|
IOServiceWork work(getIOService());
|
||||||
|
|
||||||
|
// Create the client and connect it to the server.
|
||||||
|
boost::scoped_ptr<UnixControlClient> client(new UnixControlClient());
|
||||||
|
ASSERT_TRUE(client);
|
||||||
|
ASSERT_TRUE(client->connectToServer(socket_path_));
|
||||||
|
|
||||||
|
// Send partial command. The server will be waiting for the remaining
|
||||||
|
// part to be sent and will eventually signal a timeout.
|
||||||
|
std::string command = "{ \"command\": \"foo\" ";
|
||||||
|
ASSERT_TRUE(client->sendCommand(command));
|
||||||
|
|
||||||
|
// Let's wait up to 10s for the server's response. The response
|
||||||
|
// should arrive sooner assuming that the timeout mechanism for
|
||||||
|
// the server is working properly.
|
||||||
|
const unsigned int timeout = 10;
|
||||||
|
ASSERT_TRUE(client->getResponse(response, 10));
|
||||||
|
|
||||||
|
// Explicitly close the client's connection.
|
||||||
|
client->disconnectFromServer();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Run the server until stopped.
|
||||||
|
getIOService()->run();
|
||||||
|
|
||||||
|
// Wait for the thread to return.
|
||||||
|
th.join();
|
||||||
|
|
||||||
|
// Check that the server has signalled a timeout.
|
||||||
|
EXPECT_EQ("{ \"result\": 1, \"text\": \"Connection over control channel"
|
||||||
|
" timed out\" }", response);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} // End of anonymous namespace
|
} // End of anonymous namespace
|
||||||
|
@@ -5,6 +5,7 @@
|
|||||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
#include <asiolink/asio_wrapper.h>
|
#include <asiolink/asio_wrapper.h>
|
||||||
|
#include <asiolink/interval_timer.h>
|
||||||
#include <asiolink/io_service.h>
|
#include <asiolink/io_service.h>
|
||||||
#include <asiolink/unix_domain_socket.h>
|
#include <asiolink/unix_domain_socket.h>
|
||||||
#include <asiolink/unix_domain_socket_acceptor.h>
|
#include <asiolink/unix_domain_socket_acceptor.h>
|
||||||
@@ -30,6 +31,11 @@ namespace {
|
|||||||
/// @brief Maximum size of the data chunk sent/received over the socket.
|
/// @brief Maximum size of the data chunk sent/received over the socket.
|
||||||
const size_t BUF_SIZE = 8192;
|
const size_t BUF_SIZE = 8192;
|
||||||
|
|
||||||
|
/// @brief Specifies connection timeout in milliseconds.
|
||||||
|
///
|
||||||
|
/// @todo Make it configurable.
|
||||||
|
const unsigned CONNECTION_TIMEOUT = 5000;
|
||||||
|
|
||||||
class ConnectionPool;
|
class ConnectionPool;
|
||||||
|
|
||||||
/// @brief Represents a single connection over control socket.
|
/// @brief Represents a single connection over control socket.
|
||||||
@@ -49,10 +55,12 @@ public:
|
|||||||
/// for data transmission.
|
/// for data transmission.
|
||||||
/// @param connection_pool Reference to the connection pool to which this
|
/// @param connection_pool Reference to the connection pool to which this
|
||||||
/// connection belongs.
|
/// connection belongs.
|
||||||
Connection(const boost::shared_ptr<UnixDomainSocket>& socket,
|
Connection(const IOServicePtr& io_service,
|
||||||
|
const boost::shared_ptr<UnixDomainSocket>& socket,
|
||||||
ConnectionPool& connection_pool)
|
ConnectionPool& connection_pool)
|
||||||
: socket_(socket), buf_(), response_(), connection_pool_(connection_pool),
|
: socket_(socket), timeout_timer_(*io_service), buf_(), response_(),
|
||||||
feed_(), response_in_progress_(false) {
|
connection_pool_(connection_pool), feed_(),
|
||||||
|
response_in_progress_(false) {
|
||||||
|
|
||||||
LOG_INFO(command_logger, COMMAND_SOCKET_CONNECTION_OPENED)
|
LOG_INFO(command_logger, COMMAND_SOCKET_CONNECTION_OPENED)
|
||||||
.arg(socket_->getNative());
|
.arg(socket_->getNative());
|
||||||
@@ -62,6 +70,17 @@ public:
|
|||||||
isc::dhcp::IfaceMgr::instance().addExternalSocket(socket_->getNative(), 0);
|
isc::dhcp::IfaceMgr::instance().addExternalSocket(socket_->getNative(), 0);
|
||||||
// Initialize state model for receiving and preparsing commands.
|
// Initialize state model for receiving and preparsing commands.
|
||||||
feed_.initModel();
|
feed_.initModel();
|
||||||
|
|
||||||
|
// Start timer for detecting timeouts.
|
||||||
|
timeout_timer_.setup(boost::bind(&Connection::timeoutHandler, this),
|
||||||
|
CONNECTION_TIMEOUT, IntervalTimer::ONE_SHOT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Destructor.
|
||||||
|
///
|
||||||
|
/// Cancels timeout timer if one is scheduled.
|
||||||
|
~Connection() {
|
||||||
|
timeout_timer_.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Close current connection.
|
/// @brief Close current connection.
|
||||||
@@ -77,6 +96,7 @@ public:
|
|||||||
|
|
||||||
isc::dhcp::IfaceMgr::instance().deleteExternalSocket(socket_->getNative());
|
isc::dhcp::IfaceMgr::instance().deleteExternalSocket(socket_->getNative());
|
||||||
socket_->close();
|
socket_->close();
|
||||||
|
timeout_timer_.cancel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,11 +138,18 @@ public:
|
|||||||
/// @param bytes_transferred Number of bytes sent.
|
/// @param bytes_transferred Number of bytes sent.
|
||||||
void sendHandler(const boost::system::error_code& ec,
|
void sendHandler(const boost::system::error_code& ec,
|
||||||
size_t bytes_trasferred);
|
size_t bytes_trasferred);
|
||||||
|
|
||||||
|
/// @brief Handler invoked when timeout has occurred.
|
||||||
|
void timeoutHandler();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
/// @brief Pointer to the socket used for transmission.
|
/// @brief Pointer to the socket used for transmission.
|
||||||
boost::shared_ptr<UnixDomainSocket> socket_;
|
boost::shared_ptr<UnixDomainSocket> socket_;
|
||||||
|
|
||||||
|
/// @brief Interval timer used to detect connection timeouts.
|
||||||
|
IntervalTimer timeout_timer_;
|
||||||
|
|
||||||
/// @brief Buffer used for received data.
|
/// @brief Buffer used for received data.
|
||||||
std::array<char, BUF_SIZE> buf_;
|
std::array<char, BUF_SIZE> buf_;
|
||||||
|
|
||||||
@@ -299,6 +326,15 @@ Connection::sendHandler(const boost::system::error_code& ec,
|
|||||||
connection_pool_.stop(shared_from_this());
|
connection_pool_.stop(shared_from_this());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Connection::timeoutHandler() {
|
||||||
|
ConstElementPtr rsp = createAnswer(CONTROL_RESULT_ERROR, "Connection over"
|
||||||
|
" control channel timed out");
|
||||||
|
response_ = rsp->str();
|
||||||
|
doSend();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace isc {
|
namespace isc {
|
||||||
@@ -400,7 +436,8 @@ CommandMgrImpl::doAccept() {
|
|||||||
acceptor_->asyncAccept(*socket_, [this](const boost::system::error_code& ec) {
|
acceptor_->asyncAccept(*socket_, [this](const boost::system::error_code& ec) {
|
||||||
if (!ec) {
|
if (!ec) {
|
||||||
// New connection is arriving. Start asynchronous transmission.
|
// New connection is arriving. Start asynchronous transmission.
|
||||||
ConnectionPtr connection(new Connection(socket_, connection_pool_));
|
ConnectionPtr connection(new Connection(io_service_, socket_,
|
||||||
|
connection_pool_));
|
||||||
connection_pool_.start(connection);
|
connection_pool_.start(connection);
|
||||||
|
|
||||||
} else if (ec.value() != boost::asio::error::operation_aborted) {
|
} else if (ec.value() != boost::asio::error::operation_aborted) {
|
||||||
|
Reference in New Issue
Block a user