face: send and receive NACK

refs #2930

Change-Id: I70c969ac12b493d2c83fa892beffae936cc23791
diff --git a/src/util/dummy-client-face.cpp b/src/util/dummy-client-face.cpp
index cfa28a5..1bfce8f 100644
--- a/src/util/dummy-client-face.cpp
+++ b/src/util/dummy-client-face.cpp
@@ -33,10 +33,12 @@
 {
 public:
   void
-  receive(const Block& block)
+  receive(Block block)
   {
-    if (static_cast<bool>(m_receiveCallback))
+    block.encode();
+    if (static_cast<bool>(m_receiveCallback)) {
       m_receiveCallback(block);
+    }
   }
 
   virtual void
@@ -99,21 +101,47 @@
 DummyClientFace::construct(const Options& options)
 {
   m_transport->onSendBlock.connect([this] (const Block& blockFromDaemon) {
-    const Block& block = nfd::LocalControlHeader::getPayload(blockFromDaemon);
+    try {
+      Block packet(blockFromDaemon);
+      packet.encode();
+      lp::Packet lpPacket(packet);
 
-    if (block.type() == tlv::Interest) {
-      shared_ptr<Interest> interest = make_shared<Interest>(block);
-      if (&block != &blockFromDaemon)
-        interest->getLocalControlHeader().wireDecode(blockFromDaemon);
+      Buffer::const_iterator begin, end;
+      std::tie(begin, end) = lpPacket.get<lp::FragmentField>();
+      Block block(&*begin, std::distance(begin, end));
 
-      onSendInterest(*interest);
+      if (block.type() == tlv::Interest) {
+        shared_ptr<Interest> interest = make_shared<Interest>(block);
+        if (lpPacket.has<lp::NackField>()) {
+          shared_ptr<lp::Nack> nack = make_shared<lp::Nack>(std::move(*interest));
+          nack->setHeader(lpPacket.get<lp::NackField>());
+          if (lpPacket.has<lp::NextHopFaceIdField>()) {
+            nack->getLocalControlHeader().setNextHopFaceId(lpPacket.get<lp::NextHopFaceIdField>());
+          }
+          onSendNack(*nack);
+        }
+        else {
+          if (lpPacket.has<lp::NextHopFaceIdField>()) {
+            interest->getLocalControlHeader().
+              setNextHopFaceId(lpPacket.get<lp::NextHopFaceIdField>());
+          }
+          onSendInterest(*interest);
+        }
+      }
+      else if (block.type() == tlv::Data) {
+        shared_ptr<Data> data = make_shared<Data>(block);
+
+        if (lpPacket.has<lp::CachePolicyField>()) {
+          if (lpPacket.get<lp::CachePolicyField>().getPolicy() == lp::CachePolicyType::NO_CACHE) {
+            data->getLocalControlHeader().setCachingPolicy(nfd::LocalControlHeader::CachingPolicy::NO_CACHE);
+          }
+        }
+
+        onSendData(*data);
+      }
     }
-    else if (block.type() == tlv::Data) {
-      shared_ptr<Data> data = make_shared<Data>(block);
-      if (&block != &blockFromDaemon)
-        data->getLocalControlHeader().wireDecode(blockFromDaemon);
-
-      onSendData(*data);
+    catch (tlv::Error& e) {
+      throw tlv::Error("Error decoding NDNLPv2 packet");
     }
   });
 
@@ -133,6 +161,9 @@
   onSendData.connect([this] (const Data& data) {
     this->sentDatas.push_back(data);
   });
+  onSendNack.connect([this] (const lp::Nack& nack) {
+    this->sentNacks.push_back(nack);
+  });
 }
 
 void
@@ -168,22 +199,19 @@
 void
 DummyClientFace::receive(const Packet& packet)
 {
-  // do not restrict what injected control header can contain
-  if (!packet.getLocalControlHeader().empty(nfd::LocalControlHeader::ENCODE_ALL)) {
+  lp::Packet lpPacket(packet.wireEncode());
 
-    Block header = packet.getLocalControlHeader().wireEncode(packet,
-                                                             nfd::LocalControlHeader::ENCODE_ALL);
-    Block payload = packet.wireEncode();
+  nfd::LocalControlHeader localControlHeader = packet.getLocalControlHeader();
 
-    EncodingBuffer encoder(header.size() + payload.size(), header.size() + payload.size());
-    encoder.appendByteArray(header.wire(), header.size());
-    encoder.appendByteArray(payload.wire(), payload.size());
-
-    m_transport->receive(encoder.block());
+  if (localControlHeader.hasIncomingFaceId()) {
+    lpPacket.add<lp::IncomingFaceIdField>(localControlHeader.getIncomingFaceId());
   }
-  else {
-    m_transport->receive(packet.wireEncode());
+
+  if (localControlHeader.hasNextHopFaceId()) {
+    lpPacket.add<lp::NextHopFaceIdField>(localControlHeader.getNextHopFaceId());
   }
+
+  m_transport->receive(lpPacket.wireEncode());
 }
 
 template void
@@ -192,6 +220,23 @@
 template void
 DummyClientFace::receive<Data>(const Data& packet);
 
+template<>
+void
+DummyClientFace::receive<lp::Nack>(const lp::Nack& nack)
+{
+  lp::Packet lpPacket;
+  lpPacket.add<lp::NackField>(nack.getHeader());
+  Block interest = nack.getInterest().wireEncode();
+  lpPacket.add<lp::FragmentField>(make_pair(interest.begin(), interest.end()));
+
+  nfd::LocalControlHeader localControlHeader = nack.getLocalControlHeader();
+
+  if (localControlHeader.hasIncomingFaceId()) {
+    lpPacket.add<lp::IncomingFaceIdField>(localControlHeader.getIncomingFaceId());
+  }
+
+  m_transport->receive(lpPacket.wireEncode());
+}
 
 shared_ptr<DummyClientFace>
 makeDummyClientFace(const DummyClientFace::Options& options)