]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[5599] Control channel now emits info on any discarded data
authorThomas Markwalder <tmark@isc.org>
Tue, 5 Jun 2018 15:06:15 +0000 (11:06 -0400)
committerThomas Markwalder <tmark@isc.org>
Tue, 5 Jun 2018 15:06:15 +0000 (11:06 -0400)
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

doc/guide/ctrl-channel.xml
src/bin/dhcp4/tests/ctrl_dhcp4_srv_unittest.cc
src/bin/dhcp6/tests/ctrl_dhcp6_srv_unittest.cc
src/lib/cc/json_feed.h
src/lib/config/command_mgr.cc
src/lib/config/config_messages.mes

index 02c593fb5d632c9c9fd91c6c76c775ef7c907714..ea2c9bd26728caf37b71870ee7cc54f254a48780 100644 (file)
     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
index e5fe3f1dfe6d038ff602302bcc539989753c261c..60f41f87684a1f84b33d8e8462be43b28f6382ed 100644 (file)
@@ -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
index e6fcb40ab4208ed4ad01bf9ff94a20aefd6cdd8d..c11fb801d3ce792c3d0c30d4bb4990dda1d6686e 100644 (file)
@@ -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
index 5ebef68f14d24d63516caa1d79dc4b6ba75515cd..ce3d026f18fbc9fef170279e3acc2911bfa3cadb 100644 (file)
@@ -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.
     ///
index 512938346162e1415d29359c88701b410e53b6bb..b9668bc1392dd8da974e1dc2bd1c6edb8dc9c105 100644 (file)
@@ -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();
 }
index 645bba692a0cb7b9d8a297738caf50ae02d21be6..cdec5f8bcc5bcedcb23cd87108c578babbe9751c 100644 (file)
@@ -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