]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[153-netconf-control-socket] Added control socket code from kea-yang
authorFrancis Dupont <fdupont@isc.org>
Sun, 7 Oct 2018 21:10:09 +0000 (23:10 +0200)
committerFrancis Dupont <fdupont@isc.org>
Tue, 16 Oct 2018 16:34:34 +0000 (12:34 -0400)
src/bin/netconf/Makefile.am
src/bin/netconf/control_socket.cc [new file with mode: 0644]
src/bin/netconf/control_socket.h [new file with mode: 0644]
src/bin/netconf/http_control_socket.cc [new file with mode: 0644]
src/bin/netconf/http_control_socket.h [new file with mode: 0644]
src/bin/netconf/stdout_control_socket.cc [new file with mode: 0644]
src/bin/netconf/stdout_control_socket.h [new file with mode: 0644]
src/bin/netconf/tests/Makefile.am
src/bin/netconf/tests/control_socket_unittests.cc [new file with mode: 0644]
src/bin/netconf/unix_control_socket.cc [new file with mode: 0644]
src/bin/netconf/unix_control_socket.h [new file with mode: 0644]

index 70c29377d2476f23897ed6576a06f79614b92e18..73b3aa401956a0bd39c21779fbd8dfe76286d51f 100644 (file)
@@ -44,7 +44,11 @@ BUILT_SOURCES = netconf_messages.h netconf_messages.cc
 
 noinst_LTLIBRARIES = libnetconf.la
 
-libnetconf_la_SOURCES  = netconf_cfg_mgr.cc netconf_cfg_mgr.h
+libnetconf_la_SOURCES  = control_socket.cc control_socket.h
+libnetconf_la_SOURCES += http_control_socket.cc http_control_socket.h
+libnetconf_la_SOURCES += stdout_control_socket.cc stdout_control_socket.h
+libnetconf_la_SOURCES += unix_control_socket.cc unix_control_socket.h
+libnetconf_la_SOURCES += netconf_cfg_mgr.cc netconf_cfg_mgr.h
 libnetconf_la_SOURCES += netconf_config.cc netconf_config.h
 libnetconf_la_SOURCES += netconf_controller.cc netconf_controller.h
 libnetconf_la_SOURCES += netconf_log.cc netconf_log.h
diff --git a/src/bin/netconf/control_socket.cc b/src/bin/netconf/control_socket.cc
new file mode 100644 (file)
index 0000000..5b18cd2
--- /dev/null
@@ -0,0 +1,41 @@
+// Copyright (C) 2018 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/.
+
+/// @file stdout_control_socket.cc
+/// Contains the stdout derived class for control socket communication.
+
+#include <config.h>
+
+#include <netconf/control_socket.h>
+#include <netconf/http_control_socket.h>
+#include <netconf/stdout_control_socket.h>
+#include <netconf/unix_control_socket.h>
+
+using namespace std;
+
+namespace isc {
+namespace netconf {
+
+ControlSocketBasePtr
+createControlSocket(CfgControlSocketPtr ctrl_sock) {
+    if (!ctrl_sock) {
+        isc_throw(BadValue, "null control socket configuration");
+    }
+    CfgControlSocket::Type sock_type = ctrl_sock->getType();
+    switch (sock_type) {
+    case CfgControlSocket::Type::UNIX:
+        return (createControlSocket<CfgControlSocket::Type::UNIX>(ctrl_sock));
+    case CfgControlSocket::Type::HTTP:
+        return (createControlSocket<CfgControlSocket::Type::HTTP>(ctrl_sock));
+    case CfgControlSocket::Type::STDOUT:
+        return (createControlSocket<CfgControlSocket::Type::STDOUT>(ctrl_sock));
+    default:
+        isc_throw(BadValue, "Unknown control socket type: " << sock_type);
+    }
+}
+
+} // namespace netconf
+} // namespace isc
diff --git a/src/bin/netconf/control_socket.h b/src/bin/netconf/control_socket.h
new file mode 100644 (file)
index 0000000..c9ad087
--- /dev/null
@@ -0,0 +1,130 @@
+// Copyright (C) 2018 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/.
+
+/// @file control_socket.h
+/// Contains declarations for control socket communication.
+
+#ifndef CONTROL_SOCKET_H
+#define CONTROL_SOCKET_H
+
+#include <netconf/netconf_config.h>
+#include <exceptions/exceptions.h>
+
+namespace isc {
+namespace netconf {
+
+/// @brief Exception thrown when the error during communication.
+class ControlSocketError : public isc::Exception {
+public:
+    ControlSocketError(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
+/// @brief Base class for control socket communication.
+///
+/// This class is the base class for control socket communication.
+/// Derived classes implement config-get, config-test and config-set
+/// using control sockets of different types.
+class ControlSocketBase {
+public:
+
+    /// @brief Constructor.
+    ///
+    /// @param ctrl_sock The control socket configuration.
+    /// @throw ControlSocketError if ctrl_sock is null.
+    ControlSocketBase(CfgControlSocketPtr ctrl_sock) : socket_cfg_(ctrl_sock) {
+        if (!ctrl_sock) {
+            isc_throw(ControlSocketError, "ControlSocket constructor called "
+                      "with a null configuration");
+        }
+    }
+
+    /// @brief Destructor (does nothing).
+    virtual ~ControlSocketBase() {
+    }
+
+    /// @brief Getter which returns the socket type.
+    ///
+    /// @return returns the socket type as a CfgControlSocket::Type.
+    CfgControlSocket::Type getType() const {
+        return (socket_cfg_->getType());
+    }
+
+    /// @brief Getter which returns the Unix socket name.
+    ///
+    /// @return returns the Unix socket name as a std::string.
+    const std::string getName() const {
+        return (socket_cfg_->getName());
+    }
+
+    /// @brief Getter which returns the HTTP server URL.
+    ///
+    /// @return returns the HTTP server URL as an isc::http::Url.
+    const isc::http::Url getUrl() const {
+        return (socket_cfg_->getUrl());
+    }
+
+    /// @brief Get configuration.
+    ///
+    /// Call config-get over the control socket.
+    ///
+    /// @param service The target service (used by http).
+    /// @return The JSON element answer of config-get.
+    /// @throw ControlSocketError when a communication error occurs.
+    virtual data::ConstElementPtr configGet(const std::string& service) = 0;
+
+    /// @brief Test configuration.
+    ///
+    /// Call config-test over the control socket.
+    ///
+    /// @param config The configuration to test.
+    /// @param service The target service (used by http).
+    /// @return The JSON element answer of config-test.
+    /// @throw ControlSocketError when a communication error occurs.
+    virtual data::ConstElementPtr configTest(data::ConstElementPtr config,
+                                             const std::string& service) = 0;
+
+    /// @brief Set configuration.
+    ///
+    /// Call config-set over the control socket.
+    ///
+    /// @param config The configuration to set.
+    /// @param service The target service (used by http).
+    /// @return The JSON element answer of config-set.
+    /// @throw ControlSocketError when a communication error occurs.
+    virtual data::ConstElementPtr configSet(data::ConstElementPtr config,
+                                            const std::string& service) = 0;
+
+    /// @brief The control socket configuration.
+    CfgControlSocketPtr socket_cfg_;
+};
+
+/// @brief Type definition for the pointer to the @c ControlSocketBase.
+typedef boost::shared_ptr<ControlSocketBase> ControlSocketBasePtr;
+
+/// @brief Factory template for control sockets.
+///
+/// @tparam TYPE The control socket type.
+/// @param ctrl_sock The control socket configuration.
+/// @return A pointer to a control socket communication object.
+/// @throw NotImplemented if no specialization was called.
+template <CfgControlSocket::Type TYPE>  ControlSocketBasePtr
+createControlSocket(CfgControlSocketPtr ctrl_sock) {
+    isc_throw(NotImplemented, "not specialized createControlSocket");
+}
+
+/// @brief Factory function for control sockets.
+///
+/// @param ctrl_sock The control socket configuration.
+/// @return A pointer to a control socket communication object.
+/// @throw BadValue if called with null or an unknown type.
+ControlSocketBasePtr
+createControlSocket(CfgControlSocketPtr ctrl_sock);
+
+} // namespace netconf
+} // namespace isc
+
+#endif // CONTROL_SOCKET_H
diff --git a/src/bin/netconf/http_control_socket.cc b/src/bin/netconf/http_control_socket.cc
new file mode 100644 (file)
index 0000000..b3d9179
--- /dev/null
@@ -0,0 +1,130 @@
+// Copyright (C) 2018 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/.
+
+/// @file http_control_socket.cc
+/// Contains the HTTP socket derived class for control socket communication.
+
+#include <config.h>
+
+#include <netconf/http_control_socket.h>
+#include <cc/command_interpreter.h>
+#include <http/client.h>
+#include <http/post_request_json.h>
+#include <http/response_json.h>
+#include <config/timeouts.h>
+
+using namespace std;
+using namespace isc::asiolink;
+using namespace isc::config;
+using namespace isc::data;
+using namespace isc::http;
+
+namespace isc {
+namespace netconf {
+
+template <> ControlSocketBasePtr
+createControlSocket<CfgControlSocket::Type::HTTP>(CfgControlSocketPtr ctrl_sock) {
+    return (HttpControlSocketPtr(new HttpControlSocket(ctrl_sock)));
+}
+
+HttpControlSocket::HttpControlSocket(CfgControlSocketPtr ctrl_sock)
+    : ControlSocketBase(ctrl_sock) {
+}
+
+HttpControlSocket::~HttpControlSocket() {
+}
+
+ConstElementPtr
+HttpControlSocket::configGet(const string& service) {
+    if (service == "ca") {
+        return (sendCommand(createCommand("config-get")));
+    } else {
+        return (sendCommand(createCommand("config-get", service)));
+    }
+}
+
+ConstElementPtr
+HttpControlSocket::configTest(ConstElementPtr config, const string& service) {
+    if (service == "ca") {
+        return (sendCommand(createCommand("config-test", config)));
+    } else {
+        return (sendCommand(createCommand("config-test", config, service)));
+    }
+}
+
+ConstElementPtr
+HttpControlSocket::configSet(ConstElementPtr config, const string& service) {
+    if (service == "ca") {
+        return (sendCommand(createCommand("config-set", config)));
+    } else {
+        return (sendCommand(createCommand("config-set", config, service)));
+    }
+}
+
+ConstElementPtr
+HttpControlSocket::sendCommand(ConstElementPtr command) {
+    PostHttpRequestJsonPtr request;
+    request.reset(new PostHttpRequestJson(HttpRequest::Method::HTTP_POST,
+                                          "/",
+                                          HttpVersion(1, 1)));
+    request->setBodyAsJson(command);
+    try {
+        request->finalize();
+    } catch (const std::exception& ex) {
+        isc_throw(ControlSocketError, "failed to create request: "
+                  << ex.what());
+    }
+
+    IOServicePtr io_service(new IOService());
+    HttpClient client(*io_service);
+    boost::system::error_code received_ec;
+    string receive_errmsg;
+    HttpResponseJsonPtr response(new HttpResponseJson());
+
+    client.asyncSendRequest(getUrl(), request, response,
+                [&io_service, &received_ec, &receive_errmsg]
+                (const boost::system::error_code& ec,
+                const HttpResponsePtr&, const string& errmsg) {
+                        // Capture error code and message.
+                        received_ec = ec;
+                        receive_errmsg = errmsg;
+                        // Got the IO service so stop IO service.
+                        // This causes to stop IO service when
+                        // all handlers have been invoked.
+                        io_service->stopWork();
+                },
+                HttpClient::RequestTimeout(TIMEOUT_AGENT_FORWARD_COMMAND));
+
+    // Perform this synchronously.
+    io_service->run();
+
+    if (received_ec) {
+        // Got an error code.
+        isc_throw(ControlSocketError, "communication error (code): "
+                  << received_ec.message());
+    }
+
+    if (!receive_errmsg.empty()) {
+        // Got an error message.
+        isc_throw(ControlSocketError, "communication error (message): "
+                  << receive_errmsg);
+    }
+
+    if (!response) {
+        // Failed to get the answer.
+        isc_throw(ControlSocketError, "empty response");
+    }
+
+    try {
+        return (response->getBodyAsJson());
+    } catch (const std::exception& ex) {
+        isc_throw(ControlSocketError, "unparsable response: " << ex.what());
+    }
+}
+
+} // namespace netconf
+} // namespace isc
+
diff --git a/src/bin/netconf/http_control_socket.h b/src/bin/netconf/http_control_socket.h
new file mode 100644 (file)
index 0000000..752f13b
--- /dev/null
@@ -0,0 +1,86 @@
+// Copyright (C) 2018 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/.
+
+/// @file http_control_socket.h
+/// Contains declarations for HTTP control socket communication.
+
+#ifndef HTTP_CONTROL_SOCKET_H
+#define HTTP_CONTROL_SOCKET_H
+
+#include <netconf/control_socket.h>
+
+namespace isc {
+namespace netconf {
+
+/// @brief Class for control socket communication over HTTP socket.
+///
+/// This class is the derived class for control socket communication
+/// over HTTP sockets.
+/// This class implements config-get, config-test and config-set.
+class HttpControlSocket : public ControlSocketBase {
+public:
+
+    /// @brief Constructor.
+    ///
+    /// @param ctrl_sock The control socket configuration.
+    HttpControlSocket(CfgControlSocketPtr ctrl_sock);
+
+    /// @brief Destructor (does nothing).
+    virtual ~HttpControlSocket();
+
+    /// @brief Get configuration.
+    ///
+    /// Call config-get over the control socket.
+    ///
+    /// @param service The target service.
+    /// @return The JSON element answer of config-get.
+    /// @throw ControlSocketError when a communication error occurs.
+    virtual data::ConstElementPtr configGet(const std::string& service);
+
+    /// @brief Test configuration.
+    ///
+    /// Call config-test over the control socket.
+    ///
+    /// @param config The configuration to test.
+    /// @param service The target service.
+    /// @return The JSON element answer of config-test.
+    /// @throw ControlSocketError when a communication error occurs.
+    virtual data::ConstElementPtr configTest(data::ConstElementPtr config,
+                                             const std::string& service);
+
+    /// @brief Set configuration.
+    ///
+    /// Call config-set over the control socket.
+    ///
+    /// @param config The configuration to set.
+    /// @param service The target service.
+    /// @return The JSON element answer of config-set.
+    /// @throw ControlSocketError when a communication error occurs.
+    virtual data::ConstElementPtr configSet(data::ConstElementPtr config,
+                                            const std::string& service);
+
+private:
+    /// @brief Preform the actual communication.
+    ///
+    /// @param command The command to send.
+    /// @return The answer.
+    data::ConstElementPtr sendCommand(data::ConstElementPtr command);
+};
+
+/// @brief Type definition for the pointer to the @c HttpControlSocket.
+typedef boost::shared_ptr<HttpControlSocket> HttpControlSocketPtr;
+
+/// @brief Factory template specialization for http control sockets.
+///
+/// @param ctrl_sock The control socket configuration.
+/// @return A pointer to a http control socket communication object.
+template <> ControlSocketBasePtr
+createControlSocket<CfgControlSocket::Type::HTTP>(CfgControlSocketPtr ctrl_sock);
+
+} // namespace netconf
+} // namespace isc
+
+#endif // HTTP_CONTROL_SOCKET_H
diff --git a/src/bin/netconf/stdout_control_socket.cc b/src/bin/netconf/stdout_control_socket.cc
new file mode 100644 (file)
index 0000000..d021901
--- /dev/null
@@ -0,0 +1,62 @@
+// Copyright (C) 2018 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/.
+
+/// @file stdout_control_socket.cc
+/// Contains the stdout derived class for control socket communication.
+
+#include <config.h>
+
+#include <netconf/stdout_control_socket.h>
+#include <cc/command_interpreter.h>
+
+using namespace std;
+using namespace isc::config;
+using namespace isc::data;
+
+namespace isc {
+namespace netconf {
+
+template <> ControlSocketBasePtr
+createControlSocket<CfgControlSocket::Type::STDOUT>(CfgControlSocketPtr ctrl_sock) {
+    return (StdoutControlSocketPtr(new StdoutControlSocket(ctrl_sock)));
+}
+
+StdoutControlSocket::StdoutControlSocket(CfgControlSocketPtr ctrl_sock)
+    : ControlSocketBase(ctrl_sock), output_(cout) {
+}
+
+StdoutControlSocket::StdoutControlSocket(CfgControlSocketPtr ctrl_sock,
+                                         ostream& output)
+    : ControlSocketBase(ctrl_sock), output_(output) {
+}
+
+StdoutControlSocket::~StdoutControlSocket() {
+}
+
+ConstElementPtr
+StdoutControlSocket::configGet(const string& /*service*/) {
+    isc_throw(NotImplemented, "No config-get for stdout control socket");
+}
+
+ConstElementPtr
+StdoutControlSocket::configTest(ConstElementPtr /*config*/,
+                                const string& /*service*/) {
+    return (createAnswer());
+}
+
+ConstElementPtr
+StdoutControlSocket::configSet(ConstElementPtr config,
+                               const string& service) {
+    output_ << "//////////////// "
+            << service << " configuration "
+            << "////////////////" << endl;
+    prettyPrint(config, output_);
+    output_ << endl;
+    return (createAnswer());
+}
+
+} // namespace netconf
+} // namespace isc
diff --git a/src/bin/netconf/stdout_control_socket.h b/src/bin/netconf/stdout_control_socket.h
new file mode 100644 (file)
index 0000000..9b0d1f3
--- /dev/null
@@ -0,0 +1,90 @@
+// Copyright (C) 2018 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/.
+
+/// @file stdout_control_socket.h
+/// Contains declarations for stdout control socket communication.
+
+#ifndef STDOUT_CONTROL_SOCKET_H
+#define STDOUT_CONTROL_SOCKET_H
+
+#include <netconf/control_socket.h>
+#include <iostream>
+
+namespace isc {
+namespace netconf {
+
+/// @brief Class for control socket communication over stdout.
+///
+/// This class is the derived class for control socket communication
+/// over stdout.
+/// This class implements config-test (always OK) and config-set.
+class StdoutControlSocket : public ControlSocketBase {
+public:
+
+    /// @brief Constructor.
+    ///
+    /// Use std::cout.
+    ///
+    /// @param ctrl_sock The control socket configuration.
+    StdoutControlSocket(CfgControlSocketPtr ctrl_sock);
+
+    /// @brief Destructor (does nothing).
+    virtual ~StdoutControlSocket();
+
+    /// @brief Get configuration.
+    ///
+    /// Call config-get over the control socket.
+    ///
+    /// @param service The target service (ignored).
+    /// @return The JSON element answer of config-get.
+    /// @throw NotImplemented
+    virtual data::ConstElementPtr configGet(const std::string& service);
+
+    /// @brief Test configuration.
+    ///
+    /// Call config-test over the control socket.
+    ///
+    /// @param config The configuration to test (ignored).
+    /// @param service The target service (ignored).
+    /// @return The JSON element answer of config-test (fixed answer).
+    virtual data::ConstElementPtr configTest(data::ConstElementPtr config,
+                                             const std::string& service);
+
+    /// @brief Set configuration.
+    ///
+    /// Call config-set over the control socket.
+    ///
+    /// @param config The configuration to set.
+    /// @param service The target service.
+    /// @return The JSON element answer of config-set (fixed answer).
+    virtual data::ConstElementPtr configSet(data::ConstElementPtr config,
+                                            const std::string& service);
+
+protected:
+    /// @brief Alternative constructor for tests.
+    ///
+    /// @param ctrl_sock The control socket configuration.
+    /// @param output The output stream.
+    StdoutControlSocket(CfgControlSocketPtr ctrl_sock, std::ostream& output);
+
+    /// @brief The output stream (std::cout outside tests).
+    std::ostream& output_;
+};
+
+/// @brief Type definition for the pointer to the @c StdoutControlSocket.
+typedef boost::shared_ptr<StdoutControlSocket> StdoutControlSocketPtr;
+
+/// @brief Factory template specialization for stdout control sockets.
+///
+/// @param ctrl_sock The control socket configuration.
+/// @return A pointer to a stdout control socket communication object.
+template <> ControlSocketBasePtr
+createControlSocket<CfgControlSocket::Type::STDOUT>(CfgControlSocketPtr ctrl_sock);
+
+} // namespace netconf
+} // namespace isc
+
+#endif // STDOUT_CONTROL_SOCKET_H
index 0c14ae51e88bf3ccad4e0d03fbfb94bda19aed91..1da8e6af9dcb198189f738a9d065406ac2458295 100644 (file)
@@ -44,7 +44,8 @@ noinst_LTLIBRARIES = libbasic.la
 
 TESTS += netconf_unittests
 
-netconf_unittests_SOURCES  = get_config_unittest.cc
+netconf_unittests_SOURCES  = control_socket_unittests.cc
+netconf_unittests_SOURCES += get_config_unittest.cc
 netconf_unittests_SOURCES += netconf_cfg_mgr_unittests.cc
 netconf_unittests_SOURCES += netconf_controller_unittests.cc
 netconf_unittests_SOURCES += netconf_process_unittests.cc
diff --git a/src/bin/netconf/tests/control_socket_unittests.cc b/src/bin/netconf/tests/control_socket_unittests.cc
new file mode 100644 (file)
index 0000000..a694134
--- /dev/null
@@ -0,0 +1,864 @@
+// Copyright (C) 2018 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 <config.h>
+#include <netconf/netconf_config.h>
+#include <netconf/http_control_socket.h>
+#include <netconf/stdout_control_socket.h>
+#include <netconf/unix_control_socket.h>
+#include <asiolink/asio_wrapper.h>
+#include <asiolink/interval_timer.h>
+#include <asiolink/io_service.h>
+#include <http/listener.h>
+#include <http/post_request_json.h>
+#include <http/response_creator.h>
+#include <http/response_creator_factory.h>
+#include <http/response_json.h>
+#include <http/tests/response_test.h>
+#include <util/threads/thread.h>
+#include <gtest/gtest.h>
+#include <sstream>
+
+using namespace std;
+using namespace isc;
+using namespace isc::netconf;
+using namespace isc::asiolink;
+using namespace isc::data;
+using namespace isc::http;
+using namespace isc::http::test;
+using namespace isc::util::thread;
+
+namespace {
+
+//////////////////////////////// STDOUT ////////////////////////////////
+
+/// @brief Test derived StdoutControlSocket class.
+///
+/// This class exposes the constructor taking the output stream.
+class TestStdoutControlSocket : public StdoutControlSocket {
+public:
+    /// @brief Constructor.
+    ///
+    /// @param ctrl_sock The control socket configuration.
+    /// @param output The output stream.
+    TestStdoutControlSocket(CfgControlSocketPtr ctrl_sock, ostream& output)
+        : StdoutControlSocket(ctrl_sock, output) {
+    }
+
+    /// @brief Destructor.
+    virtual ~TestStdoutControlSocket() {
+    }
+};
+
+/// @brief Type definition for the pointer to the @c TestStdoutControlSocket.
+typedef boost::shared_ptr<TestStdoutControlSocket> TestStdoutControlSocketPtr;
+
+// Verifies that the createControlSocket template can create a stdout
+// control socket.
+TEST(StdoutControlSocketTest, createControlSocket) {
+    CfgControlSocketPtr cfg(new
+                    CfgControlSocket(CfgControlSocket::Type::STDOUT,
+                                     "",
+                                     Url("http://127.0.0.1:8000/")));
+    ASSERT_TRUE(cfg);
+    ControlSocketBasePtr cs = createControlSocket(cfg);
+    ASSERT_TRUE(cs);
+    StdoutControlSocketPtr scs =
+        boost::dynamic_pointer_cast<StdoutControlSocket>(cs);
+    EXPECT_TRUE(scs);
+}
+
+// Verifies that a stdout control socket does not implement configGet.
+TEST(StdoutControlSocketTest, configGet) {
+    CfgControlSocketPtr cfg(new
+                    CfgControlSocket(CfgControlSocket::Type::STDOUT,
+                                     "",
+                                     Url("http://127.0.0.1:8000/")));
+    ASSERT_TRUE(cfg);
+    StdoutControlSocketPtr scs(new StdoutControlSocket(cfg));
+    ASSERT_TRUE(scs);
+    EXPECT_THROW(scs->configGet("foo"), NotImplemented);
+}
+
+// Verifies that a stdout control socket does not nothing for configTest.
+TEST(StdoutControlSocketTest, configTest) {
+    CfgControlSocketPtr cfg(new
+                            CfgControlSocket(CfgControlSocket::Type::STDOUT,
+                                             "",
+                                             Url("http://127.0.0.1:8000/")));
+    ASSERT_TRUE(cfg);
+    StdoutControlSocketPtr scs(new StdoutControlSocket(cfg));
+    ASSERT_TRUE(scs);
+    ConstElementPtr answer;
+    ASSERT_NO_THROW(answer = scs->configTest(ConstElementPtr(), "foo"));
+
+    // Check answer.
+    ASSERT_TRUE(answer);
+    EXPECT_EQ("{ \"result\": 0 }", answer->str());
+}
+
+// Verifies that a stdout control socket outputs configSet argument.
+TEST(StdoutControlSocketTest, configSet) {
+    CfgControlSocketPtr cfg(new
+                            CfgControlSocket(CfgControlSocket::Type::STDOUT,
+                                             "",
+                                             Url("http://127.0.0.1:8000/")));
+    ASSERT_TRUE(cfg);
+    ostringstream os;
+    TestStdoutControlSocketPtr tscs(new TestStdoutControlSocket(cfg, os));
+    ASSERT_TRUE(tscs);
+    ConstElementPtr json = Element::fromJSON("{ \"bar\": 1 }");
+    ConstElementPtr answer;
+    ASSERT_NO_THROW(answer = tscs->configSet(json, "foo"));
+
+    // Check answer.
+    ASSERT_TRUE(answer);
+    EXPECT_EQ("{ \"result\": 0 }", answer->str());
+
+    // Check output.
+    string expected = "//////////////// foo configuration ////////////////\n"
+        "{\n  \"bar\": 1\n}\n";
+    EXPECT_EQ(expected, os.str());
+}
+
+//////////////////////////////// UNIX ////////////////////////////////
+
+/// @brief Test unix socket file name.
+const string TEST_SOCKET = "test-socket";
+
+/// @brief Test timeout in ms.
+const long TEST_TIMEOUT = 10000;
+
+/// @brief Type definition for the pointer to Thread objects..
+typedef boost::shared_ptr<Thread> ThreadPtr;
+
+/// @brief Test fixture class for unix control sockets.
+class UnixControlSocketTest : public ::testing::Test {
+public:
+    /// @brief Constructor.
+    UnixControlSocketTest() : io_service_(), thread_(), ready_(false) {
+        removeUnixSocketFile();
+    }
+
+    /// @brief Destructor.
+    virtual ~UnixControlSocketTest() {
+        io_service_.stop();
+        if (thread_) {
+            thread_->wait();
+            thread_.reset();
+        }
+        removeUnixSocketFile();
+    }
+
+    /// @brief Returns socket file path.
+    ///
+    /// If the KEA_SOCKET_TEST_DIR environment variable is specified, the
+    /// socket file is created in the location pointed to by this variable.
+    /// Otherwise, it is created in the build directory.
+    static string unixSocketFilePath() {
+        ostringstream s;
+        const char* env = getenv("KEA_SOCKET_TEST_DIR");
+        if (env) {
+            s << string(env);
+        } else {
+            s << TEST_DATA_BUILDDIR;
+        }
+
+        s << "/" << TEST_SOCKET;
+        return (s.str());
+    }
+
+    /// @brief Removes unix socket descriptor.
+    void removeUnixSocketFile() {
+        static_cast<void>(remove(unixSocketFilePath().c_str()));
+    }
+
+    /// @brief Create configuration of the control socket.
+    ///
+    /// @return a pointer to a control socket configuration.
+    CfgControlSocketPtr createCfgControlSocket() {
+        CfgControlSocketPtr cfg;
+        cfg.reset(new CfgControlSocket(CfgControlSocket::Type::UNIX,
+                                       unixSocketFilePath(),
+                                       Url("http://127.0.0.1:8000/")));
+        return (cfg);
+    }
+
+    /// @brief Thread reflecting server function.
+    void reflectServer();
+
+    /// @brief Thread timeout server function.
+    void timeoutServer();
+
+    /// @brief IOService object.
+    IOService io_service_;
+
+    /// @brief Pointer to server thread.
+    ThreadPtr thread_;
+
+    /// @brief Ready flag.
+    bool ready_;
+};
+
+/// @brief Server method running in a thread reflecting the command.
+///
+/// It creates the server socket, accepts client connection, read
+/// the command and send it back in a received JSON map.
+void
+UnixControlSocketTest::reflectServer() {
+    // Acceptor.
+    boost::asio::local::stream_protocol::acceptor
+        acceptor(io_service_.get_io_service());
+    EXPECT_NO_THROW(acceptor.open());
+    boost::asio::local::stream_protocol::endpoint
+        endpoint(unixSocketFilePath());
+    EXPECT_NO_THROW(acceptor.bind(endpoint));
+    EXPECT_NO_THROW(acceptor.listen());
+    boost::asio::local::stream_protocol::socket
+        socket(io_service_.get_io_service());
+
+    // Ready.
+    ready_ = true;
+
+    // Timeout.
+    IntervalTimer timer(io_service_);
+    bool timeout = false;
+    timer.setup([&timeout]() {
+            timeout = true;
+            FAIL() << "timeout";
+        }, 1500, IntervalTimer::ONE_SHOT);
+
+    // Accept.
+    bool accepted = false;
+    boost::system::error_code ec;
+    acceptor.async_accept(socket,
+                          [&ec, &accepted]
+                          (const boost::system::error_code& error) {
+                              ec = error;
+                              accepted = true;
+                          });
+    while (!accepted && !timeout) {
+        io_service_.run_one();
+    }
+    ASSERT_FALSE(ec);
+
+    // Receive command.
+    string rbuf(1024, ' ');
+    size_t received;
+    socket.async_receive(boost::asio::buffer(&rbuf[0], rbuf.size()),
+                         [&ec, &received]
+                         (const boost::system::error_code& error, size_t cnt) {
+                             ec = error;
+                             received = cnt;
+                         });
+    while (!received && !timeout) {
+        io_service_.run_one();
+    }
+    ASSERT_FALSE(ec);
+    rbuf.resize(received);
+
+    // Reflect.
+    ElementPtr map = Element::createMap();
+    map->set("received", Element::create(rbuf));
+    string sbuf = map->str();
+
+    // Send back.
+    size_t sent;
+    socket.async_send(boost::asio::buffer(&sbuf[0], sbuf.size()),
+                      [&ec, &sent]
+                      (const boost::system::error_code& error, size_t cnt) {
+                          ec = error;
+                          sent = cnt;
+                      });
+    while (!sent && !timeout) {
+        io_service_.run_one();
+    }
+    ASSERT_FALSE(ec);
+
+    // Stop timer.
+    timer.cancel();
+
+    EXPECT_FALSE(timeout);
+    EXPECT_TRUE(accepted);
+    EXPECT_TRUE(received);
+    EXPECT_TRUE(sent);
+    EXPECT_EQ(sent, sbuf.size());
+
+    if (socket.is_open()) {
+        EXPECT_NO_THROW(socket.close());
+    }
+}
+
+/// @brief Server method running in a thread waiting forever.
+///
+/// It waits that ready becomes true.
+void
+UnixControlSocketTest::timeoutServer() {
+    while (!ready_) {
+        usleep(1000);
+    }
+}
+
+// Verifies that the createControlSocket template can create an unix
+// control socket.
+TEST_F(UnixControlSocketTest, createControlSocket) {
+    CfgControlSocketPtr cfg = createCfgControlSocket();
+    ASSERT_TRUE(cfg);
+    ControlSocketBasePtr cs = createControlSocket(cfg);
+    ASSERT_TRUE(cs);
+    UnixControlSocketPtr ucs =
+        boost::dynamic_pointer_cast<UnixControlSocket>(cs);
+    EXPECT_TRUE(ucs);
+}
+
+// Verifies that unix control sockets handle configGet() as expected.
+TEST_F(UnixControlSocketTest, configGet) {
+    CfgControlSocketPtr cfg = createCfgControlSocket();
+    ASSERT_TRUE(cfg);
+    UnixControlSocketPtr ucs(new UnixControlSocket(cfg));
+    ASSERT_TRUE(ucs);
+
+    // Run a reflecting server in a thread.
+    thread_.reset(new Thread([this]() { reflectServer(); }));
+    while (!ready_) {
+        usleep(1000);
+    }
+
+    // Try configGet.
+    ConstElementPtr reflected;
+    EXPECT_NO_THROW(reflected = ucs->configGet("foo"));
+    ASSERT_TRUE(reflected);
+    ASSERT_EQ(Element::map, reflected->getType());
+    ConstElementPtr command = reflected->get("received");
+    ASSERT_TRUE(command);
+    ASSERT_EQ(Element::string, command->getType());
+    string expected = "{ \"command\": \"config-get\" }";
+    EXPECT_EQ(expected, command->stringValue());
+}
+
+// Verifies that unix control sockets handle configTest() as expected.
+TEST_F(UnixControlSocketTest, configTest) {
+    CfgControlSocketPtr cfg = createCfgControlSocket();
+    ASSERT_TRUE(cfg);
+    UnixControlSocketPtr ucs(new UnixControlSocket(cfg));
+    ASSERT_TRUE(ucs);
+
+    // Run a reflecting server in a thread.
+    thread_.reset(new Thread([this]() { reflectServer(); }));
+    while (!ready_) {
+        usleep(1000);
+    }
+
+    // Prepare a config to test.
+    ConstElementPtr json = Element::fromJSON("{ \"bar\": 1 }");
+
+    ConstElementPtr reflected;
+    EXPECT_NO_THROW(reflected = ucs->configTest(json, "foo"));
+    ASSERT_TRUE(reflected);
+    ASSERT_EQ(Element::map, reflected->getType());
+    ConstElementPtr command = reflected->get("received");
+    ASSERT_TRUE(command);
+    ASSERT_EQ(Element::string, command->getType());
+    string expected = "{ \"arguments\": { \"bar\": 1 }, "
+        "\"command\": \"config-test\" }";
+    EXPECT_EQ(expected, command->stringValue());
+}
+
+// Verifies that unix control sockets handle configSet() as expected.
+TEST_F(UnixControlSocketTest, configSet) {
+    CfgControlSocketPtr cfg = createCfgControlSocket();
+    ASSERT_TRUE(cfg);
+    UnixControlSocketPtr ucs(new UnixControlSocket(cfg));
+    ASSERT_TRUE(ucs);
+
+    // Run a reflecting server in a thread.
+    thread_.reset(new Thread([this]() { reflectServer(); }));
+    while (!ready_) {
+        usleep(1000);
+    }
+
+    // Prepare a config to set.
+    ConstElementPtr json = Element::fromJSON("{ \"bar\": 1 }");
+
+    ConstElementPtr reflected;
+    EXPECT_NO_THROW(reflected = ucs->configSet(json, "foo"));
+    ASSERT_TRUE(reflected);
+    ASSERT_EQ(Element::map, reflected->getType());
+    ConstElementPtr command = reflected->get("received");
+    ASSERT_TRUE(command);
+    ASSERT_EQ(Element::string, command->getType());
+    string expected = "{ \"arguments\": { \"bar\": 1 }, "
+        "\"command\": \"config-set\" }";
+    EXPECT_EQ(expected, command->stringValue());
+}
+
+// Verifies that unix control sockets handle timeouts.
+TEST_F(UnixControlSocketTest, timeout) {
+    CfgControlSocketPtr cfg = createCfgControlSocket();
+    ASSERT_TRUE(cfg);
+    UnixControlSocketPtr ucs(new UnixControlSocket(cfg));
+    ASSERT_TRUE(ucs);
+
+    // Run a timeout server in a thread.
+    thread_.reset(new Thread([this]() { timeoutServer(); }));
+
+    // Try configGet: it should get a communication error,
+    EXPECT_THROW(ucs->configGet("foo"), ControlSocketError);
+    ready_ = true;
+}
+
+//////////////////////////////// HTTP ////////////////////////////////
+
+/// @brief IP address to which HTTP service is bound.
+const string SERVER_ADDRESS = "127.0.0.1";
+
+/// @brief Port number to which HTTP service is bound.
+const uint16_t SERVER_PORT = 18123;
+
+/// @brief Test HTTP JSON response.
+typedef TestHttpResponseBase<HttpResponseJson> Response;
+
+/// @brief Pointer to test HTTP JSON response.
+typedef boost::shared_ptr<Response> ResponsePtr;
+
+/// @brief Generic test HTTP response.
+typedef TestHttpResponseBase<HttpResponse> GenericResponse;
+
+/// @brief Pointer to generic test HTTP response.
+typedef boost::shared_ptr<GenericResponse> GenericResponsePtr;
+
+/// @brief Implementation of the HttpResponseCreator.
+///
+/// Send back the request in a received JSON map.
+class TestHttpResponseCreator : public HttpResponseCreator {
+public:
+    /// @brief Create a new request.
+    ///
+    /// @return Pointer to the new instance of the HttpRequest.
+    virtual HttpRequestPtr
+        createNewHttpRequest() const {
+        return (HttpRequestPtr(new PostHttpRequestJson()));
+    }
+
+protected:
+    /// @brief Creates HTTP response.
+    ///
+    /// @param request Pointer to the HTTP request.
+    /// @return Pointer to the generated HTTP response.
+    virtual HttpResponsePtr
+    createStockHttpResponse(const ConstHttpRequestPtr& request,
+                            const HttpStatusCode& status_code) const {
+        // Data is in the request context.
+        HttpVersion http_version(request->context()->http_version_major_,
+                                 request->context()->http_version_minor_);
+        ResponsePtr response(new Response(http_version, status_code));
+        response->finalize();
+        return (response);
+    }
+
+    /// @brief Creates HTTP response.
+    ///
+    /// Build a response with reflected request in a received JSON map.
+    /// When wanted reply with partial JSON.
+    ///
+    /// @param request Pointer to the HTTP request.
+    /// @return Pointer to an object representing HTTP response.
+    virtual HttpResponsePtr
+    createDynamicHttpResponse(const ConstHttpRequestPtr& request) {
+        // Request must always be JSON.
+        ConstPostHttpRequestJsonPtr request_json =
+            boost::dynamic_pointer_cast<const PostHttpRequestJson>(request);
+        if (!request_json) {
+            isc_throw(Unexpected, "request is not JSON");
+        }
+        ConstElementPtr body = request_json->getBodyAsJson();
+        if (!body) {
+            isc_throw(Unexpected, "can't get JSON from request");
+        }
+
+        // Check for the special partial JSON.
+        ConstElementPtr arguments = body->get("arguments");
+        if (arguments && (arguments->contains("want-partial"))) {
+            // Use a generic response.
+            GenericResponsePtr
+                response(new GenericResponse(request->getHttpVersion(),
+                                             HttpStatusCode::OK));
+            HttpResponseContextPtr ctx = response->context();
+            // Generate JSON response.
+            ctx->headers_.push_back(HttpHeaderContext("Content-Type",
+                                                      "application/json"));
+            // Not closed JSON map so it will fail.
+            ctx->body_ = "{";
+            response->finalize();
+            // Take into account the missing '}'.
+            response->setContentLength(2);
+            return (response);
+        }
+
+        // Reflect.
+        ResponsePtr response(new Response(request->getHttpVersion(),
+                                          HttpStatusCode::OK));
+        ElementPtr map = Element::createMap();
+        map->set("received", Element::create(body->str()));
+        response->setBodyAsJson(map);
+        response->finalize();
+        return (response);
+    }
+};
+
+/// @brief Implementation of the test HttpResponseCreatorFactory.
+class TestHttpResponseCreatorFactory : public HttpResponseCreatorFactory {
+public:
+
+    /// @brief Creates @ref TestHttpResponseCreator instance.
+    virtual HttpResponseCreatorPtr create() const {
+        HttpResponseCreatorPtr response_creator(new TestHttpResponseCreator());
+        return (response_creator);
+    }
+};
+
+/// @brief Test fixture class for http control sockets.
+class HttpControlSocketTest : public ::testing::Test {
+public:
+    HttpControlSocketTest()
+        : io_service_(), thread_(),
+          ready_(false), done_(false), finished_(false) {
+    }
+
+    /// @brief Destructor.
+    virtual ~HttpControlSocketTest() {
+        io_service_.stop();
+        if (thread_) {
+            thread_->wait();
+            thread_.reset();
+        }
+    }
+
+    /// @brief Returns socket URL.
+    static Url httpSocketUrl() {
+        ostringstream s;
+        s << "http://" << SERVER_ADDRESS << ":" << SERVER_PORT << "/";
+        return (Url(s.str()));
+    }
+
+    /// @brief Create configuration of the control socket.
+    ///
+    /// @return a pointer to a control socket configuration.
+    CfgControlSocketPtr createCfgControlSocket() {
+        CfgControlSocketPtr cfg;
+        cfg.reset(new CfgControlSocket(CfgControlSocket::Type::HTTP,
+                                       "", httpSocketUrl()));
+        return (cfg);
+    }
+
+    /// @brief Create the reflecting listener.
+    void createReflectListener();
+
+    /// @brief Start listener.
+    ///
+    /// Run IO in a thread.
+    void start() {
+        thread_.reset(new Thread([this]() {
+                    ready_ = true;
+                    while (!done_) {
+                        io_service_.run_one();
+                    }
+                    io_service_.poll();
+                    finished_ = true;
+                }));
+        while (!ready_) {
+            usleep(1000);
+        }
+        if (listener_) {
+            ASSERT_NO_THROW(listener_->start());
+        }
+    }
+
+    /// @brief Stop listener.
+    ///
+    /// Post an empty action to finish current run_one.
+    void stop() {
+        if (listener_) {
+            ASSERT_NO_THROW(listener_->stop());
+        }
+        done_ = true;
+        io_service_.post([]() { return; });
+        while (!finished_) {
+            usleep(1000);
+        }
+    }
+
+    /// @brief IOService object.
+    IOService io_service_;
+
+    /// @brief Pointer to listener.
+    HttpListenerPtr listener_;
+
+    /// @brief Pointer to server thread.
+    ThreadPtr thread_;
+
+    /// @brief Ready flag.
+    bool ready_;
+
+    /// @brief Done flag (stopping thread).
+    bool done_;
+
+    /// @brief Finished flag (stopped thread).
+    bool finished_;
+};
+
+/// @brief Create the reflecting listener.
+void
+HttpControlSocketTest::createReflectListener() {
+    HttpResponseCreatorFactoryPtr
+        factory(new TestHttpResponseCreatorFactory());
+    listener_.reset(new
+                HttpListener(io_service_,
+                             IOAddress(SERVER_ADDRESS), SERVER_PORT,
+                             factory,
+                             HttpListener::RequestTimeout(2000),
+                             HttpListener::IdleTimeout(2000)));
+}
+
+// Verifies that the createControlSocket template can create a http
+// control socket.
+TEST_F(HttpControlSocketTest, createControlSocket) {
+    CfgControlSocketPtr cfg = createCfgControlSocket();
+    ASSERT_TRUE(cfg);
+    ControlSocketBasePtr cs = createControlSocket(cfg);
+    ASSERT_TRUE(cs);
+    HttpControlSocketPtr hcs =
+        boost::dynamic_pointer_cast<HttpControlSocket>(cs);
+    EXPECT_TRUE(hcs);
+}
+
+// Verifies that http control sockets handle configGet() as expected.
+TEST_F(HttpControlSocketTest, configGet) {
+    CfgControlSocketPtr cfg = createCfgControlSocket();
+    ASSERT_TRUE(cfg);
+    HttpControlSocketPtr hcs(new HttpControlSocket(cfg));
+    ASSERT_TRUE(hcs);
+
+    // Run a reflecting server in a thread.
+    createReflectListener();
+    start();
+
+    // Try configGet.
+    ConstElementPtr reflected;
+    EXPECT_NO_THROW(reflected = hcs->configGet("foo"));
+    stop();
+
+    // Check result.
+    ASSERT_TRUE(reflected);
+    ASSERT_EQ(Element::map, reflected->getType());
+    ConstElementPtr command = reflected->get("received");
+    ASSERT_TRUE(command);
+    ASSERT_EQ(Element::string, command->getType());
+    string expected = "{ \"command\": \"config-get\", "
+        "\"service\": [ \"foo\" ] }";
+    EXPECT_EQ(expected, command->stringValue());
+}
+
+// Verifies that http control sockets handle configGet() for a control agent
+// as expected.
+TEST_F(HttpControlSocketTest, configGetCA) {
+    CfgControlSocketPtr cfg = createCfgControlSocket();
+    ASSERT_TRUE(cfg);
+    HttpControlSocketPtr hcs(new HttpControlSocket(cfg));
+    ASSERT_TRUE(hcs);
+
+    // Run a reflecting server in a thread.
+    createReflectListener();
+    start();
+
+    // Try configGet.
+    ConstElementPtr reflected;
+    EXPECT_NO_THROW(reflected = hcs->configGet("ca"));
+    stop();
+
+    // Check result.
+    ASSERT_TRUE(reflected);
+    ASSERT_EQ(Element::map, reflected->getType());
+    ConstElementPtr command = reflected->get("received");
+    ASSERT_TRUE(command);
+    ASSERT_EQ(Element::string, command->getType());
+    string expected = "{ \"command\": \"config-get\" }";
+    EXPECT_EQ(expected, command->stringValue());
+}
+
+// Verifies that http control sockets handle configTest() as expected.
+TEST_F(HttpControlSocketTest, configTest) {
+    CfgControlSocketPtr cfg = createCfgControlSocket();
+    ASSERT_TRUE(cfg);
+    HttpControlSocketPtr hcs(new HttpControlSocket(cfg));
+    ASSERT_TRUE(hcs);
+
+    // Run a reflecting server in a thread.
+    createReflectListener();
+    start();
+
+    // Prepare a config to test.
+    ConstElementPtr json = Element::fromJSON("{ \"bar\": 1 }");
+
+    // Try configTest.
+    ConstElementPtr reflected;
+    EXPECT_NO_THROW(reflected = hcs->configTest(json, "foo"));
+    stop();
+
+    // Check result.
+    ASSERT_TRUE(reflected);
+    ASSERT_EQ(Element::map, reflected->getType());
+    ConstElementPtr command = reflected->get("received");
+    ASSERT_TRUE(command);
+    ASSERT_EQ(Element::string, command->getType());
+    string expected = "{ \"arguments\": { \"bar\": 1 }, "
+        "\"command\": \"config-test\", "
+        "\"service\": [ \"foo\" ] }";
+    EXPECT_EQ(expected, command->stringValue());
+}
+
+// Verifies that http control sockets handle configTest() for a control agent
+// as expected.
+TEST_F(HttpControlSocketTest, configTestCA) {
+    CfgControlSocketPtr cfg = createCfgControlSocket();
+    ASSERT_TRUE(cfg);
+    HttpControlSocketPtr hcs(new HttpControlSocket(cfg));
+    ASSERT_TRUE(hcs);
+
+    // Run a reflecting server in a thread.
+    createReflectListener();
+    start();
+
+    // Prepare a config to test.
+    ConstElementPtr json = Element::fromJSON("{ \"bar\": 1 }");
+
+    // Try configTest.
+    ConstElementPtr reflected;
+    EXPECT_NO_THROW(reflected = hcs->configTest(json, "ca"));
+    stop();
+
+    // Check result.
+    ASSERT_TRUE(reflected);
+    ASSERT_EQ(Element::map, reflected->getType());
+    ConstElementPtr command = reflected->get("received");
+    ASSERT_TRUE(command);
+    ASSERT_EQ(Element::string, command->getType());
+    string expected = "{ \"arguments\": { \"bar\": 1 }, "
+        "\"command\": \"config-test\" }";
+    EXPECT_EQ(expected, command->stringValue());
+}
+
+// Verifies that http control sockets handle configSet() as expected.
+TEST_F(HttpControlSocketTest, configSet) {
+    CfgControlSocketPtr cfg = createCfgControlSocket();
+    ASSERT_TRUE(cfg);
+    HttpControlSocketPtr hcs(new HttpControlSocket(cfg));
+    ASSERT_TRUE(hcs);
+
+    // Run a reflecting server in a thread.
+    createReflectListener();
+    start();
+
+    // Prepare a config to set.
+    ConstElementPtr json = Element::fromJSON("{ \"bar\": 1 }");
+
+    // Try configSet.
+    ConstElementPtr reflected;
+    EXPECT_NO_THROW(reflected = hcs->configSet(json, "foo"));
+    stop();
+
+    // Check result.
+    ASSERT_TRUE(reflected);
+    ASSERT_EQ(Element::map, reflected->getType());
+    ConstElementPtr command = reflected->get("received");
+    ASSERT_TRUE(command);
+    ASSERT_EQ(Element::string, command->getType());
+    string expected = "{ \"arguments\": { \"bar\": 1 }, "
+        "\"command\": \"config-set\", "
+        "\"service\": [ \"foo\" ] }";
+    EXPECT_EQ(expected, command->stringValue());
+}
+
+// Verifies that http control sockets handle configSet() for a control agent
+// as expected.
+TEST_F(HttpControlSocketTest, configSetCA) {
+    CfgControlSocketPtr cfg = createCfgControlSocket();
+    ASSERT_TRUE(cfg);
+    HttpControlSocketPtr hcs(new HttpControlSocket(cfg));
+    ASSERT_TRUE(hcs);
+
+    // Run a reflecting server in a thread.
+    createReflectListener();
+    start();
+
+    // Prepare a config to set.
+    ConstElementPtr json = Element::fromJSON("{ \"bar\": 1 }");
+
+    // Try configSet.
+    ConstElementPtr reflected;
+    EXPECT_NO_THROW(reflected = hcs->configSet(json, "ca"));
+    stop();
+
+    // Check result.
+    ASSERT_TRUE(reflected);
+    ASSERT_EQ(Element::map, reflected->getType());
+    ConstElementPtr command = reflected->get("received");
+    ASSERT_TRUE(command);
+    ASSERT_EQ(Element::string, command->getType());
+    string expected = "{ \"arguments\": { \"bar\": 1 }, "
+        "\"command\": \"config-set\" }";
+    EXPECT_EQ(expected, command->stringValue());
+}
+
+// Verifies that http control sockets handle can't connect errors.
+TEST_F(HttpControlSocketTest, connectionRefused) {
+    CfgControlSocketPtr cfg = createCfgControlSocket();
+    ASSERT_TRUE(cfg);
+    HttpControlSocketPtr hcs(new HttpControlSocket(cfg));
+    ASSERT_TRUE(hcs);
+
+    // Try configGet: it should get a communication error,
+    try {
+        hcs->configGet("foo");
+    } catch (const ControlSocketError& ex) {
+        EXPECT_EQ("communication error (code): Connection refused",
+                  string(ex.what()));
+    } catch (const std::exception& ex) {
+        FAIL() << "unexpected exception: " << ex.what();
+    } catch (...) {
+        FAIL() << "unexpected exception";
+    }
+}
+
+// Verifies that http control sockets handle timeout errors.
+TEST_F(HttpControlSocketTest, partial) {
+    CfgControlSocketPtr cfg = createCfgControlSocket();
+    ASSERT_TRUE(cfg);
+    HttpControlSocketPtr hcs(new HttpControlSocket(cfg));
+    ASSERT_TRUE(hcs);
+
+    // Create the server but do not start it.
+    createReflectListener();
+    start();
+
+    // Prepare a special config to set.
+    ConstElementPtr json = Element::fromJSON("{ \"want-partial\": true }");
+
+    // Try configSet: it should get a communication error,
+    try {
+        hcs->configSet(json, "foo");
+    } catch (const ControlSocketError& ex) {
+        EXPECT_EQ("communication error (code): End of file",
+                  string(ex.what()));
+    } catch (const std::exception& ex) {
+        FAIL() << "unexpected exception: " << ex.what();
+    } catch (...) {
+        FAIL() << "unexpected exception";
+    }
+    stop();
+}
+
+}
diff --git a/src/bin/netconf/unix_control_socket.cc b/src/bin/netconf/unix_control_socket.cc
new file mode 100644 (file)
index 0000000..2553371
--- /dev/null
@@ -0,0 +1,99 @@
+// Copyright (C) 2018 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/.
+
+/// @file unix_control_socket.cc
+/// Contains the UNIX socket derived class for control socket communication.
+
+#include <config.h>
+
+#include <netconf/unix_control_socket.h>
+#include <asiolink/asio_wrapper.h>
+#include <asiolink/io_service.h>
+#include <cc/command_interpreter.h>
+#include <cc/json_feed.h>
+#include <config/client_connection.h>
+#include <config/timeouts.h>
+
+using namespace std;
+using namespace isc::asiolink;
+using namespace isc::config;
+using namespace isc::data;
+
+namespace isc {
+namespace netconf {
+
+template <> ControlSocketBasePtr
+createControlSocket<CfgControlSocket::Type::UNIX>(CfgControlSocketPtr ctrl_sock) {
+    return (UnixControlSocketPtr(new UnixControlSocket(ctrl_sock)));
+}
+
+UnixControlSocket::UnixControlSocket(CfgControlSocketPtr ctrl_sock)
+    : ControlSocketBase(ctrl_sock) {
+}
+
+UnixControlSocket::~UnixControlSocket() {
+}
+
+ConstElementPtr
+UnixControlSocket::configGet(const string& /*service*/) {
+    return (sendCommand(createCommand("config-get")));
+}
+
+ConstElementPtr
+UnixControlSocket::configTest(ConstElementPtr config,
+                              const string& /*service*/) {
+    return (sendCommand(createCommand("config-test", config)));
+}
+
+ConstElementPtr
+UnixControlSocket::configSet(ConstElementPtr config,
+                             const string& /*service*/) {
+    return (sendCommand(createCommand("config-set", config)));
+}
+
+ConstElementPtr
+UnixControlSocket::sendCommand(ConstElementPtr command) {
+    IOServicePtr io_service(new IOService());
+    ClientConnection conn(*io_service);
+    boost::system::error_code received_ec;
+    ConstJSONFeedPtr received_feed;
+
+    conn.start(ClientConnection::SocketPath(getName()),
+               ClientConnection::ControlCommand(command->toWire()),
+               [&io_service, &received_ec, &received_feed]
+               (const boost::system::error_code& ec, ConstJSONFeedPtr feed) {
+                   // Capture error code and parsed data.
+                   received_ec = ec;
+                   received_feed = feed;
+                   // Got the IO service so stop IO service. This causes to
+                   // stop IO service when all handlers have been invoked.
+                   io_service->stopWork();
+               },
+               ClientConnection::Timeout(TIMEOUT_AGENT_FORWARD_COMMAND));
+
+    // Perform this synchronously.
+    io_service->run();
+
+    if (received_ec) {
+        // Got an error.
+        isc_throw(ControlSocketError, "communication error: "
+                  << received_ec.message());
+    }
+
+    if (!received_feed) {
+        // Failed to get the answer.
+        isc_throw(ControlSocketError, "empty response");
+    }
+
+    try {
+        return (received_feed->toElement());
+    } catch (const std::exception& ex) {
+        isc_throw(ControlSocketError, "unparsable response: " << ex.what());
+    }
+}
+
+} // namespace netconf
+} // namespace isc
diff --git a/src/bin/netconf/unix_control_socket.h b/src/bin/netconf/unix_control_socket.h
new file mode 100644 (file)
index 0000000..08c9589
--- /dev/null
@@ -0,0 +1,86 @@
+// Copyright (C) 2018 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/.
+
+/// @file unix_control_socket.h
+/// Contains declarations for UNIX control socket communication.
+
+#ifndef UNIX_CONTROL_SOCKET_H
+#define UNIX_CONTROL_SOCKET_H
+
+#include <netconf/control_socket.h>
+
+namespace isc {
+namespace netconf {
+
+/// @brief Class for control socket communication over UNIX socket.
+///
+/// This class is the derived class for control socket communication
+/// over UNIX sockets.
+/// This class implements config-get, config-test and config-set.
+class UnixControlSocket : public ControlSocketBase {
+public:
+
+    /// @brief Constructor.
+    ///
+    /// @param ctrl_sock The control socket configuration.
+    UnixControlSocket(CfgControlSocketPtr ctrl_sock);
+
+    /// @brief Destructor (does nothing).
+    virtual ~UnixControlSocket();
+
+    /// @brief Get configuration.
+    ///
+    /// Call config-get over the control socket.
+    ///
+    /// @param service The target service (ignored).
+    /// @return The JSON element answer of config-get.
+    /// @throw ControlSocketError when a communication error occurs.
+    virtual data::ConstElementPtr configGet(const std::string& service);
+
+    /// @brief Test configuration.
+    ///
+    /// Call config-test over the control socket.
+    ///
+    /// @param service The target service (ignored).
+    /// @param config The configuration to test.
+    /// @return The JSON element answer of config-test.
+    /// @throw ControlSocketError when a communication error occurs.
+    virtual data::ConstElementPtr configTest(data::ConstElementPtr config,
+                                             const std::string& service);
+
+    /// @brief Set configuration.
+    ///
+    /// Call config-set over the control socket.
+    ///
+    /// @param config The configuration to set.
+    /// @param service The target service (ignored).
+    /// @return The JSON element answer of config-set.
+    /// @throw ControlSocketError when a communication error occurs.
+    virtual data::ConstElementPtr configSet(data::ConstElementPtr config,
+                                            const std::string& service);
+
+private:
+    /// @brief Preform the actual communication.
+    ///
+    /// @param command The command to send.
+    /// @return The answer.
+    data::ConstElementPtr sendCommand(data::ConstElementPtr command);
+};
+
+/// @brief Type definition for the pointer to the @c UnixControlSocket.
+typedef boost::shared_ptr<UnixControlSocket> UnixControlSocketPtr;
+
+/// @brief Factory template specialization for unix control sockets.
+///
+/// @param ctrl_sock The control socket configuration.
+/// @return A pointer to a unix control socket communication object.
+template <> ControlSocketBasePtr
+createControlSocket<CfgControlSocket::Type::UNIX>(CfgControlSocketPtr ctrl_sock);
+
+} // namespace netconf
+} // namespace isc
+
+#endif // UNIX_CONTROL_SOCKET_H