face: send and receive NACK

refs #2930

Change-Id: I70c969ac12b493d2c83fa892beffae936cc23791
diff --git a/src/face.cpp b/src/face.cpp
index 1579ee5..336e85e 100644
--- a/src/face.cpp
+++ b/src/face.cpp
@@ -134,24 +134,50 @@
 Face::~Face() = default;
 
 const PendingInterestId*
-Face::expressInterest(const Interest& interest, const OnData& onData, const OnTimeout& onTimeout)
+Face::expressInterest(const Interest& interest,
+                      const DataCallback& afterSatisfied,
+                      const NackCallback& afterNacked,
+                      const TimeoutCallback& afterTimeout)
 {
   shared_ptr<Interest> interestToExpress = make_shared<Interest>(interest);
 
   // Use `interestToExpress` to avoid wire format creation for the original Interest
-  if (interestToExpress->wireEncode().size() > MAX_NDN_PACKET_SIZE)
+  if (interestToExpress->wireEncode().size() > MAX_NDN_PACKET_SIZE) {
     BOOST_THROW_EXCEPTION(Error("Interest size exceeds maximum limit"));
+  }
 
   // If the same ioService thread, dispatch directly calls the method
-  m_ioService.dispatch([=] { m_impl->asyncExpressInterest(interestToExpress, onData, onTimeout); });
+  m_ioService.dispatch([=] { m_impl->asyncExpressInterest(interestToExpress, afterSatisfied,
+                                                          afterNacked, afterTimeout); });
 
   return reinterpret_cast<const PendingInterestId*>(interestToExpress.get());
 }
 
 const PendingInterestId*
+Face::expressInterest(const Interest& interest,
+                      const OnData& onData,
+                      const OnTimeout& onTimeout)
+{
+  return this->expressInterest(
+    interest,
+    [onData] (const Interest& interest, const Data& data) {
+      if (onData != nullptr) {
+        onData(interest, const_cast<Data&>(data));
+      }
+    },
+    [onTimeout] (const Interest& interest, const lp::Nack& nack) {
+      if (onTimeout != nullptr) {
+        onTimeout(interest);
+      }
+    },
+    onTimeout
+  );
+}
+
+const PendingInterestId*
 Face::expressInterest(const Name& name,
                       const Interest& tmpl,
-                      const OnData& onData, const OnTimeout& onTimeout/* = OnTimeout()*/)
+                      const OnData& onData, const OnTimeout& onTimeout/* = nullptr*/)
 {
   return expressInterest(Interest(tmpl)
                          .setName(name)
@@ -181,6 +207,12 @@
 }
 
 void
+Face::put(const lp::Nack& nack)
+{
+  m_ioService.dispatch([=] { m_impl->asyncPutNack(make_shared<lp::Nack>(nack)); });
+}
+
+void
 Face::removePendingInterest(const PendingInterestId* pendingInterestId)
 {
   m_ioService.post([=] { m_impl->asyncRemovePendingInterest(pendingInterestId); });
@@ -427,28 +459,50 @@
   m_impl->m_ioServiceWork.reset();
 }
 
+/**
+ * @brief extract local fields from NDNLPv2 packet and tag onto a network layer packet
+ */
+template<typename NETPKT>
+static void
+extractLpLocalFields(NETPKT& netPacket, const lp::Packet& lpPacket)
+{
+  if (lpPacket.has<lp::IncomingFaceIdField>()) {
+    netPacket.getLocalControlHeader().
+      setIncomingFaceId(lpPacket.get<lp::IncomingFaceIdField>());
+  }
+}
+
 void
 Face::onReceiveElement(const Block& blockFromDaemon)
 {
-  const Block& block = nfd::LocalControlHeader::getPayload(blockFromDaemon);
+  lp::Packet lpPacket(blockFromDaemon); // bare Interest/Data is a valid lp::Packet,
+                                        // no need to distinguish
 
-  if (block.type() == tlv::Interest)
-    {
-      shared_ptr<Interest> interest = make_shared<Interest>(block);
-      if (&block != &blockFromDaemon)
-        interest->getLocalControlHeader().wireDecode(blockFromDaemon);
-
-      m_impl->processInterestFilters(*interest);
+  Buffer::const_iterator begin, end;
+  std::tie(begin, end) = lpPacket.get<lp::FragmentField>();
+  Block netPacket(&*begin, std::distance(begin, end));
+  switch (netPacket.type()) {
+    case tlv::Interest: {
+      shared_ptr<Interest> interest = make_shared<Interest>(netPacket);
+      if (lpPacket.has<lp::NackField>()) {
+        auto nack = make_shared<lp::Nack>(std::move(*interest));
+        nack->setHeader(lpPacket.get<lp::NackField>());
+        extractLpLocalFields(*nack, lpPacket);
+        m_impl->nackPendingInterests(*nack);
+      }
+      else {
+        extractLpLocalFields(*interest, lpPacket);
+        m_impl->processInterestFilters(*interest);
+      }
+      break;
     }
-  else if (block.type() == tlv::Data)
-    {
-      shared_ptr<Data> data = make_shared<Data>(block);
-      if (&block != &blockFromDaemon)
-        data->getLocalControlHeader().wireDecode(blockFromDaemon);
-
+    case tlv::Data: {
+      shared_ptr<Data> data = make_shared<Data>(netPacket);
+      extractLpLocalFields(*data, lpPacket);
       m_impl->satisfyPendingInterests(*data);
+      break;
     }
-  // ignore any other type
+  }
 }
 
 } // namespace ndn