nfd-control: Adding class of ControlResponse

Change-Id: I596bb3f5470e3d3c6671e74bef991e9b047637c9
refs: #1162
diff --git a/src/encoding/tlv-nfd-control.hpp b/src/encoding/tlv-nfd-control.hpp
new file mode 100644
index 0000000..09bfef3
--- /dev/null
+++ b/src/encoding/tlv-nfd-control.hpp
@@ -0,0 +1,38 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2013, Regents of the University of California
+ *
+ * BSD license, See the LICENSE file for more information
+ *
+ * Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+ */
+
+#ifndef NDN_TLV_NFD_CONTROL_HPP
+#define NDN_TLV_NFD_CONTROL_HPP
+
+namespace ndn {
+namespace tlv {
+namespace nfd_control {
+
+enum {
+  // ControlResponse
+  ControlResponse = 101,
+  StatusCode      = 102,
+  StatusText      = 103,
+
+  // FIB Management Protocol
+  FibManagementOptions = 104,
+  FaceId          = 105,
+  Cost            = 106,
+  Strategy        = 107,
+
+  // Face Management Protocol
+  FaceManagementOptions = 108
+};
+
+
+} // namespace nfd_control
+} // namespace tlv
+} // namespace ndn
+
+#endif // NDN_TLV_NFD_CONTROL_HPP
diff --git a/src/management/control-response.hpp b/src/management/control-response.hpp
new file mode 100644
index 0000000..b14409c
--- /dev/null
+++ b/src/management/control-response.hpp
@@ -0,0 +1,169 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
+/**
+ * Copyright (C) 2013 Regents of the University of California.
+ * See COPYING for copyright and distribution information.
+ */
+
+#ifndef NDN_MANAGEMENT_CONTROL_RESPONSE_HPP
+#define NDN_MANAGEMENT_CONTROL_RESPONSE_HPP
+
+#include "encoding/block.hpp"
+#include "encoding/tlv-nfd-control.hpp"
+
+namespace ndn {
+
+/**
+ * @brief Class defining abstraction of ControlResponse for NFD Control Protocol
+ *
+ * @see http://redmine.named-data.net/projects/nfd/wiki/ControlCommand
+ */
+class ControlResponse {
+public:
+  struct Error : public Tlv::Error { Error(const std::string &what) : Tlv::Error(what) {} };
+
+  ControlResponse()
+    : m_code(200)
+  {
+  }
+
+  ControlResponse(uint32_t code, const std::string &text)
+    : m_code(code)
+    , m_text(text)
+  {
+  }
+  
+  inline uint32_t
+  getCode() const;
+
+  inline void
+  setCode(uint32_t code);
+
+  inline const std::string &
+  getText() const;
+
+  inline void
+  setText(const std::string &text);
+
+  inline const Block&
+  getBody() const;
+
+  inline void
+  setBody(const Block& body);
+  
+  inline const Block&
+  wireEncode() const;
+
+  inline void
+  wireDecode(const Block &block);
+  
+protected:
+  uint32_t m_code;
+  std::string m_text;
+  Block m_body;
+
+  mutable Block m_wire;
+};
+
+inline uint32_t
+ControlResponse::getCode() const
+{
+  return m_code;
+}
+
+inline void
+ControlResponse::setCode(uint32_t code)
+{
+  m_code = code;
+  m_wire.reset();
+}
+
+inline const std::string &
+ControlResponse::getText() const
+{
+  return m_text;
+}
+
+inline void
+ControlResponse::setText(const std::string &text)
+{
+  m_text = text;
+  m_wire.reset();
+}
+
+inline const Block&
+ControlResponse::getBody() const
+{
+  return m_body;
+}
+
+inline void
+ControlResponse::setBody(const Block& body)
+{
+  m_body = body;
+  m_body.encode(); // will do nothing if already encoded
+  m_wire.reset();
+}
+
+
+inline const Block&
+ControlResponse::wireEncode() const
+{
+  if (m_wire.hasWire())
+    return m_wire;
+
+  m_wire = Block(tlv::nfd_control::ControlResponse);
+  m_wire.push_back
+    (nonNegativeIntegerBlock(tlv::nfd_control::StatusCode, m_code));
+
+  m_wire.push_back
+    (dataBlock(tlv::nfd_control::StatusText, m_text.c_str(), m_text.size()));
+
+  if (m_body.hasWire())
+    {
+      m_wire.push_back(m_body);
+    }
+  
+  m_wire.encode();  
+  return m_wire;
+}
+
+inline void
+ControlResponse::wireDecode(const Block &wire)
+{
+  m_wire = wire;
+  m_wire.parse();
+
+  Block::element_iterator val = m_wire.getAll().begin();
+  if (val == m_wire.getAll().end() ||
+      val->type() != tlv::nfd_control::StatusCode)
+    {
+      throw Error("Incorrect ControlResponse format (StatusCode missing or not the first item)");
+    }
+  
+  m_code = readNonNegativeInteger(*val);
+  ++val;
+
+  if (val == m_wire.getAll().end() ||
+      val->type() != tlv::nfd_control::StatusText)
+    {
+      throw Error("Incorrect ControlResponse format (StatusText missing or not the second item)");
+    }
+  m_text.assign(reinterpret_cast<const char*>(val->value()), val->value_size());
+  ++val;
+
+  if (val != m_wire.getAll().end())
+    m_body = *val;
+  else
+    m_body = Block();
+}
+
+inline std::ostream&
+operator << (std::ostream &os, const ControlResponse &status)
+{
+  os << status.getCode() << " " << status.getText();
+  return os;
+}
+
+} // namespace ndn
+
+#endif // NDN_MANAGEMENT_CONTROL_RESPONSE_HPP
diff --git a/tests/test-nfd-control.cpp b/tests/test-nfd-control.cpp
new file mode 100644
index 0000000..8e282a0
--- /dev/null
+++ b/tests/test-nfd-control.cpp
@@ -0,0 +1,42 @@
+/**
+ * Copyright (C) 2013 Regents of the University of California.
+ * @author: Jeff Thompson <jefft0@remap.ucla.edu>
+ * See COPYING for copyright and distribution information.
+ */
+
+#include <boost/test/unit_test.hpp>
+
+#include "management/control-response.hpp"
+
+#include <boost/test/output_test_stream.hpp>
+
+using namespace std;
+
+namespace ndn {
+
+BOOST_AUTO_TEST_SUITE(TestNfdControl)
+
+const uint8_t TestControlResponse[] = {0x65, 0x17, 0x66, 0x02, 0x01, 0x94, 0x67, 0x11, 0x4e, 0x6f, 0x74, 0x68, 0x69, 0x6e, 0x67, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x66, 0x6f, 0x75, 0x6e, 0x64};
+
+BOOST_AUTO_TEST_CASE (ControlResponseEncode)
+{
+  ndn::ControlResponse controlResponse(404, "Nothing not found");
+  const Block &wire = controlResponse.wireEncode();
+
+  BOOST_REQUIRE_EQUAL_COLLECTIONS(TestControlResponse, TestControlResponse+sizeof(TestControlResponse),
+                                  wire.begin(), wire.end());
+}
+
+BOOST_AUTO_TEST_CASE (ControlResponseDecode)
+{
+  ndn::ControlResponse controlResponse;
+  
+  BOOST_REQUIRE_NO_THROW(controlResponse.wireDecode(Block(TestControlResponse, sizeof(TestControlResponse))));
+
+  BOOST_REQUIRE_EQUAL(controlResponse.getCode(), 404);
+  BOOST_REQUIRE_EQUAL(controlResponse.getText(), "Nothing not found");
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+
+} // namespace ndn