mgmt: added manager to enable/disable control header features

refs: #1210

Change-Id: If4eb182c4baaba41be7c5f8e22e4f436eef1dac3
diff --git a/daemon/mgmt/local-control-header-manager.cpp b/daemon/mgmt/local-control-header-manager.cpp
new file mode 100644
index 0000000..6a0f056
--- /dev/null
+++ b/daemon/mgmt/local-control-header-manager.cpp
@@ -0,0 +1,110 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#include "local-control-header-manager.hpp"
+
+namespace nfd {
+
+NFD_LOG_INIT("LocalControlHeaderManager");
+
+const Name LocalControlHeaderManager::COMMAND_PREFIX = "/localhost/nfd/control-header";
+
+const size_t LocalControlHeaderManager::COMMAND_UNSIGNED_NCOMPS =
+  LocalControlHeaderManager::COMMAND_PREFIX.size() +
+  1 + // control-module
+  1; // verb
+
+const size_t LocalControlHeaderManager::COMMAND_SIGNED_NCOMPS =
+  LocalControlHeaderManager::COMMAND_UNSIGNED_NCOMPS +
+  0; // No signed Interest support in mock
+
+
+LocalControlHeaderManager::LocalControlHeaderManager(function<shared_ptr<Face>(FaceId)> getFace,
+                                                     shared_ptr<AppFace> face)
+  : ManagerBase(face),
+    m_getFace(getFace)
+{
+  face->setInterestFilter("/localhost/nfd/control-header",
+                          bind(&LocalControlHeaderManager::onLocalControlHeaderRequest, this, _2));
+}
+
+void
+LocalControlHeaderManager::onLocalControlHeaderRequest(const Interest& request)
+{
+  static const Name::Component MODULE_IN_FACEID("in-faceid");
+  static const Name::Component MODULE_NEXTHOP_FACEID("nexthop-faceid");
+  static const Name::Component VERB_ENABLE("enable");
+  static const Name::Component VERB_DISABLE("disable");
+
+  const Name& command = request.getName();
+  const size_t commandNComps = command.size();
+
+  if (COMMAND_UNSIGNED_NCOMPS <= commandNComps &&
+      commandNComps < COMMAND_SIGNED_NCOMPS)
+    {
+      NFD_LOG_INFO("command result: unsigned verb: " << command);
+      sendResponse(command, 401, "Signature required");
+
+      return;
+    }
+  else if (commandNComps < COMMAND_SIGNED_NCOMPS ||
+      !COMMAND_PREFIX.isPrefixOf(command))
+    {
+      NFD_LOG_INFO("command result: malformed");
+      sendResponse(command, 400, "Malformed command");
+      return;
+    }
+
+  shared_ptr<Face> face = m_getFace(request.getIncomingFaceId());
+
+  const Name::Component& module = command.get(COMMAND_PREFIX.size());
+  const Name::Component& verb = command.get(COMMAND_PREFIX.size() + 1);
+
+  if (module == MODULE_IN_FACEID)
+    {
+      if (verb == VERB_ENABLE)
+        {
+          face->setLocalControlHeaderFeature(LOCAL_CONTROL_HEADER_FEATURE_IN_FACEID, true);
+          sendResponse(command, 200, "Success");
+        }
+      else if (verb == VERB_DISABLE)
+        {
+          face->setLocalControlHeaderFeature(LOCAL_CONTROL_HEADER_FEATURE_IN_FACEID, false);
+          sendResponse(command, 200, "Success");
+        }
+      else
+        {
+          NFD_LOG_INFO("command result: unsupported verb: " << verb);
+          sendResponse(command, 501, "Unsupported");
+        }
+    }
+  else if (module == MODULE_NEXTHOP_FACEID)
+    {
+      if (verb == VERB_ENABLE)
+        {
+          face->setLocalControlHeaderFeature(LOCAL_CONTROL_HEADER_FEATURE_NEXTHOP_FACEID, true);
+          sendResponse(command, 200, "Success");
+        }
+      else if (verb == VERB_DISABLE)
+        {
+          face->setLocalControlHeaderFeature(LOCAL_CONTROL_HEADER_FEATURE_NEXTHOP_FACEID, false);
+          sendResponse(command, 200, "Success");
+        }
+      else
+        {
+          NFD_LOG_INFO("command result: unsupported verb: " << verb);
+          sendResponse(command, 501, "Unsupported");
+        }
+    }
+  else
+    {
+      NFD_LOG_INFO("command result: unsupported module: " << module);
+      sendResponse(command, 501, "Unsupported");
+    }
+}
+
+} // namespace nfd
+
diff --git a/daemon/mgmt/local-control-header-manager.hpp b/daemon/mgmt/local-control-header-manager.hpp
new file mode 100644
index 0000000..ab3c08a
--- /dev/null
+++ b/daemon/mgmt/local-control-header-manager.hpp
@@ -0,0 +1,46 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#ifndef NFD_MGMT_LOCAL_CONTROL_HEADER_MANAGER_HPP
+#define NFD_MGMT_LOCAL_CONTROL_HEADER_MANAGER_HPP
+
+#include "common.hpp"
+#include "face/face.hpp"
+#include "mgmt/app-face.hpp"
+#include "mgmt/manager-base.hpp"
+
+namespace nfd {
+
+class LocalControlHeaderManager : public ManagerBase
+{
+public:
+  LocalControlHeaderManager(function<shared_ptr<Face>(FaceId)> getFace,
+                            shared_ptr<AppFace> face);
+
+  void
+  onLocalControlHeaderRequest(const Interest& request);
+
+
+private:
+  function<shared_ptr<Face>(FaceId)> m_getFace;
+
+  static const Name COMMAND_PREFIX; // /localhost/nfd/control-header
+
+  // number of components in an invalid, but not malformed, unsigned command.
+  // (/localhost/nfd/control-headeer + control-module + verb) = 5
+  static const size_t COMMAND_UNSIGNED_NCOMPS;
+
+  // number of components in a valid signed Interest.
+  // 5 in mock (see UNSIGNED_NCOMPS)
+  static const size_t COMMAND_SIGNED_NCOMPS;
+};
+
+} // namespace nfd
+
+#endif // NFD_MGMT_LOCAL_CONTROL_HEADER_MANAGER_HPP
+
+
+
diff --git a/tests/mgmt/local-control-header-manager.cpp b/tests/mgmt/local-control-header-manager.cpp
new file mode 100644
index 0000000..b9c1d32
--- /dev/null
+++ b/tests/mgmt/local-control-header-manager.cpp
@@ -0,0 +1,307 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (C) 2014 Named Data Networking Project
+ * See COPYING for copyright and distribution information.
+ */
+
+#include "mgmt/local-control-header-manager.hpp"
+#include "face/face.hpp"
+#include "mgmt/internal-face.hpp"
+#include "../face/dummy-face.hpp"
+
+#include <algorithm>
+
+#include <boost/test/unit_test.hpp>
+
+namespace nfd {
+
+NFD_LOG_INIT("LocalControlHeaderManagerTest");
+
+class LocalControlHeaderManagerFixture
+{
+public:
+
+  LocalControlHeaderManagerFixture()
+    : m_callbackFired(false)
+  {
+
+  }
+
+  shared_ptr<Face>
+  getFace(FaceId id)
+  {
+    if (id > 0 && id <= m_faces.size())
+      {
+        return m_faces[id-1];
+      }
+    NFD_LOG_DEBUG("No face found returning NULL");
+    return shared_ptr<DummyFace>();
+  }
+
+  void
+  addFace(shared_ptr<Face> face)
+  {
+    m_faces.push_back(face);
+  }
+
+  void
+  validateControlResponse(const Data& response,
+                          const Name& expectedName,
+                          uint32_t expectedCode,
+                          const std::string& expectedText)
+  {
+    m_callbackFired = true;
+
+    ControlResponse control;
+    Block controlRaw = response.getContent().blockFromValue();
+
+    control.wireDecode(controlRaw);
+
+    NFD_LOG_DEBUG("received control response"
+                  << " Name: " << response.getName()
+                  << " code: " << control.getCode()
+                  << " text: " << control.getText());
+
+    BOOST_CHECK_EQUAL(response.getName(), expectedName);
+    BOOST_CHECK_EQUAL(control.getCode(), expectedCode);
+    BOOST_CHECK_EQUAL(control.getText(), expectedText);
+
+    if (!control.getBody().empty())
+      {
+        BOOST_FAIL("found unexpected control response body");
+      }
+  }
+
+  bool
+  didCallbackFire()
+  {
+    return m_callbackFired;
+  }
+
+  void
+  resetCallbackFired()
+  {
+    m_callbackFired = false;
+  }
+
+private:
+  std::vector<shared_ptr<Face> > m_faces;
+  bool m_callbackFired;
+};
+
+
+BOOST_AUTO_TEST_SUITE(MgmtLocalControlHeaderManager)
+
+BOOST_FIXTURE_TEST_CASE(InFaceId, LocalControlHeaderManagerFixture)
+{
+  shared_ptr<Face> dummy = make_shared<DummyFace>();
+  addFace(dummy);
+
+  shared_ptr<InternalFace> face(make_shared<InternalFace>());
+
+  LocalControlHeaderManager manager(bind(&LocalControlHeaderManagerFixture::getFace, this, _1),
+                                        face);
+
+  Name enable("/localhost/nfd/control-header/in-faceid/enable");
+
+  face->onReceiveData +=
+    bind(&LocalControlHeaderManagerFixture::validateControlResponse, this, _1,
+         enable, 200, "Success");
+
+  Interest enableCommand(enable);
+  enableCommand.setIncomingFaceId(1);
+  manager.onLocalControlHeaderRequest(enableCommand);
+
+  BOOST_REQUIRE(didCallbackFire());
+  BOOST_REQUIRE(dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_HEADER_FEATURE_IN_FACEID));
+  BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_HEADER_FEATURE_NEXTHOP_FACEID));
+
+  face->onReceiveData.clear();
+  resetCallbackFired();
+
+  Name disable("/localhost/nfd/control-header/in-faceid/disable");
+
+  face->onReceiveData +=
+    bind(&LocalControlHeaderManagerFixture::validateControlResponse, this, _1,
+         disable, 200, "Success");
+
+  Interest disableCommand(disable);
+  disableCommand.setIncomingFaceId(1);
+  manager.onLocalControlHeaderRequest(disableCommand);
+
+  BOOST_REQUIRE(didCallbackFire());
+  BOOST_REQUIRE(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_HEADER_FEATURE_IN_FACEID));
+  BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_HEADER_FEATURE_NEXTHOP_FACEID));
+}
+
+BOOST_FIXTURE_TEST_CASE(NextHopFaceId, LocalControlHeaderManagerFixture)
+{
+  shared_ptr<Face> dummy = make_shared<DummyFace>();
+  addFace(dummy);
+
+  shared_ptr<InternalFace> face(make_shared<InternalFace>());
+
+  LocalControlHeaderManager manager(bind(&LocalControlHeaderManagerFixture::getFace, this, _1),
+                                        face);
+
+  Name enable("/localhost/nfd/control-header/nexthop-faceid/enable");
+
+  face->onReceiveData +=
+    bind(&LocalControlHeaderManagerFixture::validateControlResponse, this, _1,
+         enable, 200, "Success");
+
+  Interest enableCommand(enable);
+  enableCommand.setIncomingFaceId(1);
+  manager.onLocalControlHeaderRequest(enableCommand);
+
+  BOOST_REQUIRE(didCallbackFire());
+  BOOST_REQUIRE(dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_HEADER_FEATURE_NEXTHOP_FACEID));
+  BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_HEADER_FEATURE_IN_FACEID));
+
+
+  face->onReceiveData.clear();
+  resetCallbackFired();
+
+  Name disable("/localhost/nfd/control-header/nexthop-faceid/disable");
+
+  face->onReceiveData +=
+    bind(&LocalControlHeaderManagerFixture::validateControlResponse, this, _1,
+         disable, 200, "Success");
+
+  Interest disableCommand(disable);
+  disableCommand.setIncomingFaceId(1);
+  manager.onLocalControlHeaderRequest(disableCommand);
+
+  BOOST_REQUIRE(didCallbackFire());
+  BOOST_REQUIRE(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_HEADER_FEATURE_NEXTHOP_FACEID));
+  BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_HEADER_FEATURE_IN_FACEID));
+}
+
+BOOST_FIXTURE_TEST_CASE(ShortCommand, LocalControlHeaderManagerFixture)
+{
+  shared_ptr<Face> dummy = make_shared<DummyFace>();
+  addFace(dummy);
+
+  shared_ptr<InternalFace> face(make_shared<InternalFace>());
+
+  LocalControlHeaderManager manager(bind(&LocalControlHeaderManagerFixture::getFace, this, _1),
+                                        face);
+
+  Name commandName("/localhost/nfd/control-header");
+
+  face->onReceiveData +=
+    bind(&LocalControlHeaderManagerFixture::validateControlResponse, this, _1,
+         commandName, 400, "Malformed command");
+
+  Interest command(commandName);
+  command.setIncomingFaceId(1);
+  manager.onLocalControlHeaderRequest(command);
+
+  BOOST_REQUIRE(didCallbackFire());
+  BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_HEADER_FEATURE_IN_FACEID));
+  BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_HEADER_FEATURE_NEXTHOP_FACEID));
+}
+
+BOOST_FIXTURE_TEST_CASE(ShortCommandModule, LocalControlHeaderManagerFixture)
+{
+  shared_ptr<Face> dummy = make_shared<DummyFace>();
+  addFace(dummy);
+
+  shared_ptr<InternalFace> face(make_shared<InternalFace>());
+
+  LocalControlHeaderManager manager(bind(&LocalControlHeaderManagerFixture::getFace, this, _1),
+                                        face);
+
+  Name commandName("/localhost/nfd/control-header/in-faceid");
+
+  face->onReceiveData +=
+    bind(&LocalControlHeaderManagerFixture::validateControlResponse, this, _1,
+         commandName, 400, "Malformed command");
+
+  Interest command(commandName);
+  command.setIncomingFaceId(1);
+  manager.onLocalControlHeaderRequest(command);
+
+  BOOST_REQUIRE(didCallbackFire());
+  BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_HEADER_FEATURE_IN_FACEID));
+  BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_HEADER_FEATURE_NEXTHOP_FACEID));
+}
+
+BOOST_FIXTURE_TEST_CASE(UnsupportedModule, LocalControlHeaderManagerFixture)
+{
+  shared_ptr<Face> dummy = make_shared<DummyFace>();
+  addFace(dummy);
+
+  shared_ptr<InternalFace> face(make_shared<InternalFace>());
+
+  LocalControlHeaderManager manager(bind(&LocalControlHeaderManagerFixture::getFace, this, _1),
+                                        face);
+
+  Name commandName("/localhost/nfd/control-header/madeup/moremadeup");
+
+  face->onReceiveData +=
+    bind(&LocalControlHeaderManagerFixture::validateControlResponse, this, _1,
+         commandName, 501, "Unsupported");
+
+  Interest command(commandName);
+  command.setIncomingFaceId(1);
+  manager.onLocalControlHeaderRequest(command);
+
+  BOOST_REQUIRE(didCallbackFire());
+  BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_HEADER_FEATURE_IN_FACEID));
+  BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_HEADER_FEATURE_NEXTHOP_FACEID));
+}
+
+BOOST_FIXTURE_TEST_CASE(InFaceIdUnsupportedVerb, LocalControlHeaderManagerFixture)
+{
+  shared_ptr<Face> dummy = make_shared<DummyFace>();
+  addFace(dummy);
+
+  shared_ptr<InternalFace> face(make_shared<InternalFace>());
+
+  LocalControlHeaderManager manager(bind(&LocalControlHeaderManagerFixture::getFace, this, _1),
+                                        face);
+
+  Name commandName("/localhost/nfd/control-header/in-faceid/madeup");
+
+  face->onReceiveData +=
+    bind(&LocalControlHeaderManagerFixture::validateControlResponse, this, _1,
+         commandName, 501, "Unsupported");
+
+  Interest command(commandName);
+  command.setIncomingFaceId(1);
+  manager.onLocalControlHeaderRequest(command);
+
+  BOOST_REQUIRE(didCallbackFire());
+  BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_HEADER_FEATURE_IN_FACEID));
+  BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_HEADER_FEATURE_NEXTHOP_FACEID));
+}
+
+BOOST_FIXTURE_TEST_CASE(NextHopFaceIdUnsupportedVerb, LocalControlHeaderManagerFixture)
+{
+  shared_ptr<Face> dummy = make_shared<DummyFace>();
+  addFace(dummy);
+
+  shared_ptr<InternalFace> face(make_shared<InternalFace>());
+
+  LocalControlHeaderManager manager(bind(&LocalControlHeaderManagerFixture::getFace, this, _1),
+                                        face);
+
+  Name commandName("/localhost/nfd/control-header/nexthop-faceid/madeup");
+
+  face->onReceiveData +=
+    bind(&LocalControlHeaderManagerFixture::validateControlResponse, this, _1,
+         commandName, 501, "Unsupported");
+
+  Interest command(commandName);
+  command.setIncomingFaceId(1);
+  manager.onLocalControlHeaderRequest(command);
+
+  BOOST_REQUIRE(didCallbackFire());
+  BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_HEADER_FEATURE_IN_FACEID));
+  BOOST_CHECK(!dummy->isLocalControlHeaderEnabled(LOCAL_CONTROL_HEADER_FEATURE_NEXTHOP_FACEID));
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace nfd