face: send Nack after every InterestCallback has responded

refs #4228

Change-Id: I3b1ef58c70d34c2099249216a3600efe01b79f71
diff --git a/src/detail/pending-interest.hpp b/src/detail/pending-interest.hpp
index 38d7f70..2a4da87 100644
--- a/src/detail/pending-interest.hpp
+++ b/src/detail/pending-interest.hpp
@@ -80,6 +80,7 @@
     , m_nackCallback(nackCallback)
     , m_timeoutCallback(timeoutCallback)
     , m_timeoutEvent(scheduler)
+    , m_nNotNacked(0)
   {
     scheduleTimeoutEvent(scheduler);
   }
@@ -94,6 +95,7 @@
     : m_interest(std::move(interest))
     , m_origin(PendingInterestOrigin::FORWARDER)
     , m_timeoutEvent(scheduler)
+    , m_nNotNacked(0)
   {
     scheduleTimeoutEvent(scheduler);
   }
@@ -114,6 +116,35 @@
   }
 
   /**
+   * @brief Record that the Interest has been forwarded to one destination
+   *
+   * A "destination" could be either a local InterestFilter or the forwarder.
+   */
+  void
+  recordForwarding()
+  {
+    ++m_nNotNacked;
+  }
+
+  /**
+   * @brief Record an incoming Nack against a forwarded Interest
+   * @return least severe Nack if all destinations where the Interest was forwarded have Nacked;
+   *          otherwise, nullopt
+   */
+  optional<lp::Nack>
+  recordNack(const lp::Nack& nack)
+  {
+    --m_nNotNacked;
+    BOOST_ASSERT(m_nNotNacked >= 0);
+
+    if (!m_leastSevereNack || lp::isLessSevere(nack.getReason(), m_leastSevereNack->getReason())) {
+      m_leastSevereNack = nack;
+    }
+
+    return m_nNotNacked > 0 ? nullopt : m_leastSevereNack;
+  }
+
+  /**
    * @brief Invoke the Data callback
    * @note This method does nothing if the Data callback is empty
    */
@@ -175,6 +206,8 @@
   NackCallback m_nackCallback;
   TimeoutCallback m_timeoutCallback;
   util::scheduler::ScopedEventId m_timeoutEvent;
+  int m_nNotNacked; ///< number of Interest destinations that have not Nacked
+  optional<lp::Nack> m_leastSevereNack;
   std::function<void()> m_deleter;
 };