rib: move config parsing to Service class

This commit also de-duplicates code in Service class constructors
and reduces usage of unique_ptrs.

refs #4650

Change-Id: Ibbf454841b76ffe4d569b51fef3b1d06f7d2fdfc
diff --git a/rib/service.cpp b/rib/service.cpp
index 739883c..ddf13ed 100644
--- a/rib/service.cpp
+++ b/rib/service.cpp
@@ -27,42 +27,89 @@
 
 #include "auto-prefix-propagator.hpp"
 #include "fib-updater.hpp"
-#include "rib-manager.hpp"
 #include "readvertise/client-to-nlsr-readvertise-policy.hpp"
 #include "readvertise/nfd-rib-readvertise-destination.hpp"
 #include "readvertise/readvertise.hpp"
 
 #include "core/global-io.hpp"
+#include "core/logger.hpp"
 
 #include <boost/property_tree/info_parser.hpp>
-
 #include <ndn-cxx/transport/tcp-transport.hpp>
 #include <ndn-cxx/transport/unix-transport.hpp>
 
 namespace nfd {
 namespace rib {
 
-static const std::string INTERNAL_CONFIG = "internal://nfd.conf";
-static const Name READVERTISE_NLSR_PREFIX = "/localhost/nlsr";
+NFD_LOG_INIT(RibService);
 
 Service* Service::s_instance = nullptr;
 
-Service::Service(const std::string& configFile, ndn::KeyChain& keyChain)
-  : m_configFile(configFile)
-  , m_keyChain(keyChain)
+static const std::string CFG_SECTION = "rib";
+static const std::string CFG_LOCALHOST_SECURITY = "localhost_security";
+static const std::string CFG_LOCALHOP_SECURITY = "localhop_security";
+static const std::string CFG_PREFIX_PROPAGATE = "auto_prefix_propagate";
+static const std::string CFG_READVERTISE_NLSR = "readvertise_nlsr";
+static const Name READVERTISE_NLSR_PREFIX = "/localhost/nlsr";
+
+static ConfigSection
+loadConfigSectionFromFile(const std::string& filename)
 {
-  if (s_instance != nullptr) {
-    BOOST_THROW_EXCEPTION(std::logic_error("RIB service cannot be instantiated more than once"));
+  ConfigSection config;
+  // Any format errors should have been caught already
+  boost::property_tree::read_info(filename, config);
+  return config;
+}
+
+/**
+ * \brief Look into the config file and construct appropriate transport to communicate with NFD
+ * If NFD-RIB instance was initialized with config file, INFO format is assumed
+ */
+static shared_ptr<ndn::Transport>
+makeLocalNfdTransport(const ConfigSection& config)
+{
+  if (config.get_child_optional("face_system.unix")) {
+    // default socket path should be the same as in UnixStreamFactory::processConfig
+    auto path = config.get<std::string>("face_system.unix.path", "/var/run/nfd.sock");
+    return make_shared<ndn::UnixTransport>(path);
   }
-  if (&getGlobalIoService() != &getRibIoService()) {
-    BOOST_THROW_EXCEPTION(std::logic_error("RIB service must run on RIB thread"));
+  else if (config.get_child_optional("face_system.tcp") &&
+           config.get<std::string>("face_system.tcp.listen", "yes") == "yes") {
+    // default port should be the same as in TcpFactory::processConfig
+    auto port = config.get<std::string>("face_system.tcp.port", "6363");
+    return make_shared<ndn::TcpTransport>("localhost", port);
   }
-  s_instance = this;
+  else {
+    BOOST_THROW_EXCEPTION(ConfigFile::Error("No transport is available to communicate with NFD"));
+  }
+}
+
+Service::Service(const std::string& configFile, ndn::KeyChain& keyChain)
+  : Service(keyChain, makeLocalNfdTransport(loadConfigSectionFromFile(configFile)))
+{
+  ConfigFile config(ConfigFile::ignoreUnknownSection);
+  config.addSectionHandler(CFG_SECTION, bind(&Service::processConfig, this, _1, _2, _3));
+  config.parse(configFile, true);
+  config.parse(configFile, false);
 }
 
 Service::Service(const ConfigSection& configSection, ndn::KeyChain& keyChain)
-  : m_configSection(configSection)
-  , m_keyChain(keyChain)
+  : Service(keyChain, makeLocalNfdTransport(configSection))
+{
+  ConfigFile config(ConfigFile::ignoreUnknownSection);
+  config.addSectionHandler(CFG_SECTION, bind(&Service::processConfig, this, _1, _2, _3));
+  const std::string INTERNAL_CONFIG = "internal://nfd.conf";
+  config.parse(configSection, true, INTERNAL_CONFIG);
+  config.parse(configSection, false, INTERNAL_CONFIG);
+}
+
+Service::Service(ndn::KeyChain& keyChain, shared_ptr<ndn::Transport> localNfdTransport)
+  : m_keyChain(keyChain)
+  , m_face(std::move(localNfdTransport), getGlobalIoService(), m_keyChain)
+  , m_nfdController(m_face, m_keyChain)
+  , m_fibUpdater(m_rib, m_nfdController)
+  , m_dispatcher(m_face, m_keyChain)
+  , m_ribManager(m_rib, m_face, m_nfdController, m_dispatcher)
 {
   if (s_instance != nullptr) {
     BOOST_THROW_EXCEPTION(std::logic_error("RIB service cannot be instantiated more than once"));
@@ -91,90 +138,90 @@
 }
 
 void
-Service::initialize()
+Service::processConfig(const ConfigSection& section, bool isDryRun, const std::string& filename)
 {
-  m_face = make_unique<ndn::Face>(getLocalNfdTransport(), getGlobalIoService(), m_keyChain);
-  m_nfdController = make_unique<ndn::nfd::Controller>(*m_face, m_keyChain);
-  m_fibUpdater = make_unique<FibUpdater>(m_rib, *m_nfdController);
-  m_prefixPropagator = make_unique<AutoPrefixPropagator>(*m_nfdController, m_keyChain, m_rib);
-  m_dispatcher = make_unique<ndn::mgmt::Dispatcher>(*m_face, m_keyChain);
-  m_ribManager = make_unique<RibManager>(m_rib, *m_dispatcher, *m_face, *m_nfdController, *m_prefixPropagator);
+  if (isDryRun) {
+    checkConfig(section, filename);
+  }
+  else {
+    applyConfig(section, filename);
+  }
+}
 
-  ConfigFile config([] (const std::string& filename, const std::string& sectionName,
-                        const ConfigSection& section, bool isDryRun) {
-      // Ignore sections belonging to NFD, but raise an error
-      // if we're missing a handler for a "rib" section.
-      if (sectionName == "rib") {
-        ConfigFile::throwErrorOnUnknownSection(filename, sectionName, section, isDryRun);
+void
+Service::checkConfig(const ConfigSection& section, const std::string& filename)
+{
+  for (const auto& item : section) {
+    const std::string& key = item.first;
+    if (key == CFG_LOCALHOST_SECURITY || key == CFG_LOCALHOP_SECURITY) {
+      ndn::security::v2::validator_config::ValidationPolicyConfig policy;
+      policy.load(section, filename);
+    }
+    else if (key == CFG_PREFIX_PROPAGATE) {
+      // AutoPrefixPropagator does not support config dry-run
+    }
+    else if (key == CFG_READVERTISE_NLSR) {
+      ConfigFile::parseYesNo(item, CFG_SECTION + "." + CFG_READVERTISE_NLSR);
+    }
+    else {
+      BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option " + CFG_SECTION + "." + key));
+    }
+  }
+}
+
+void
+Service::applyConfig(const ConfigSection& section, const std::string& filename)
+{
+  bool wantPrefixPropagate = false;
+  bool wantReadvertiseNlsr = false;
+
+  for (const auto& item : section) {
+    const std::string& key = item.first;
+    const ConfigSection& value = item.second;
+    if (key == CFG_LOCALHOST_SECURITY) {
+      m_ribManager.applyLocalhostConfig(value, filename);
+    }
+    else if (key == CFG_LOCALHOP_SECURITY) {
+      m_ribManager.enableLocalhop(value, filename);
+    }
+    else if (key == CFG_PREFIX_PROPAGATE) {
+      if (m_prefixPropagator == nullptr) {
+        m_prefixPropagator = make_unique<AutoPrefixPropagator>(m_nfdController, m_keyChain, m_rib);
       }
-    });
-  m_ribManager->setConfigFile(config);
-
-  // parse config file
-  if (!m_configFile.empty()) {
-    config.parse(m_configFile, true);
-    config.parse(m_configFile, false);
-  }
-  else {
-    config.parse(m_configSection, true, INTERNAL_CONFIG);
-    config.parse(m_configSection, false, INTERNAL_CONFIG);
+      m_prefixPropagator->loadConfig(item.second);
+      m_prefixPropagator->enable();
+      wantPrefixPropagate = true;
+    }
+    else if (key == CFG_READVERTISE_NLSR) {
+      wantReadvertiseNlsr = ConfigFile::parseYesNo(item, CFG_SECTION + "." + CFG_READVERTISE_NLSR);
+    }
+    else {
+      BOOST_THROW_EXCEPTION(ConfigFile::Error("Unrecognized option " + CFG_SECTION + "." + key));
+    }
   }
 
-  if (m_ribManager->wantAutoPrefixPropagator) {
-    m_prefixPropagator->enable();
-  }
-  else {
+  if (!wantPrefixPropagate && m_prefixPropagator != nullptr) {
     m_prefixPropagator->disable();
   }
 
-  if (m_ribManager->wantReadvertiseToNlsr && m_readvertiseNlsr == nullptr) {
+  if (wantReadvertiseNlsr && m_readvertiseNlsr == nullptr) {
+    NFD_LOG_DEBUG("Enabling readvertise-to-nlsr");
     m_readvertiseNlsr = make_unique<Readvertise>(
       m_rib,
       make_unique<ClientToNlsrReadvertisePolicy>(),
-      make_unique<NfdRibReadvertiseDestination>(*m_nfdController, READVERTISE_NLSR_PREFIX, m_rib));
+      make_unique<NfdRibReadvertiseDestination>(m_nfdController, READVERTISE_NLSR_PREFIX, m_rib));
   }
-  else if (!m_ribManager->wantReadvertiseToNlsr && m_readvertiseNlsr != nullptr) {
+  else if (!wantReadvertiseNlsr && m_readvertiseNlsr != nullptr) {
+    NFD_LOG_DEBUG("Disabling readvertise-to-nlsr");
     m_readvertiseNlsr.reset();
   }
-
-  m_ribManager->registerWithNfd();
-  m_ribManager->enableLocalFields();
 }
 
-shared_ptr<ndn::Transport>
-Service::getLocalNfdTransport()
+void
+Service::initialize()
 {
-  ConfigSection config;
-
-  if (!m_configFile.empty()) {
-    // Any format errors should have been caught already
-    // If error is thrown at this point, it is development error
-    boost::property_tree::read_info(m_configFile, config);
-  }
-  else {
-    config = m_configSection;
-  }
-
-  if (config.get_child_optional("face_system.unix")) {
-    // unix socket enabled
-
-    auto socketPath = config.get<std::string>("face_system.unix.path", "/var/run/nfd.sock");
-    // default socketPath should be the same as in UnixStreamFactory::processConfig
-
-    return make_shared<ndn::UnixTransport>(socketPath);
-  }
-  else if (config.get_child_optional("face_system.tcp") &&
-           config.get<std::string>("face_system.tcp.listen", "yes") == "yes") {
-    // tcp is enabled
-
-    auto port = config.get<std::string>("face_system.tcp.port", "6363");
-    // default port should be the same as in TcpFactory::processConfig
-
-    return make_shared<ndn::TcpTransport>("localhost", port);
-  }
-  else {
-    BOOST_THROW_EXCEPTION(Error("No transport is available to communicate with NFD"));
-  }
+  m_ribManager.registerWithNfd();
+  m_ribManager.enableLocalFields();
 }
 
 } // namespace rib