Commit e47da044 authored by Thomas Markwalder's avatar Thomas Markwalder

[5599] Control channel now emits info on any discarded data

doc/guide/ctrl-channel.xml
    Minor update to using socat

src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc
src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc
    Updated testing for server response to server
    side control channel timeouts

src/lib/cc/json_feed.h
        JSONFeed::getProcessedText() - new method that
        returns a copy of the current accumulation of
        accepted text

src/lib/config/config_messages.mes
src/lib/config/command_mgr.cc
    Connection::receiveHandler() - added log info about
    discarded data when client closes connection

    Connection::timeoutHandler() - added info about discarded
    data to server's response to client on server side timeout
parent 50c6675a
......@@ -227,9 +227,14 @@
such as <command>socat</command> and <command>curl</command>.</para>
<para>In order to control the given Kea service via unix domain socket, use
<command>socat</command> as follows:
<command>socat</command> in interactive mode as follows:
<screen>
$ socat UNIX:/path/to/the/kea/socket -
</screen>
or in batch mode, include the "ignoreeof" option as shown below to ensure
socat waits long enough for the server to respond:
<screen>
$ echo "{ some command...}" | socat UNIX:/path/to/the/kea/socket -,ignoreeof
</screen>
where <command>/path/to/the/kea/socket</command> is the path specified in the
<command>Dhcp4/control-socket/socket-name</command> parameter in the Kea
......
......@@ -1414,8 +1414,8 @@ TEST_F(CtrlChannelDhcpv4SrvTest, longResponse) {
}
// This test verifies that the server signals timeout if the transmission
// takes too long.
TEST_F(CtrlChannelDhcpv4SrvTest, connectionTimeout) {
// takes too long, after receiving a partial command.
TEST_F(CtrlChannelDhcpv4SrvTest, connectionTimeoutPartialCommand) {
createUnixChannelServer();
// Set connection timeout to 2s to prevent long waiting time for the
......@@ -1462,11 +1462,57 @@ TEST_F(CtrlChannelDhcpv4SrvTest, connectionTimeout) {
th.join();
// Check that the server has signalled a timeout.
EXPECT_EQ("{ \"result\": 1, \"text\": \"Connection over control channel"
" timed out\" }", response);
EXPECT_EQ("{ \"result\": 1, \"text\": "
"\"Connection over control channel timed out, "
"discarded partial command of 19 bytes\" }" , response);
}
// This test verifies that the server signals timeout if the transmission
// takes too long, having received no data from the client.
TEST_F(CtrlChannelDhcpv4SrvTest, connectionTimeoutNoData) {
createUnixChannelServer();
// Set connection timeout to 2s to prevent long waiting time for the
// timeout during this test.
const unsigned short timeout = 2;
CommandMgr::instance().setConnectionTimeout(timeout);
// 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_));
// Let's wait up to 15s 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 = 15;
ASSERT_TRUE(client->getResponse(response, timeout));
// 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
......@@ -1435,8 +1435,8 @@ TEST_F(CtrlChannelDhcpv6SrvTest, longResponse) {
}
// This test verifies that the server signals timeout if the transmission
// takes too long.
TEST_F(CtrlChannelDhcpv6SrvTest, connectionTimeout) {
// takes too long, having received a partial command.
TEST_F(CtrlChannelDhcpv6SrvTest, connectionTimeoutPartialCommand) {
createUnixChannelServer();
// Set connection timeout to 2s to prevent long waiting time for the
......@@ -1483,9 +1483,58 @@ TEST_F(CtrlChannelDhcpv6SrvTest, connectionTimeout) {
th.join();
// Check that the server has signalled a timeout.
EXPECT_EQ("{ \"result\": 1, \"text\": \"Connection over control channel"
" timed out\" }", response);
EXPECT_EQ("{ \"result\": 1, \"text\": "
"\"Connection over control channel timed out,"
" discarded partial command of 19 bytes\" }", response);
}
// This test verifies that the server signals timeout if the transmission
// takes too long, having received no data.
TEST_F(CtrlChannelDhcpv6SrvTest, connectionTimeoutNoData) {
createUnixChannelServer();
// Set connection timeout to 2s to prevent long waiting time for the
// timeout during this test.
const unsigned short timeout = 2;
CommandMgr::instance().setConnectionTimeout(timeout);
// 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_));
// Having sent nothing let's just wait and see if Server times us out.
// Let's wait up to 15s 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 = 15;
ASSERT_TRUE(client->getResponse(response, timeout));
// 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
......@@ -164,6 +164,11 @@ public:
return (error_message_);
}
/// @brief Returns the text parsed into the buffer.
std::string getProcessedText() const {
return (output_);
}
/// @brief Returns processed data as a structure of @ref isc::data::Element
/// objects.
///
......
......@@ -258,11 +258,19 @@ Connection::receiveHandler(const boost::system::error_code& ec,
size_t bytes_transferred) {
if (ec) {
if (ec.value() == boost::asio::error::eof) {
std::stringstream os;
if (feed_.getProcessedText().empty()) {
os << "no input data to discard";
}
else {
os << "discarding partial command of "
<< feed_.getProcessedText().size() << " bytes";
}
// Foreign host has closed the connection. We should remove it from the
// connection pool.
LOG_INFO(command_logger, COMMAND_SOCKET_CLOSED_BY_FOREIGN_HOST)
.arg(socket_->getNative());
.arg(socket_->getNative()).arg(os.str());
} else if (ec.value() != boost::asio::error::operation_aborted) {
LOG_ERROR(command_logger, COMMAND_SOCKET_READ_FAIL)
.arg(ec.value()).arg(socket_->getNative());
......@@ -384,8 +392,14 @@ Connection::timeoutHandler() {
.arg(ex.what());
}
ConstElementPtr rsp = createAnswer(CONTROL_RESULT_ERROR, "Connection over"
" control channel timed out");
std::stringstream os;
os << "Connection over control channel timed out";
if (!feed_.getProcessedText().empty()) {
os << ", discarded partial command of "
<< feed_.getProcessedText().size() << " bytes";
}
ConstElementPtr rsp = createAnswer(CONTROL_RESULT_ERROR, os.str());
response_ = rsp->str();
doSend();
}
......
......@@ -48,9 +48,10 @@ This error indicates that the server detected incoming connection and executed
accept system call on said socket, but this call returned an error. Additional
information may be provided by the system as second parameter.
% COMMAND_SOCKET_CLOSED_BY_FOREIGN_HOST Closed command socket %1 by foreign host
% COMMAND_SOCKET_CLOSED_BY_FOREIGN_HOST Closed command socket %1 by foreign host, %2
This is an information message indicating that the command connection has been
closed by a command control client.
closed by a command control client, and whether or not any partially read data
was discarded.
% COMMAND_SOCKET_CONNECTION_CANCEL_FAIL Failed to cancel read operation on socket %1: %2
This error message is issued to indicate an error to cancel asynchronous read
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment