face: send and receive NACK

refs #2930

Change-Id: I70c969ac12b493d2c83fa892beffae936cc23791
diff --git a/src/detail/face-impl.hpp b/src/detail/face-impl.hpp
index fb6642a..98fda53 100644
--- a/src/detail/face-impl.hpp
+++ b/src/detail/face-impl.hpp
@@ -39,6 +39,9 @@
 
 #include "../management/nfd-controller.hpp"
 #include "../management/nfd-command-options.hpp"
+#include "../management/nfd-local-control-header.hpp"
+
+#include "../lp/packet.hpp"
 
 namespace ndn {
 
@@ -74,7 +77,7 @@
   satisfyPendingInterests(Data& data)
   {
     for (auto entry = m_pendingInterestTable.begin(); entry != m_pendingInterestTable.end(); ) {
-      if ((*entry)->getInterest().matchesData(data)) {
+      if ((*entry)->getInterest()->matchesData(data)) {
         shared_ptr<PendingInterest> matchedEntry = *entry;
 
         entry = m_pendingInterestTable.erase(entry);
@@ -87,6 +90,24 @@
   }
 
   void
+  nackPendingInterests(const lp::Nack& nack)
+  {
+    for (auto entry = m_pendingInterestTable.begin(); entry != m_pendingInterestTable.end(); ) {
+      const Interest& pendingInterest = *(*entry)->getInterest();
+      if (pendingInterest == nack.getInterest()) {
+        shared_ptr<PendingInterest> matchedEntry = *entry;
+
+        entry = m_pendingInterestTable.erase(entry);
+
+        matchedEntry->invokeNackCallback(nack);
+      }
+      else {
+        ++entry;
+      }
+    }
+  }
+
+  void
   processInterestFilters(Interest& interest)
   {
     for (const auto& filter : m_interestFilterTable) {
@@ -111,26 +132,32 @@
   }
 
   void
-  asyncExpressInterest(const shared_ptr<const Interest>& interest,
-                       const OnData& onData, const OnTimeout& onTimeout)
+  asyncExpressInterest(shared_ptr<const Interest> interest,
+                       const DataCallback& afterSatisfied,
+                       const NackCallback& afterNacked,
+                       const TimeoutCallback& afterTimeout)
   {
     this->ensureConnected(true);
 
     auto entry =
       m_pendingInterestTable.insert(make_shared<PendingInterest>(interest,
-                                                                 onData, onTimeout,
+                                                                 afterSatisfied,
+                                                                 afterNacked,
+                                                                 afterTimeout,
                                                                  ref(m_scheduler))).first;
     (*entry)->setDeleter([this, entry] { m_pendingInterestTable.erase(entry); });
 
-    if (!interest->getLocalControlHeader().empty(nfd::LocalControlHeader::ENCODE_NEXT_HOP)) {
-      // encode only NextHopFaceId towards the forwarder
-      m_face.m_transport->send(interest->getLocalControlHeader()
-                               .wireEncode(*interest, nfd::LocalControlHeader::ENCODE_NEXT_HOP),
-                               interest->wireEncode());
+    lp::Packet packet;
+
+    nfd::LocalControlHeader localControlHeader = interest->getLocalControlHeader();
+    if (localControlHeader.hasNextHopFaceId()) {
+      packet.add<lp::NextHopFaceIdField>(localControlHeader.getNextHopFaceId());
     }
-    else {
-      m_face.m_transport->send(interest->wireEncode());
-    }
+
+    packet.add<lp::FragmentField>(std::make_pair(interest->wireEncode().begin(),
+                                                 interest->wireEncode().end()));
+
+    m_face.m_transport->send(packet.wireEncode());
   }
 
   void
@@ -144,15 +171,40 @@
   {
     this->ensureConnected(true);
 
-    if (!data->getLocalControlHeader().empty(nfd::LocalControlHeader::ENCODE_CACHING_POLICY)) {
-      m_face.m_transport->send(
-        data->getLocalControlHeader().wireEncode(*data,
-                                                 nfd::LocalControlHeader::ENCODE_CACHING_POLICY),
-        data->wireEncode());
+    lp::Packet packet;
+
+    nfd::LocalControlHeader localControlHeader = data->getLocalControlHeader();
+    if (localControlHeader.hasCachingPolicy()) {
+      switch (localControlHeader.getCachingPolicy()) {
+        case nfd::LocalControlHeader::CachingPolicy::NO_CACHE: {
+          lp::CachePolicy cachePolicy;
+          cachePolicy.setPolicy(lp::CachePolicyType::NO_CACHE);
+          packet.add<lp::CachePolicyField>(cachePolicy);
+          break;
+        }
+        default:
+          break;
+      }
     }
-    else {
-      m_face.m_transport->send(data->wireEncode());
-    }
+
+    packet.add<lp::FragmentField>(std::make_pair(data->wireEncode().begin(),
+                                                 data->wireEncode().end()));
+
+    m_face.m_transport->send(packet.wireEncode());
+  }
+
+  void
+  asyncPutNack(shared_ptr<const lp::Nack> nack)
+  {
+    this->ensureConnected(true);
+
+    lp::Packet packet;
+    packet.add<lp::NackField>(nack->getHeader());
+
+    Block interest = nack->getInterest().wireEncode();
+    packet.add<lp::FragmentField>(std::make_pair(interest.begin(), interest.end()));
+
+    m_face.m_transport->send(packet.wireEncode());
   }
 
   /////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/src/detail/pending-interest.hpp b/src/detail/pending-interest.hpp
index 41f6b37..bac5836 100644
--- a/src/detail/pending-interest.hpp
+++ b/src/detail/pending-interest.hpp
@@ -28,31 +28,32 @@
 #include "../util/time.hpp"
 #include "../util/scheduler.hpp"
 #include "../util/scheduler-scoped-event-id.hpp"
+#include "../lp/nack.hpp"
 
 namespace ndn {
 
 class PendingInterest : noncopyable
 {
 public:
-  typedef function<void(const Interest&, Data&)> OnData;
-  typedef function<void(const Interest&)> OnTimeout;
-
   /**
    * @brief Create a new PitEntry and set the timeout based on the current time and
-   *        the interest lifetime.
-   *
-   * @param interest A shared_ptr for the interest
-   * @param onData A function object to call when a matching data packet is received.
-   * @param onTimeout A function object to call if the interest times out.
-   *                  If onTimeout is an empty OnTimeout(), this does not use it.
-   * @param scheduler Scheduler instance to use to schedule a timeout event.  The scheduled
-   *                  event will be automatically cancelled when pending interest is destroyed.
+   *        the Interest lifetime.
+   * @param interest shared_ptr for the Interest
+   * @param dataCallback function to call when matching Data packet is received
+   * @param nackCallback function to call when Nack matching Interest is received
+   * @param timeoutCallback function to call if Interest times out
+   * @param scheduler Scheduler instance to use to schedule a timeout event. The scheduled
+   *                  event will be automatically cancelled when pending Interest is destroyed.
    */
-  PendingInterest(shared_ptr<const Interest> interest, const OnData& onData,
-                  const OnTimeout& onTimeout, Scheduler& scheduler)
+  PendingInterest(shared_ptr<const Interest> interest,
+                  const DataCallback& dataCallback,
+                  const NackCallback& nackCallback,
+                  const TimeoutCallback& timeoutCallback,
+                  Scheduler& scheduler)
     : m_interest(interest)
-    , m_onData(onData)
-    , m_onTimeout(onTimeout)
+    , m_dataCallback(dataCallback)
+    , m_nackCallback(nackCallback)
+    , m_timeoutCallback(timeoutCallback)
     , m_timeoutEvent(scheduler)
   {
     m_timeoutEvent =
@@ -65,10 +66,10 @@
   /**
    * @return the Interest
    */
-  const Interest&
+  shared_ptr<const Interest>
   getInterest() const
   {
-    return *m_interest;
+    return m_interest;
   }
 
   /**
@@ -76,9 +77,19 @@
    * @note If the DataCallback is an empty function, this method does nothing.
    */
   void
-  invokeDataCallback(Data& data)
+  invokeDataCallback(const Data& data)
   {
-    m_onData(*m_interest, data);
+    m_dataCallback(*m_interest, data);
+  }
+
+  /**
+   * @brief invokes the NackCallback
+   * @note If the NackCallback is an empty function, this method does nothing.
+   */
+  void
+  invokeNackCallback(const lp::Nack& nack)
+  {
+    m_nackCallback(*m_interest, nack);
   }
 
   /**
@@ -98,8 +109,8 @@
   void
   invokeTimeoutCallback()
   {
-    if (m_onTimeout) {
-      m_onTimeout(*m_interest);
+    if (m_timeoutCallback) {
+      m_timeoutCallback(*m_interest);
     }
 
     BOOST_ASSERT(m_deleter);
@@ -108,8 +119,9 @@
 
 private:
   shared_ptr<const Interest> m_interest;
-  const OnData m_onData;
-  const OnTimeout m_onTimeout;
+  DataCallback m_dataCallback;
+  NackCallback m_nackCallback;
+  TimeoutCallback m_timeoutCallback;
   util::scheduler::ScopedEventId m_timeoutEvent;
   std::function<void()> m_deleter;
 };
@@ -133,7 +145,7 @@
   operator()(const shared_ptr<const PendingInterest>& pendingInterest) const
   {
     return (reinterpret_cast<const PendingInterestId*>(
-              &pendingInterest->getInterest()) == m_id);
+              pendingInterest->getInterest().get()) == m_id);
   }
 private:
   const PendingInterestId* m_id;