]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#3478] Finished server side
authorFrancis Dupont <fdupont@isc.org>
Fri, 2 Aug 2024 14:37:57 +0000 (16:37 +0200)
committerFrancis Dupont <fdupont@isc.org>
Wed, 21 Aug 2024 07:58:57 +0000 (09:58 +0200)
src/bin/d2/d2_process.cc
src/lib/config/http_command_mgr.cc
src/lib/config/http_command_mgr.h
src/lib/config/tests/http_command_mgr_unittests.cc
src/lib/http/connection.cc

index f82b73fff474cfdc8f565136daba6b70019558f3..2974e8fb2ab6a3a6dac33f05cff3352958393e19 100644 (file)
@@ -82,6 +82,9 @@ D2Process::init() {
 
     // Set the HTTP authentication default realm.
     HttpCommandConfig::DEFAULT_AUTHENTICATION_REALM = "kea-dhcp-ddns-server";
+
+    // D2 server does not use the interface manager.
+    HttpCommandMgr::instance().addExternalSockets(false);
 };
 
 void
index d88f0be719f58568cd0f78e7804f2adcc958b221..65315464f46599624ce51cf7a22f5c4fba19707f 100644 (file)
@@ -32,7 +32,8 @@ public:
     HttpCommandMgrImpl()
         : io_service_(), timeout_(TIMEOUT_AGENT_RECEIVE_COMMAND),
           idle_timeout_(TIMEOUT_AGENT_IDLE_CONNECTION_TIMEOUT),
-          current_config_(), http_listeners_(), active_(0) {
+          current_config_(), http_listeners_(), active_(0),
+          use_external_(true) {
     }
 
     /// @brief Configure control socket from configuration.
@@ -66,6 +67,9 @@ public:
 
     /// @brief Number of active listeners (0 or 1).
     size_t active_;
+
+    /// @brief Use external sockets flag.
+    bool use_external_;
 };
 
 void
@@ -116,10 +120,12 @@ HttpCommandMgrImpl::configure(HttpCommandConfigPtr config) {
                               config->getCertRequired());
         use_https = true;
     }
+
     // Create response creator factory first. It will be used to
     // generate response creators. Each response creator will be used
     // to generate answer to specific request.
     HttpResponseCreatorFactoryPtr rfc(new HttpCommandResponseCreatorFactory(config));
+
     // Create HTTP listener. It will open up a TCP socket and be
     // prepared to accept incoming connection.
     HttpListenerPtr http_listener
@@ -130,6 +136,10 @@ HttpCommandMgrImpl::configure(HttpCommandConfigPtr config) {
                           rfc,
                           HttpListener::RequestTimeout(timeout_),
                           HttpListener::IdleTimeout(idle_timeout_)));
+
+    // Pass the use external socket flag.
+    http_listener->addExternalSockets(use_external_);
+
     // Instruct the HTTP listener to actually open socket, install
     // callback and start listening.
     http_listener->start();
@@ -222,6 +232,11 @@ HttpCommandMgr::setIdleConnectionTimeout(const long timeout) {
     impl_->idle_timeout_ = timeout;
 }
 
+void
+HttpCommandMgr::addExternalSockets(bool use_external) {
+    impl_->use_external_ = use_external;
+}
+
 void
 HttpCommandMgr::configure(HttpCommandConfigPtr config) {
     impl_->configure(config);
index ec4356eaaf50d9ac2c70779f89a3a846e4e7ab18..9cd51707a63f568c911b3cd6d8bdc9441cb10adc 100644 (file)
@@ -48,6 +48,14 @@ public:
     /// @param timeout New connection timeout in milliseconds.
     void setIdleConnectionTimeout(const long timeout);
 
+    /// @brief Use external sockets flag.
+    ///
+    /// Add sockets as external sockets of the interface manager
+    /// so available I/O on them makes a waiting select to return.
+    ///
+    /// @param use_external True (default) add external sockets.
+    void addExternalSockets(bool use_external = true);
+
     /// @brief Configure control socket from configuration.
     ///
     /// @param config Configuration of the control socket.
index 6044a7b39291adfcf74dc1ca43eb7103d70e3708..7177b8259ca0b126614e257f00ef5904784f82cb 100644 (file)
@@ -12,6 +12,7 @@
 #include <cc/command_interpreter.h>
 #include <config/http_command_mgr.h>
 #include <config/command_mgr.h>
+#include <dhcp/iface_mgr.h>
 #include <http/response.h>
 #include <http/response_parser.h>
 #include <http/testutils/test_http_client.h>
@@ -80,6 +81,7 @@ public:
         }
         test_timer_.cancel();
         resetState();
+        IfaceMgr::instance().deleteAllExternalSockets();
     }
 
     /// @brief Resets state.
@@ -98,6 +100,7 @@ public:
             io_service_->stopAndPoll();
             HttpCommandMgr::instance().setIOService(IOServicePtr());
         }
+        HttpCommandMgr::instance().addExternalSockets(true);
     }
 
     /// @brief Constructs a complete HTTP POST given a request body.
@@ -203,7 +206,7 @@ public:
 
 /// Verifies the configure and close of HttpCommandMgr.
 TEST_F(HttpCommandMgrTest, basic) {
-    // Make sure we can confiugure one.
+    // Make sure we can configure one.
     ASSERT_NO_THROW_LOG(HttpCommandMgr::instance().configure(http_config_));
     auto listener = HttpCommandMgr::instance().getHttpListener();
     ASSERT_TRUE(listener);
@@ -230,6 +233,30 @@ TEST_F(HttpCommandMgrTest, basic) {
     EXPECT_FALSE(HttpCommandMgr::instance().getHttpListener());
 }
 
+/// Verifies the use external socket flag of HttpCommandMgr.
+TEST_F(HttpCommandMgrTest, useExternal) {
+    // Default is to use external sockets.
+    ASSERT_NO_THROW_LOG(HttpCommandMgr::instance().configure(http_config_));
+    auto listener = HttpCommandMgr::instance().getHttpListener();
+    ASSERT_TRUE(listener);
+    int fd = listener->getNative();
+    EXPECT_NE(-1, fd);
+    EXPECT_TRUE(IfaceMgr::instance().isExternalSocket(fd));
+    ASSERT_NO_THROW_LOG(HttpCommandMgr::instance().close());
+
+    // Change to no external sockets.
+    HttpCommandMgr::instance().addExternalSockets(false);
+
+    // Retry: now the listener is not added as an external socket
+    // to the interface manager.
+    ASSERT_NO_THROW_LOG(HttpCommandMgr::instance().configure(http_config_));
+    listener = HttpCommandMgr::instance().getHttpListener();
+    ASSERT_TRUE(listener);
+    fd = listener->getNative();
+    EXPECT_NE(-1, fd);
+    EXPECT_FALSE(IfaceMgr::instance().isExternalSocket(fd));
+}
+
 /// Verifies the configure and close of HttpCommandMgr with TLS.
 TEST_F(HttpCommandMgrTest, basicTls) {
     string ca_dir(string(TEST_CA_DIR));
@@ -266,6 +293,37 @@ TEST_F(HttpCommandMgrTest, basicTls) {
     EXPECT_FALSE(HttpCommandMgr::instance().getHttpListener());
 }
 
+/// Verifies the use external socket flag of HttpCommandMgr with TLS.
+TEST_F(HttpCommandMgrTest, useExternalTls) {
+    string ca_dir(string(TEST_CA_DIR));
+    // Setup TLS for the manager.
+    http_config_->setSocketType("https");
+    http_config_->setTrustAnchor(ca_dir + string("/kea-ca.crt"));
+    http_config_->setCertFile(ca_dir + string("/kea-server.crt"));
+    http_config_->setKeyFile(ca_dir + string("/kea-server.key"));
+
+    // Default is to use external sockets.
+    ASSERT_NO_THROW_LOG(HttpCommandMgr::instance().configure(http_config_));
+    auto listener = HttpCommandMgr::instance().getHttpListener();
+    ASSERT_TRUE(listener);
+    int fd = listener->getNative();
+    EXPECT_NE(-1, fd);
+    EXPECT_TRUE(IfaceMgr::instance().isExternalSocket(fd));
+    ASSERT_NO_THROW_LOG(HttpCommandMgr::instance().close());
+
+    // Change to no external sockets.
+    HttpCommandMgr::instance().addExternalSockets(false);
+
+    // Retry: now the listener is not added as an external socket
+    // to the interface manager.
+    ASSERT_NO_THROW_LOG(HttpCommandMgr::instance().configure(http_config_));
+    listener = HttpCommandMgr::instance().getHttpListener();
+    ASSERT_TRUE(listener);
+    fd = listener->getNative();
+    EXPECT_NE(-1, fd);
+    EXPECT_FALSE(IfaceMgr::instance().isExternalSocket(fd));
+}
+
 // This test verifies that an HTTP connection can be established and used to
 // transmit an HTTP request and receive the response.
 TEST_F(HttpCommandMgrTest, command) {
@@ -288,4 +346,29 @@ TEST_F(HttpCommandMgrTest, command) {
     EXPECT_EQ(hr->getBody(), "[ { \"arguments\": [ \"bar\" ], \"result\": 0 } ]");
 }
 
+// This test verifies that an HTTP connection can be established and used to
+// transmit an HTTP request and receive the response (no external sockets).
+TEST_F(HttpCommandMgrTest, commandNoExternal) {
+    // Change to no external sockets.
+    HttpCommandMgr::instance().addExternalSockets(false);
+
+    // Configure.
+    ASSERT_NO_THROW_LOG(HttpCommandMgr::instance().configure(http_config_));
+    EXPECT_TRUE(HttpCommandMgr::instance().getHttpListener());
+
+    // Now let's send a "foo" command.  This should create a client, connect
+    // to our listener, post our request and retrieve our reply.
+    ASSERT_NO_THROW(startRequest("{\"command\": \"foo\"}"));
+    ASSERT_TRUE(client_);
+    ASSERT_NO_THROW(runIOService());
+    ASSERT_TRUE(client_);
+
+    // Parse the response into an HttpResponse.
+    HttpResponsePtr hr;
+    ASSERT_NO_THROW_LOG(hr = parseResponse(client_->getResponse()));
+
+    // We should have a response from our command handler.
+    EXPECT_EQ(hr->getBody(), "[ { \"arguments\": [ \"bar\" ], \"result\": 0 } ]");
+}
+
 } // end of anonymous namespace
index 8f494de712b1107d7ccb7d01aa30a6cf03133c09..1796b76baaa080480685033d197e3b37b55444b5 100644 (file)
@@ -278,17 +278,6 @@ HttpConnection::asyncAccept() {
             }
             tls_acceptor->asyncAccept(*tls_socket_, cb);
         }
-        if (use_external_) {
-            auto& iface_mgr = IfaceMgr::instance ();
-            if (tcp_socket_) {
-                iface_mgr.addExternalSocket(tcp_socket_->getNative(), 0);
-            }
-            if (tls_socket_) {
-                iface_mgr.addExternalSocket(tls_socket_->getNative(), 0);
-            }
-            watch_socket_.reset(new WatchSocket());
-            iface_mgr.addExternalSocket(watch_socket_->getSelectFd(), 0);
-        }
     } catch (const std::exception& ex) {
         isc_throw(HttpConnectionError, "unable to start accepting TCP "
                   "connections: " << ex.what());
@@ -442,6 +431,18 @@ HttpConnection::acceptorCallback(const boost::system::error_code& ec) {
                 .arg(static_cast<unsigned>(request_timeout_/1000));
         }
 
+        if (use_external_) {
+            auto& iface_mgr = IfaceMgr::instance ();
+            if (tcp_socket_) {
+                iface_mgr.addExternalSocket(tcp_socket_->getNative(), 0);
+            }
+            if (tls_socket_) {
+                iface_mgr.addExternalSocket(tls_socket_->getNative(), 0);
+            }
+            watch_socket_.reset(new WatchSocket());
+            iface_mgr.addExternalSocket(watch_socket_->getSelectFd(), 0);
+        }
+
         setupRequestTimer();
         doHandshake();
     }