face: insert pending Interest records for Interests from forwarder

refs #4228

Change-Id: Id039f62496c6289aec8a36964b25c1f25c52eda8
diff --git a/src/detail/face-impl.hpp b/src/detail/face-impl.hpp
index 09c481a..53eb4c0 100644
--- a/src/detail/face-impl.hpp
+++ b/src/detail/face-impl.hpp
@@ -90,16 +90,17 @@
   {
     this->ensureConnected(true);
 
+    const Interest& interest2 = *interest;
     auto entry = m_pendingInterestTable.insert(make_shared<PendingInterest>(
-      interest, afterSatisfied, afterNacked, afterTimeout, ref(m_scheduler))).first;
+      std::move(interest), afterSatisfied, afterNacked, afterTimeout, ref(m_scheduler))).first;
     (*entry)->setDeleter([this, entry] { m_pendingInterestTable.erase(entry); });
 
     lp::Packet lpPacket;
-    addFieldFromTag<lp::NextHopFaceIdField, lp::NextHopFaceIdTag>(lpPacket, *interest);
-    addFieldFromTag<lp::CongestionMarkField, lp::CongestionMarkTag>(lpPacket, *interest);
+    addFieldFromTag<lp::NextHopFaceIdField, lp::NextHopFaceIdTag>(lpPacket, interest2);
+    addFieldFromTag<lp::CongestionMarkField, lp::CongestionMarkTag>(lpPacket, interest2);
 
-    m_face.m_transport->send(finishEncoding(std::move(lpPacket), interest->wireEncode(),
-                                            'I', interest->getName()));
+    m_face.m_transport->send(finishEncoding(std::move(lpPacket), interest2.wireEncode(),
+                                            'I', interest2.getName()));
   }
 
   void
@@ -114,37 +115,62 @@
     m_pendingInterestTable.clear();
   }
 
-  void
+  /** @return whether the Data should be sent to the forwarder, if it does not come from the forwarder
+   */
+  bool
   satisfyPendingInterests(const Data& data)
   {
+    bool hasAppMatch = false, hasForwarderMatch = false;
     for (auto entry = m_pendingInterestTable.begin(); entry != m_pendingInterestTable.end(); ) {
-      if ((*entry)->getInterest()->matchesData(data)) {
-        shared_ptr<PendingInterest> matchedEntry = *entry;
-        NDN_LOG_DEBUG("   satisfying " << *matchedEntry->getInterest());
-        entry = m_pendingInterestTable.erase(entry);
+      if (!(*entry)->getInterest()->matchesData(data)) {
+        ++entry;
+        continue;
+      }
+
+      shared_ptr<PendingInterest> matchedEntry = *entry;
+      NDN_LOG_DEBUG("   satisfying " << *matchedEntry->getInterest() <<
+                    " from " << matchedEntry->getOrigin());
+      entry = m_pendingInterestTable.erase(entry);
+
+      if (matchedEntry->getOrigin() == PendingInterestOrigin::APP) {
+        hasAppMatch = true;
         matchedEntry->invokeDataCallback(data);
       }
       else {
-        ++entry;
+        hasForwarderMatch = true;
       }
     }
+    // if Data matches no pending Interest record, it is sent to the forwarder as unsolicited Data
+    return hasForwarderMatch || !hasAppMatch;
   }
 
-  void
+  /** @return whether the Nack should be sent to the forwarder, if it does not come from the forwarder
+   */
+  bool
   nackPendingInterests(const lp::Nack& nack)
   {
+    bool shouldSendToForwarder = false;
     for (auto entry = m_pendingInterestTable.begin(); entry != m_pendingInterestTable.end(); ) {
-      const Interest& pendingInterest = *(*entry)->getInterest();
-      if (nack.getInterest().matchesInterest(pendingInterest)) {
-        shared_ptr<PendingInterest> matchedEntry = *entry;
-        NDN_LOG_DEBUG("   nacking " << *matchedEntry->getInterest());
-        entry = m_pendingInterestTable.erase(entry);
+      if (!nack.getInterest().matchesInterest(*(*entry)->getInterest())) {
+        ++entry;
+        continue;
+      }
+
+      shared_ptr<PendingInterest> matchedEntry = *entry;
+      NDN_LOG_DEBUG("   nacking " << *matchedEntry->getInterest() <<
+                    " from " << matchedEntry->getOrigin());
+      entry = m_pendingInterestTable.erase(entry);
+
+      // TODO #4228 record Nack on PendingInterest record, and send Nack only if all InterestFilters have Nacked
+
+      if (matchedEntry->getOrigin() == PendingInterestOrigin::APP) {
         matchedEntry->invokeNackCallback(nack);
       }
       else {
-        ++entry;
+        shouldSendToForwarder = true;
       }
     }
+    return shouldSendToForwarder;
   }
 
 public: // producer
@@ -168,12 +194,18 @@
   }
 
   void
-  processInterestFilters(const Interest& interest)
+  processIncomingInterest(shared_ptr<const Interest> interest)
   {
+    const Interest& interest2 = *interest;
+    auto entry = m_pendingInterestTable.insert(make_shared<PendingInterest>(
+      std::move(interest), ref(m_scheduler))).first;
+    (*entry)->setDeleter([this, entry] { m_pendingInterestTable.erase(entry); });
+
     for (const auto& filter : m_interestFilterTable) {
-      if (filter->doesMatch(interest.getName())) {
+      if (filter->doesMatch(interest2.getName())) {
         NDN_LOG_DEBUG("   matches " << filter->getFilter());
-        filter->invokeInterestCallback(interest);
+        filter->invokeInterestCallback(interest2);
+        // TODO #4228 record number of matched InterestFilters on PendingInterest record
       }
     }
   }
@@ -181,6 +213,11 @@
   void
   asyncPutData(const Data& data)
   {
+    bool shouldSendToForwarder = satisfyPendingInterests(data);
+    if (!shouldSendToForwarder) {
+      return;
+    }
+
     this->ensureConnected(true);
 
     lp::Packet lpPacket;
@@ -194,6 +231,11 @@
   void
   asyncPutNack(const lp::Nack& nack)
   {
+    bool shouldSendToForwarder = nackPendingInterests(nack);
+    if (!shouldSendToForwarder) {
+      return;
+    }
+
     this->ensureConnected(true);
 
     lp::Packet lpPacket;