Commit c4fb6001 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[5317] Only a single control command connection is now allowed.

parent 2688a2a8
......@@ -1066,4 +1066,50 @@ TEST_F(CtrlChannelDhcpv4SrvTest, configReloadValid) {
::remove("test8.json");
}
// Verify that server returns an error if more than one connection is established.
TEST_F(CtrlChannelDhcpv4SrvTest, concurrentConnections) {
createUnixChannelServer();
boost::scoped_ptr<UnixControlClient> client1(new UnixControlClient());
ASSERT_TRUE(client1);
boost::scoped_ptr<UnixControlClient> client2(new UnixControlClient());
ASSERT_TRUE(client1);
// Client 1 connects.
ASSERT_TRUE(client1->connectToServer(socket_path_));
ASSERT_NO_THROW(getIOService()->poll());
// Client 2 connects.
ASSERT_TRUE(client2->connectToServer(socket_path_));
ASSERT_NO_THROW(getIOService()->poll());
// Send the command while another client is connected.
ASSERT_TRUE(client2->sendCommand("{ \"command\": \"list-commands\" }"));
ASSERT_NO_THROW(getIOService()->poll());
// The server should not allow for concurrent connections and should send
// out an error message.
std::string response;
ASSERT_TRUE(client2->getResponse(response));
EXPECT_EQ("{ \"result\": 1, \"text\": \"exceeded maximum number of concurrent"
" connections\" }", response);
// Now disconnect the first server and retry.
client1->disconnectFromServer();
ASSERT_NO_THROW(getIOService()->poll());
ASSERT_TRUE(client2->connectToServer(socket_path_));
ASSERT_NO_THROW(getIOService()->poll());
ASSERT_TRUE(client2->sendCommand("{ \"command\": \"list-commands\" }"));
ASSERT_NO_THROW(getIOService()->poll());
// The server should now respond ok.
ASSERT_TRUE(client2->getResponse(response));
EXPECT_TRUE(response.find("\"result\": 0") != std::string::npos);
client2->disconnectFromServer();
}
} // End of anonymous namespace
......@@ -1088,4 +1088,51 @@ TEST_F(CtrlChannelDhcpv6SrvTest, configReloadValid) {
::remove("test8.json");
}
// Verify that server returns an error if more than one connection is established.
TEST_F(CtrlChannelDhcpv6SrvTest, concurrentConnections) {
createUnixChannelServer();
boost::scoped_ptr<UnixControlClient> client1(new UnixControlClient());
ASSERT_TRUE(client1);
boost::scoped_ptr<UnixControlClient> client2(new UnixControlClient());
ASSERT_TRUE(client1);
// Client 1 connects.
ASSERT_TRUE(client1->connectToServer(socket_path_));
ASSERT_NO_THROW(getIOService()->poll());
// Client 2 connects.
ASSERT_TRUE(client2->connectToServer(socket_path_));
ASSERT_NO_THROW(getIOService()->poll());
// Send the command while another client is connected.
ASSERT_TRUE(client2->sendCommand("{ \"command\": \"list-commands\" }"));
ASSERT_NO_THROW(getIOService()->poll());
// The server should not allow for concurrent connections and should send
// out an error message.
std::string response;
ASSERT_TRUE(client2->getResponse(response));
EXPECT_EQ("{ \"result\": 1, \"text\": \"exceeded maximum number of concurrent"
" connections\" }", response);
// Now disconnect the first server and retry.
client1->disconnectFromServer();
ASSERT_NO_THROW(getIOService()->poll());
ASSERT_TRUE(client2->connectToServer(socket_path_));
ASSERT_NO_THROW(getIOService()->poll());
ASSERT_TRUE(client2->sendCommand("{ \"command\": \"list-commands\" }"));
ASSERT_NO_THROW(getIOService()->poll());
// The server should now respond ok.
ASSERT_TRUE(client2->getResponse(response));
EXPECT_TRUE(response.find("\"result\": 0") != std::string::npos);
client2->disconnectFromServer();
}
} // End of anonymous namespace
......@@ -97,6 +97,10 @@ public:
connections_.clear();
}
size_t getConnectionsNum() const {
return (connections_.size());
}
private:
std::set<ConnectionPtr> connections_;
......@@ -109,7 +113,14 @@ Connection::receiveHandler(const boost::system::error_code& ec,
size_t bytes_transferred) {
if (ec) {
if (ec.value() != boost::asio::error::operation_aborted) {
if (ec.value() == boost::asio::error::eof) {
// 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());
connection_pool_.stop(shared_from_this());
} else if (ec.value() != boost::asio::error::operation_aborted) {
LOG_ERROR(command_logger, COMMAND_SOCKET_READ_FAIL)
.arg(ec.value()).arg(socket_->getNative());
}
......@@ -137,8 +148,13 @@ Connection::receiveHandler(const boost::system::error_code& ec,
std::string sbuf(&buf_[0], bytes_transferred);
cmd = Element::fromJSON(sbuf, true);
// If successful, then process it as a command.
rsp = CommandMgr::instance().processCommand(cmd);
if (connection_pool_.getConnectionsNum() > 1) {
rsp = createAnswer(CONTROL_RESULT_ERROR, "exceeded maximum number of concurrent"
" connections");
} else {
// If successful, then process it as a command.
rsp = CommandMgr::instance().processCommand(cmd);
}
} catch (const Exception& ex) {
LOG_WARN(command_logger, COMMAND_PROCESS_ERROR1).arg(ex.what());
......
......@@ -55,6 +55,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
This is an information message indicating that the command connection has been
closed by a command control client.
% COMMAND_SOCKET_CONNECTION_CLOSED Closed socket %1 for existing command connection
This is an informational message that the socket created for handling
client's connection is closed. This usually means that the client disconnected,
......
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