diff --git a/src/bin/d2/d2_process.cc b/src/bin/d2/d2_process.cc index 8ebc540044..40c436d59f 100644 --- a/src/bin/d2/d2_process.cc +++ b/src/bin/d2/d2_process.cc @@ -440,6 +440,12 @@ D2Process::reconfigureQueueMgr() { D2Process::~D2Process() { queue_mgr_->stopListening(); + getIOService()->restart(); + try { + getIOService()->poll(); + } catch (...) { + } + queue_mgr_->removeListener(); } D2CfgMgrPtr diff --git a/src/bin/d2/d2_process.h b/src/bin/d2/d2_process.h index 5ca34222d3..11f5f9f931 100644 --- a/src/bin/d2/d2_process.h +++ b/src/bin/d2/d2_process.h @@ -129,8 +129,7 @@ public: /// @return an Element that contains the results of argument processing, /// consisting of an integer status value (0 means successful, /// non-zero means failure), and a string explanation of the outcome. - virtual isc::data::ConstElementPtr - shutdown(isc::data::ConstElementPtr args); + virtual isc::data::ConstElementPtr shutdown(isc::data::ConstElementPtr args); /// @brief Processes the given configuration. /// @@ -155,9 +154,8 @@ public: /// @return an Element that contains the results of configuration composed /// of an integer status value (0 means successful, non-zero means failure), /// and a string explanation of the outcome. - virtual isc::data::ConstElementPtr - configure(isc::data::ConstElementPtr config_set, - bool check_only = false); + virtual isc::data::ConstElementPtr configure(isc::data::ConstElementPtr config_set, + bool check_only = false); /// @brief Destructor virtual ~D2Process(); @@ -318,7 +316,7 @@ private: /// @brief Defines a shared pointer to D2Process. typedef boost::shared_ptr D2ProcessPtr; -}; // namespace isc::d2 -}; // namespace isc +} // namespace isc::d2 +} // namespace isc #endif diff --git a/src/bin/d2/tests/d2_process_unittests.cc b/src/bin/d2/tests/d2_process_unittests.cc index d4e1709421..35727b814a 100644 --- a/src/bin/d2/tests/d2_process_unittests.cc +++ b/src/bin/d2/tests/d2_process_unittests.cc @@ -9,9 +9,11 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -25,6 +27,7 @@ using namespace isc; using namespace isc::config; using namespace isc::d2; using namespace isc::data; +using namespace isc::hooks; using namespace isc::process; using namespace boost::posix_time; @@ -64,6 +67,8 @@ public: D2ProcessTest() : D2Process("d2test", asiolink::IOServicePtr(new isc::asiolink::IOService())) { + HooksManager::setTestMode(false); + D2Controller::instance(); } /// @brief Destructor diff --git a/src/bin/d2/tests/d2_queue_mgr_unittests.cc b/src/bin/d2/tests/d2_queue_mgr_unittests.cc index 153df5b97d..93f4166f3c 100644 --- a/src/bin/d2/tests/d2_queue_mgr_unittests.cc +++ b/src/bin/d2/tests/d2_queue_mgr_unittests.cc @@ -247,12 +247,15 @@ public: } virtual ~QueueMgrUDPTest() { + sender_->stopSending(); + queue_mgr_->stopListening(); test_timer_.cancel(); io_service_->restart(); try { io_service_->poll(); } catch (...) { } + queue_mgr_->removeListener(); } void reset_results() { diff --git a/src/bin/dhcp4/dhcp4_srv.cc b/src/bin/dhcp4/dhcp4_srv.cc index cead6efbfb..51a9e47352 100644 --- a/src/bin/dhcp4/dhcp4_srv.cc +++ b/src/bin/dhcp4/dhcp4_srv.cc @@ -4812,6 +4812,7 @@ Dhcpv4Srv::stopD2() { D2ClientMgr& d2_mgr = CfgMgr::instance().getD2ClientMgr(); if (d2_mgr.ddnsEnabled()) { // Updates are enabled, so lets stop the sender + d2_mgr.stop(); d2_mgr.stopSender(); } } diff --git a/src/bin/dhcp4/tests/d2_unittest.cc b/src/bin/dhcp4/tests/d2_unittest.cc index ebb2c4733b..68c9801da3 100644 --- a/src/bin/dhcp4/tests/d2_unittest.cc +++ b/src/bin/dhcp4/tests/d2_unittest.cc @@ -370,6 +370,7 @@ TEST_F(Dhcp4SrvD2Test, DISABLED_forceUDPSendFailure) { // First message is off the queue. EXPECT_EQ(2, mgr.getQueueSize()); + mgr.stop(); } // Tests error handling of D2ClientMgr::sendRequest() failure diff --git a/src/bin/dhcp6/dhcp6_srv.cc b/src/bin/dhcp6/dhcp6_srv.cc index 1f21a2f7e0..8639625860 100644 --- a/src/bin/dhcp6/dhcp6_srv.cc +++ b/src/bin/dhcp6/dhcp6_srv.cc @@ -4626,6 +4626,7 @@ Dhcpv6Srv::stopD2() { D2ClientMgr& d2_mgr = CfgMgr::instance().getD2ClientMgr(); if (d2_mgr.ddnsEnabled()) { // Updates are enabled, so lets stop the sender + d2_mgr.stop(); d2_mgr.stopSender(); } } diff --git a/src/bin/dhcp6/tests/d2_unittest.cc b/src/bin/dhcp6/tests/d2_unittest.cc index 7b6ebdb11f..0246835cba 100644 --- a/src/bin/dhcp6/tests/d2_unittest.cc +++ b/src/bin/dhcp6/tests/d2_unittest.cc @@ -374,6 +374,7 @@ TEST_F(Dhcp6SrvD2Test, DISABLED_forceUDPSendFailure) { // First message is off the queue. EXPECT_EQ(2, mgr.getQueueSize()); + mgr.stop(); } // Tests error handling of D2ClientMgr::sendRequest() failure diff --git a/src/hooks/dhcp/lease_cmds/tests/lease_cmds_unittest.h b/src/hooks/dhcp/lease_cmds/tests/lease_cmds_unittest.h index 2a6fb2e2b8..0052c229c1 100644 --- a/src/hooks/dhcp/lease_cmds/tests/lease_cmds_unittest.h +++ b/src/hooks/dhcp/lease_cmds/tests/lease_cmds_unittest.h @@ -536,6 +536,7 @@ public: /// @brief Disables DHCP-DDNS updates. void disableD2() { + d2_mgr_.stop(); d2_mgr_.stopSender(); // Default constructor creates a config with DHCP-DDNS updates // disabled. diff --git a/src/lib/dhcp_ddns/ncr_io.cc b/src/lib/dhcp_ddns/ncr_io.cc index 8028091efc..70ded4bb8c 100644 --- a/src/lib/dhcp_ddns/ncr_io.cc +++ b/src/lib/dhcp_ddns/ncr_io.cc @@ -48,14 +48,12 @@ std::string ncrProtocolToString(NameChangeProtocol protocol) { return (stream.str()); } - //************************** NameChangeListener *************************** NameChangeListener::NameChangeListener(RequestReceiveHandlerPtr recv_handler) : listening_(false), io_pending_(false), recv_handler_(recv_handler) { }; - void NameChangeListener::startListening(const isc::asiolink::IOServicePtr& io_service) { if (amListening()) { @@ -231,6 +229,18 @@ NameChangeSender::stopSending() { DHCP_DDNS_NCR_SEND_CLOSE_ERROR).arg(ex.what()); } + if (io_service_) { + try { + io_service_->restart(); + io_service_->poll(); + } catch (const std::exception& ex) { + // Swallow exceptions. If we have some sort of error we'll log + // it but we won't propagate the throw. + LOG_ERROR(dhcp_ddns_logger, + DHCP_DDNS_NCR_FLUSH_IO_ERROR).arg(ex.what()); + } + } + io_service_.reset(); } diff --git a/src/lib/dhcp_ddns/ncr_io.h b/src/lib/dhcp_ddns/ncr_io.h index d90ea926a9..b2b3287e02 100644 --- a/src/lib/dhcp_ddns/ncr_io.h +++ b/src/lib/dhcp_ddns/ncr_io.h @@ -593,7 +593,7 @@ private: /// @brief Prepares the IO for transmission in a thread safe context. /// /// @param io_service is the IOService that will handle IO event processing. - void startSendingInternal(const isc::asiolink::IOServicePtr & io_service); + void startSendingInternal(const isc::asiolink::IOServicePtr& io_service); /// @brief Queues the given request to be sent in a thread safe context. /// diff --git a/src/lib/dhcp_ddns/ncr_udp.cc b/src/lib/dhcp_ddns/ncr_udp.cc index 26579742f7..bfbd8abb33 100644 --- a/src/lib/dhcp_ddns/ncr_udp.cc +++ b/src/lib/dhcp_ddns/ncr_udp.cc @@ -86,6 +86,7 @@ NameChangeUDPListener::~NameChangeUDPListener() { void NameChangeUDPListener::open(const isc::asiolink::IOServicePtr& io_service) { + // create our endpoint and bind the low level socket to it. isc::asiolink::UDPEndpoint endpoint(ip_address_, port_); @@ -299,7 +300,6 @@ NameChangeUDPSender::close() { closeWatchSocket(); watch_socket_.reset(); - io_service_.reset(); } void @@ -327,6 +327,9 @@ NameChangeUDPSender::doSend(NameChangeRequestPtr& ncr) { void NameChangeUDPSender::sendCompletionHandler(const bool successful, const UDPCallback *send_callback) { + if (!watch_socket_) { + return; + } // Clear the IO ready marker. try { watch_socket_->clearReady(); @@ -369,7 +372,7 @@ NameChangeUDPSender::getSelectFd() { " not in send mode"); } - return(watch_socket_->getSelectFd()); + return (watch_socket_->getSelectFd()); } bool diff --git a/src/lib/dhcp_ddns/tests/ncr_udp_unittests.cc b/src/lib/dhcp_ddns/tests/ncr_udp_unittests.cc index fb9c7204d3..b7063ebb81 100644 --- a/src/lib/dhcp_ddns/tests/ncr_udp_unittests.cc +++ b/src/lib/dhcp_ddns/tests/ncr_udp_unittests.cc @@ -1046,10 +1046,6 @@ public: MultiThreadingMgr::instance().setMode(false); } - void SetUp() { - - } - void reset_results() { s_handle_->sent_ncrs_.clear(); r_handle_->received_ncrs_.clear(); diff --git a/src/lib/dhcpsrv/d2_client_mgr.cc b/src/lib/dhcpsrv/d2_client_mgr.cc index 575b6f395e..022e01212c 100644 --- a/src/lib/dhcpsrv/d2_client_mgr.cc +++ b/src/lib/dhcpsrv/d2_client_mgr.cc @@ -25,7 +25,8 @@ D2ClientMgr::D2ClientMgr() : d2_client_config_(new D2ClientConfig()), // Default constructor initializes with a disabled configuration. } -D2ClientMgr::~D2ClientMgr(){ +D2ClientMgr::~D2ClientMgr() { + stop(); stopSender(); } @@ -177,7 +178,6 @@ D2ClientMgr::generateFqdn(const asiolink::IOAddress& address, return (qualifyName(gen_name.str(), ddns_params, trailing_dot)); } - std::string D2ClientMgr::qualifyName(const std::string& partial_name, const DdnsParams& ddns_params, @@ -312,6 +312,14 @@ D2ClientMgr::stopSender() { name_change_sender_->stopSending(); LOG_INFO(dhcpsrv_logger, DHCPSRV_DHCP_DDNS_SENDER_STOPPED); } + + if (private_io_service_) { + private_io_service_->restart(); + try { + private_io_service_->poll(); + } catch (...) { + } + } } void @@ -354,7 +362,7 @@ D2ClientMgr::getQueueSize() const { isc_throw(D2ClientError, "D2ClientMgr::getQueueSize sender is null"); } - return(name_change_sender_->getQueueSize()); + return (name_change_sender_->getQueueSize()); } size_t @@ -363,11 +371,9 @@ D2ClientMgr::getQueueMaxSize() const { isc_throw(D2ClientError, "D2ClientMgr::getQueueMaxSize sender is null"); } - return(name_change_sender_->getQueueMaxSize()); + return (name_change_sender_->getQueueMaxSize()); } - - const dhcp_ddns::NameChangeRequestPtr& D2ClientMgr::peekAt(const size_t index) const { if (!name_change_sender_) { @@ -397,6 +403,11 @@ D2ClientMgr::operator()(const dhcp_ddns::NameChangeSender::Result result, } } +void +D2ClientMgr::stop() { + name_change_sender_.reset(); +} + int D2ClientMgr::getSelectFd() { if (!amSending()) { @@ -418,6 +429,5 @@ D2ClientMgr::runReadyIO() { name_change_sender_->runReadyIO(); } -}; // namespace dhcp - -}; // namespace isc +} // namespace dhcp +} // namespace isc diff --git a/src/lib/dhcpsrv/d2_client_mgr.h b/src/lib/dhcpsrv/d2_client_mgr.h index 5356b61efa..ecd755fcbd 100644 --- a/src/lib/dhcpsrv/d2_client_mgr.h +++ b/src/lib/dhcpsrv/d2_client_mgr.h @@ -394,6 +394,9 @@ public: /// it will not accept additional messages. void suspendUpdates(); + /// @brief Stop the sender + void stop(); + protected: /// @brief Function operator implementing the NCR sender callback. /// @@ -429,6 +432,7 @@ protected: int getRegisteredSelectFd(); private: + /// @brief Container class for DHCP-DDNS configuration parameters. D2ClientConfigPtr d2_client_config_; diff --git a/src/lib/dhcpsrv/tests/alloc_engine_expiration_unittest.cc b/src/lib/dhcpsrv/tests/alloc_engine_expiration_unittest.cc index 6e3a7cb010..4ecac66900 100644 --- a/src/lib/dhcpsrv/tests/alloc_engine_expiration_unittest.cc +++ b/src/lib/dhcpsrv/tests/alloc_engine_expiration_unittest.cc @@ -202,6 +202,7 @@ public: if (mgr.amSending()) { mgr.stopSender(); mgr.clearQueue(); + mgr.stop(); } // Clear configuration. diff --git a/src/lib/dhcpsrv/tests/d2_client_unittest.cc b/src/lib/dhcpsrv/tests/d2_client_unittest.cc index 6bab3abeed..0908950281 100644 --- a/src/lib/dhcpsrv/tests/d2_client_unittest.cc +++ b/src/lib/dhcpsrv/tests/d2_client_unittest.cc @@ -300,6 +300,7 @@ TEST(D2ClientMgr, validConfig) { // and not the original configuration. EXPECT_EQ(*new_cfg, *updated_config); EXPECT_NE(*original_config, *updated_config); + d2_client_mgr->stop(); } /// @brief Checks passing the D2ClientMgr a valid D2 client configuration @@ -335,6 +336,7 @@ TEST(D2ClientMgr, ipv6Config) { // and not the original configuration. EXPECT_EQ(*new_cfg, *updated_config); EXPECT_NE(*original_config, *updated_config); + d2_client_mgr->stop(); } /// @brief Test class for execerising manager functions that are diff --git a/src/lib/dhcpsrv/tests/d2_udp_unittest.cc b/src/lib/dhcpsrv/tests/d2_udp_unittest.cc index e1d66c087c..c387fb57a5 100644 --- a/src/lib/dhcpsrv/tests/d2_udp_unittest.cc +++ b/src/lib/dhcpsrv/tests/d2_udp_unittest.cc @@ -28,30 +28,68 @@ namespace ph = std::placeholders; namespace { +class D2ClientMgrTestSendHandler : public D2ClientMgr { +public: + /// @brief Constructor + D2ClientMgrTestSendHandler() : simulate_send_failure_(false), callback_count_(0) { + } + + /// @brief Overrides base class completion callback. + /// + /// This method will be invoked each time a send completes. It allows + /// intervention prior to calling the production implementation in the + /// base. If simulate_send_failure_ is true, the base call impl will + /// be called with an error status, otherwise it will be called with + /// the result parameter given. + /// + /// @param result Result code of the send operation. + /// @param ncr NameChangeRequest which failed to send. + virtual void operator()(const dhcp_ddns::NameChangeSender::Result result, + dhcp_ddns::NameChangeRequestPtr& ncr) { + ++callback_count_; + if (simulate_send_failure_) { + simulate_send_failure_ = false; + D2ClientMgr::operator()(dhcp_ddns::NameChangeSender::ERROR, ncr); + } else { + D2ClientMgr::operator()(result, ncr); + } + } + + /// @brief If true simulates a send which completed with a failed status. + bool simulate_send_failure_; + + /// @brief Tracks the number times the completion handler is called. + int callback_count_; + + /// Expose restricted members. + using D2ClientMgr::getSelectFd; +}; + /// @brief Test fixture for exercising D2ClientMgr send management /// services. It inherits from D2ClientMgr to allow overriding various /// methods and accessing otherwise restricted member. In particular it /// overrides the NameChangeSender completion completion callback, allowing /// the injection of send errors. -class D2ClientMgrTest : public D2ClientMgr, public ::testing::Test { +class D2ClientMgrTest : public ::testing::Test { public: - /// @brief If true simulates a send which completed with a failed status. - bool simulate_send_failure_; + boost::shared_ptr handle_; + /// @brief If true causes an exception throw in the client error handler. bool error_handler_throw_; - /// @brief Tracks the number times the completion handler is called. - int callback_count_; + /// @brief Tracks the number of times the client error handler was called. int error_handler_count_; /// @brief Constructor - D2ClientMgrTest() : simulate_send_failure_(false), - error_handler_throw_(false), - callback_count_(0), error_handler_count_(0) { + D2ClientMgrTest() : handle_(new D2ClientMgrTestSendHandler()), + error_handler_throw_(false), + error_handler_count_(0) { } /// @brief virtual Destructor - virtual ~D2ClientMgrTest(){ + virtual ~D2ClientMgrTest() { + handle_->stop(); + handle_->stopSender(); } /// @brief Updates the D2ClientMgr's configuration to DDNS enabled. @@ -70,14 +108,13 @@ public: IOAddress sender_ip(server_ip.isV4() ? D2ClientConfig::DFT_V4_SENDER_IP : D2ClientConfig::DFT_V6_SENDER_IP); - ASSERT_NO_THROW(new_cfg.reset(new D2ClientConfig(true, - server_ip, server_port, - sender_ip, D2ClientConfig::DFT_SENDER_PORT, - D2ClientConfig::DFT_MAX_QUEUE_SIZE, - protocol, dhcp_ddns::FMT_JSON))); + ASSERT_NO_THROW(new_cfg.reset(new D2ClientConfig(true, server_ip, server_port, + sender_ip, D2ClientConfig::DFT_SENDER_PORT, + D2ClientConfig::DFT_MAX_QUEUE_SIZE, + protocol, dhcp_ddns::FMT_JSON))); - ASSERT_NO_THROW(setD2ClientConfig(new_cfg)); - ASSERT_TRUE(ddnsEnabled()); + ASSERT_NO_THROW(handle_->setD2ClientConfig(new_cfg)); + ASSERT_TRUE(handle_->ddnsEnabled()); } /// @brief Checks sender's select-fd against an expected state of readiness. @@ -98,7 +135,7 @@ public: int select_fd = -1; ASSERT_NO_THROW( // cppcheck-suppress redundantAssignment - select_fd = getSelectFd() + select_fd = handle_->getSelectFd() ); FD_SET(select_fd, &read_fds); @@ -123,27 +160,6 @@ public: } } - /// @brief Overrides base class completion callback. - /// - /// This method will be invoked each time a send completes. It allows - /// intervention prior to calling the production implementation in the - /// base. If simulate_send_failure_ is true, the base call impl will - /// be called with an error status, otherwise it will be called with - /// the result parameter given. - /// - /// @param result Result code of the send operation. - /// @param ncr NameChangeRequest which failed to send. - virtual void operator()(const dhcp_ddns::NameChangeSender::Result result, - dhcp_ddns::NameChangeRequestPtr& ncr) { - ++callback_count_; - if (simulate_send_failure_) { - simulate_send_failure_ = false; - D2ClientMgr::operator()(dhcp_ddns::NameChangeSender::ERROR, ncr); - } else { - D2ClientMgr::operator()(result, ncr); - } - } - /// @brief Serves as the "application level" client error handler. /// /// This method is passed into calls to startSender as the client error @@ -186,78 +202,74 @@ public: return (dhcp_ddns::NameChangeRequest::fromJSON(ncr_str)); } - - /// Expose restricted members. - using D2ClientMgr::getSelectFd; }; - /// @brief Checks that D2ClientMgr disable and enable a UDP sender. TEST_F(D2ClientMgrTest, udpSenderEnableDisable) { // Verify DDNS is disabled by default. - ASSERT_FALSE(ddnsEnabled()); + ASSERT_FALSE(handle_->ddnsEnabled()); // Verify we are not in send mode. - ASSERT_FALSE(amSending()); + ASSERT_FALSE(handle_->amSending()); // Enable DDNS with server at 127.0.0.1/prot 53001 via UDP. enableDdns("127.0.0.1", 530001, dhcp_ddns::NCR_UDP); - ASSERT_FALSE(amSending()); + ASSERT_FALSE(handle_->amSending()); - ASSERT_NO_THROW(startSender(getErrorHandler())); - ASSERT_TRUE(amSending()); + ASSERT_NO_THROW(handle_->startSender(getErrorHandler())); + ASSERT_TRUE(handle_->amSending()); // Verify that we take sender out of send mode. - ASSERT_NO_THROW(stopSender()); - ASSERT_FALSE(amSending()); + ASSERT_NO_THROW(handle_->stopSender()); + ASSERT_FALSE(handle_->amSending()); } /// @brief Checks D2ClientMgr queuing methods with a UDP sender. TEST_F(D2ClientMgrTest, udpSenderQueing) { // Enable DDNS with server at 127.0.0.1/prot 53001 via UDP. enableDdns("127.0.0.1", 530001, dhcp_ddns::NCR_UDP); - ASSERT_FALSE(amSending()); + ASSERT_FALSE(handle_->amSending()); // Queue should be empty. - EXPECT_EQ(0, getQueueSize()); + EXPECT_EQ(0, handle_->getQueueSize()); // Trying to peek past the end of the queue should throw. - EXPECT_THROW(peekAt(1), dhcp_ddns::NcrSenderError); + EXPECT_THROW(handle_->peekAt(1), dhcp_ddns::NcrSenderError); // Trying to send a NCR when not in send mode should fail. dhcp_ddns::NameChangeRequestPtr ncr = buildTestNcr(); - EXPECT_THROW(sendRequest(ncr), D2ClientError); + EXPECT_THROW(handle_->sendRequest(ncr), D2ClientError); // Place sender in send mode. - ASSERT_NO_THROW(startSender(getErrorHandler())); - ASSERT_TRUE(amSending()); + ASSERT_NO_THROW(handle_->startSender(getErrorHandler())); + ASSERT_TRUE(handle_->amSending()); // Send should succeed now. - ASSERT_NO_THROW(sendRequest(ncr)); + ASSERT_NO_THROW(handle_->sendRequest(ncr)); // Queue should have 1 entry. - EXPECT_EQ(1, getQueueSize()); + EXPECT_EQ(1, handle_->getQueueSize()); // Attempt to fetch the entry we just queued. dhcp_ddns::NameChangeRequestPtr ncr2; - ASSERT_NO_THROW(ncr2 = peekAt(0)); + ASSERT_NO_THROW(ncr2 = handle_->peekAt(0)); // Verify what we queued matches what we fetched. EXPECT_TRUE(*ncr == *ncr2); // Clearing the queue while in send mode should fail. - ASSERT_THROW(clearQueue(), dhcp_ddns::NcrSenderError); + ASSERT_THROW(handle_->clearQueue(), dhcp_ddns::NcrSenderError); // We should still have 1 in the queue. - EXPECT_EQ(1, getQueueSize()); + EXPECT_EQ(1, handle_->getQueueSize()); // Get out of send mode. - ASSERT_NO_THROW(stopSender()); - ASSERT_FALSE(amSending()); + ASSERT_NO_THROW(handle_->stopSender()); + ASSERT_FALSE(handle_->amSending()); // Clear queue should succeed now. - ASSERT_NO_THROW(clearQueue()); - EXPECT_EQ(0, getQueueSize()); + ASSERT_NO_THROW(handle_->clearQueue()); + EXPECT_EQ(0, handle_->getQueueSize()); } /// @brief Checks that D2ClientMgr can send with a UDP sender and @@ -267,23 +279,23 @@ TEST_F(D2ClientMgrTest, udpSend) { enableDdns("127.0.0.1", 530001, dhcp_ddns::NCR_UDP); // Trying to fetch the select-fd when not sending should fail. - ASSERT_THROW(getSelectFd(), D2ClientError); + ASSERT_THROW(handle_->getSelectFd(), D2ClientError); // Place sender in send mode. - ASSERT_NO_THROW(startSender(getErrorHandler())); + ASSERT_NO_THROW(handle_->startSender(getErrorHandler())); // select_fd should evaluate to NOT ready to read. selectCheck(false); // Build a test request and send it. dhcp_ddns::NameChangeRequestPtr ncr = buildTestNcr(); - ASSERT_NO_THROW(sendRequest(ncr)); + ASSERT_NO_THROW(handle_->sendRequest(ncr)); // select_fd should evaluate to ready to read. selectCheck(true); // Call service handler. - runReadyIO(); + handle_->runReadyIO(); // select_fd should evaluate to not ready to read. selectCheck(false); @@ -297,20 +309,20 @@ TEST_F(D2ClientMgrTest, udpSendExternalIOService) { // Place sender in send mode using an external IO service. asiolink::IOServicePtr io_service(new IOService()); - ASSERT_NO_THROW(startSender(getErrorHandler(), io_service)); + ASSERT_NO_THROW(handle_->startSender(getErrorHandler(), io_service)); // select_fd should evaluate to NOT ready to read. selectCheck(false); // Build a test request and send it. dhcp_ddns::NameChangeRequestPtr ncr = buildTestNcr(); - ASSERT_NO_THROW(sendRequest(ncr)); + ASSERT_NO_THROW(handle_->sendRequest(ncr)); // select_fd should evaluate to ready to read. selectCheck(true); // Call service handler. - runReadyIO(); + handle_->runReadyIO(); // select_fd should evaluate to not ready to read. selectCheck(false); @@ -318,7 +330,7 @@ TEST_F(D2ClientMgrTest, udpSendExternalIOService) { // Explicitly stop the sender. This ensures the sender's // ASIO socket is closed prior to the local io_service // instance goes out of scope. - ASSERT_NO_THROW(stopSender()); + ASSERT_NO_THROW(handle_->stopSender()); } /// @brief Checks that D2ClientMgr can send with a UDP sender and @@ -329,20 +341,20 @@ TEST_F(D2ClientMgrTest, udpSendExternalIOService6) { // Place sender in send mode using an external IO service. asiolink::IOServicePtr io_service(new IOService()); - ASSERT_NO_THROW(startSender(getErrorHandler(), io_service)); + ASSERT_NO_THROW(handle_->startSender(getErrorHandler(), io_service)); // select_fd should evaluate to NOT ready to read. selectCheck(false); // Build a test request and send it. dhcp_ddns::NameChangeRequestPtr ncr = buildTestNcr(); - ASSERT_NO_THROW(sendRequest(ncr)); + ASSERT_NO_THROW(handle_->sendRequest(ncr)); // select_fd should evaluate to ready to read. selectCheck(true); // Call service handler. - runReadyIO(); + handle_->runReadyIO(); // select_fd should evaluate to not ready to read. selectCheck(false); @@ -350,47 +362,45 @@ TEST_F(D2ClientMgrTest, udpSendExternalIOService6) { // Explicitly stop the sender. This ensures the sender's // ASIO socket is closed prior to the local io_service // instance goes out of scope. - ASSERT_NO_THROW(stopSender()); + ASSERT_NO_THROW(handle_->stopSender()); } - /// @brief Checks that D2ClientMgr invokes the client error handler /// when send errors occur. TEST_F(D2ClientMgrTest, udpSendErrorHandler) { // Enable DDNS with server at 127.0.0.1/prot 53001 via UDP. // Place sender in send mode. enableDdns("127.0.0.1", 530001, dhcp_ddns::NCR_UDP); - ASSERT_NO_THROW(startSender(getErrorHandler())); + ASSERT_NO_THROW(handle_->startSender(getErrorHandler())); // Simulate a failed response in the send call back. This should // cause the error handler to get invoked. - simulate_send_failure_ = true; + handle_->simulate_send_failure_ = true; // Verify error count is zero. ASSERT_EQ(0, error_handler_count_); // Send a test request. dhcp_ddns::NameChangeRequestPtr ncr = buildTestNcr(); - ASSERT_NO_THROW(sendRequest(ncr)); + ASSERT_NO_THROW(handle_->sendRequest(ncr)); // Call the ready handler. This should complete the message with an error. - ASSERT_NO_THROW(runReadyIO()); + ASSERT_NO_THROW(handle_->runReadyIO()); // If we executed error handler properly, the error count should one. ASSERT_EQ(1, error_handler_count_); } - /// @brief Checks that client error handler exceptions are handled gracefully. TEST_F(D2ClientMgrTest, udpSendErrorHandlerThrow) { // Enable DDNS with server at 127.0.0.1/prot 53001 via UDP. // Place sender in send mode. enableDdns("127.0.0.1", 530001, dhcp_ddns::NCR_UDP); - ASSERT_NO_THROW(startSender(getErrorHandler())); + ASSERT_NO_THROW(handle_->startSender(getErrorHandler())); // Simulate a failed response in the send call back and // force a throw in the error handler. - simulate_send_failure_ = true; + handle_->simulate_send_failure_ = true; error_handler_throw_ = true; // Verify error count is zero. @@ -398,11 +408,11 @@ TEST_F(D2ClientMgrTest, udpSendErrorHandlerThrow) { // Send a test request. dhcp_ddns::NameChangeRequestPtr ncr = buildTestNcr(); - ASSERT_NO_THROW(sendRequest(ncr)); + ASSERT_NO_THROW(handle_->sendRequest(ncr)); // Call the ready handler. This should complete the message with an error. // The handler should throw but the exception should not escape. - ASSERT_NO_THROW(runReadyIO()); + ASSERT_NO_THROW(handle_->runReadyIO()); // If throw flag is false, then we were in the error handler should // have thrown. @@ -418,16 +428,16 @@ TEST_F(D2ClientMgrTest, ifaceRegister) { enableDdns("127.0.0.1", 530001, dhcp_ddns::NCR_UDP); // Place sender in send mode. - ASSERT_NO_THROW(startSender(getErrorHandler())); + ASSERT_NO_THROW(handle_->startSender(getErrorHandler())); // Queue three messages. for (unsigned i = 0; i < 3; ++i) { dhcp_ddns::NameChangeRequestPtr ncr = buildTestNcr(); - ASSERT_NO_THROW(sendRequest(ncr)); + ASSERT_NO_THROW(handle_->sendRequest(ncr)); } // Make sure queue count is correct. - EXPECT_EQ(3, getQueueSize()); + EXPECT_EQ(3, handle_->getQueueSize()); // select_fd should evaluate to ready to read. selectCheck(true); @@ -436,21 +446,21 @@ TEST_F(D2ClientMgrTest, ifaceRegister) { IfaceMgr::instance().receive4(0, 0); // Verify the callback handler was invoked, no errors counted. - EXPECT_EQ(2, getQueueSize()); - ASSERT_EQ(1, callback_count_); + EXPECT_EQ(2, handle_->getQueueSize()); + ASSERT_EQ(1, handle_->callback_count_); ASSERT_EQ(0, error_handler_count_); // Stop the sender. This should complete the second message but leave // the third in the queue. - ASSERT_NO_THROW(stopSender()); - EXPECT_EQ(1, getQueueSize()); - ASSERT_EQ(2, callback_count_); + ASSERT_NO_THROW(handle_->stopSender()); + EXPECT_EQ(1, handle_->getQueueSize()); + ASSERT_EQ(2, handle_->callback_count_); ASSERT_EQ(0, error_handler_count_); // Calling receive again should have no affect. IfaceMgr::instance().receive4(0, 0); - EXPECT_EQ(1, getQueueSize()); - ASSERT_EQ(2, callback_count_); + EXPECT_EQ(1, handle_->getQueueSize()); + ASSERT_EQ(2, handle_->callback_count_); ASSERT_EQ(0, error_handler_count_); } @@ -459,43 +469,43 @@ TEST_F(D2ClientMgrTest, udpSuspendUpdates) { // Enable DDNS with server at 127.0.0.1/prot 53001 via UDP. // Place sender in send mode. enableDdns("127.0.0.1", 530001, dhcp_ddns::NCR_UDP); - ASSERT_NO_THROW(startSender(getErrorHandler())); + ASSERT_NO_THROW(handle_->startSender(getErrorHandler())); // Send a test request. for (unsigned i = 0; i < 3; ++i) { dhcp_ddns::NameChangeRequestPtr ncr = buildTestNcr(); - ASSERT_NO_THROW(sendRequest(ncr)); + ASSERT_NO_THROW(handle_->sendRequest(ncr)); } - ASSERT_EQ(3, getQueueSize()); + ASSERT_EQ(3, handle_->getQueueSize()); // Call the ready handler. This should complete the first message // and initiate sending the second message. - ASSERT_NO_THROW(runReadyIO()); + ASSERT_NO_THROW(handle_->runReadyIO()); // Queue count should have gone down by 1. - ASSERT_EQ(2, getQueueSize()); + ASSERT_EQ(2, handle_->getQueueSize()); // Suspend updates. This should disable updates and stop the sender. - ASSERT_NO_THROW(suspendUpdates()); + ASSERT_NO_THROW(handle_->suspendUpdates()); - EXPECT_FALSE(ddnsEnabled()); - EXPECT_FALSE(amSending()); + EXPECT_FALSE(handle_->ddnsEnabled()); + EXPECT_FALSE(handle_->amSending()); // Stopping the sender should have completed the second message's // in-progress send, so queue size should be 1. - ASSERT_EQ(1, getQueueSize()); + ASSERT_EQ(1, handle_->getQueueSize()); } /// @brief Tests that invokeErrorHandler does not fail if there is no handler. TEST_F(D2ClientMgrTest, missingErrorHandler) { // Ensure we aren't in send mode. - ASSERT_FALSE(ddnsEnabled()); - ASSERT_FALSE(amSending()); + ASSERT_FALSE(handle_->ddnsEnabled()); + ASSERT_FALSE(handle_->amSending()); // There is no error handler at this point, so invoking should not throw. dhcp_ddns::NameChangeRequestPtr ncr; - ASSERT_NO_THROW(invokeClientErrorHandler(dhcp_ddns::NameChangeSender::ERROR, - ncr)); + ASSERT_NO_THROW(handle_->invokeClientErrorHandler(dhcp_ddns::NameChangeSender::ERROR, + ncr)); // Verify we didn't invoke the error handler, error count is zero. ASSERT_EQ(0, error_handler_count_); diff --git a/src/lib/dhcpsrv/tests/ncr_generator_unittest.cc b/src/lib/dhcpsrv/tests/ncr_generator_unittest.cc index 5e20fd339a..6f26b8c27b 100644 --- a/src/lib/dhcpsrv/tests/ncr_generator_unittest.cc +++ b/src/lib/dhcpsrv/tests/ncr_generator_unittest.cc @@ -97,6 +97,7 @@ public: /// @brief Disables DHCP-DDNS updates. void disableD2() { + d2_mgr_.stop(); d2_mgr_.stopSender(); // Default constructor creates a config with DHCP-DDNS updates // disabled. diff --git a/src/lib/http/client.cc b/src/lib/http/client.cc index 056bcba7b4..7a8f4f57be 100644 --- a/src/lib/http/client.cc +++ b/src/lib/http/client.cc @@ -1845,6 +1845,15 @@ public: if (thread_pool_) { thread_pool_->stop(); } + + if (thread_io_service_) { + thread_io_service_->restart(); + try { + thread_io_service_->poll(); + } catch (...) { + } + thread_io_service_->stop(); + } } /// @brief Pauses the client's thread pool. @@ -1963,6 +1972,7 @@ HttpClient::HttpClient(const IOServicePtr& io_service, bool multi_threading_enab } HttpClient::~HttpClient() { + impl_->stop(); } void