fw: refine pipelines, dispatch to strategy

refs #1345 #1255

Change-Id: If1cfc26049f87318103fc09c3b211ebf1eb3ebaa
diff --git a/daemon/fw/forwarder.cpp b/daemon/fw/forwarder.cpp
index 6c9f3ce..3a8a91f 100644
--- a/daemon/fw/forwarder.cpp
+++ b/daemon/fw/forwarder.cpp
@@ -5,14 +5,16 @@
  */
 
 #include "forwarder.hpp"
-#include "core/logger.hpp"
-#include "best-route-strategy.hpp"
+#include "available-strategies.hpp"
 
 namespace nfd {
 
 NFD_LOG_INIT("Forwarder");
 
-const Name Forwarder::s_localhostName("ndn:/localhost");
+using fw::Strategy;
+
+const ndn::Milliseconds Forwarder::DEFAULT_INTEREST_LIFETIME(static_cast<ndn::Milliseconds>(4000));
+const Name Forwarder::LOCALHOST_NAME("ndn:/localhost");
 
 Forwarder::Forwarder()
   : m_faceTable(*this)
@@ -20,24 +22,29 @@
   , m_fib(m_nameTree)
   , m_pit(m_nameTree)
   , m_measurements(m_nameTree)
-  , m_strategyChoice(m_nameTree, make_shared<fw::BestRouteStrategy>(boost::ref(*this)))
+  , m_strategyChoice(m_nameTree, fw::makeDefaultStrategy(*this))
 {
-  m_strategy = make_shared<fw::BestRouteStrategy>(boost::ref(*this));
+  fw::installStrategies(*this);
 }
 
 void
 Forwarder::onIncomingInterest(Face& inFace, const Interest& interest)
 {
   // receive Interest
-  NFD_LOG_DEBUG("onIncomingInterest face=" << inFace.getId() << " interest=" << interest.getName());
+  NFD_LOG_DEBUG("onIncomingInterest face=" << inFace.getId() <<
+                " interest=" << interest.getName());
   const_cast<Interest&>(interest).setIncomingFaceId(inFace.getId());
+  if (interest.getInterestLifetime() < 0) {
+    const_cast<Interest&>(interest).setInterestLifetime(DEFAULT_INTEREST_LIFETIME);
+  }
 
   // /localhost scope control
-  bool violatesLocalhost = !inFace.isLocal() &&
-                           s_localhostName.isPrefixOf(interest.getName());
-  if (violatesLocalhost) {
-    NFD_LOG_DEBUG("onIncomingInterest face=" << inFace.getId()
-      << " interest=" << interest.getName() << " violates /localhost");
+  bool isViolatingLocalhost = !inFace.isLocal() &&
+                              LOCALHOST_NAME.isPrefixOf(interest.getName());
+  if (isViolatingLocalhost) {
+    NFD_LOG_DEBUG("onIncomingInterest face=" << inFace.getId() <<
+                  " interest=" << interest.getName() << " violates /localhost");
+    // (drop)
     return;
   }
 
@@ -55,9 +62,9 @@
   // cancel unsatisfy & straggler timer
   this->cancelUnsatisfyAndStragglerTimer(pitEntry);
 
+  // is pending?
   const pit::InRecordCollection& inRecords = pitEntry->getInRecords();
   bool isPending = inRecords.begin() == inRecords.end();
-
   if (!isPending) {
     // CS lookup
     const Data* csMatch = m_cs.find(interest);
@@ -73,46 +80,71 @@
   // insert InRecord
   pitEntry->insertOrUpdateInRecord(inFace.shared_from_this(), interest);
 
-  // app chosen nexthops
-  bool isAppChosenNexthops = false; // TODO get from local control header
-  if (isAppChosenNexthops) {
-    // TODO foreach chosen nexthop: goto outgoing Interest pipeline
-    return;
-  }
-
   // FIB lookup
   shared_ptr<fib::Entry> fibEntry = m_fib.findLongestPrefixMatch(*pitEntry);
 
   // dispatch to strategy
-  this->dispatchToStrategy(inFace, interest, fibEntry, pitEntry);
+  this->dispatchToStrategy(pitEntry, bind(&Strategy::afterReceiveInterest, _1,
+    boost::cref(inFace), boost::cref(interest), fibEntry, pitEntry));
 }
 
 void
 Forwarder::onInterestLoop(Face& inFace, const Interest& interest,
                           shared_ptr<pit::Entry> pitEntry)
 {
-  NFD_LOG_DEBUG("onInterestLoop face=" << inFace.getId() << " interest=" << interest.getName());
+  NFD_LOG_DEBUG("onInterestLoop face=" << inFace.getId() <<
+                " interest=" << interest.getName());
 
-  // do nothing, which means Interest is dropped
+  // (drop)
+}
+
+/** \brief compare two InRecords for picking outgoing Interest
+ *  \return true if b is preferred over a
+ *
+ *  This function should be passed to std::max_element over InRecordCollection.
+ *  The outgoing Interest picked is the last incoming Interest
+ *  that does not come from outFace.
+ *  If all InRecords come from outFace, it's fine to pick that. This happens when
+ *  there's only one InRecord that comes from outFace. The legit use is for
+ *  vehicular network; otherwise, strategy shouldn't send to the sole inFace.
+ */
+static inline bool
+compare_pickInterest(const pit::InRecord& a, const pit::InRecord& b, const Face* outFace)
+{
+  bool isOutFaceA = a.getFace().get() == outFace;
+  bool isOutFaceB = b.getFace().get() == outFace;
+
+  if (!isOutFaceA && isOutFaceB) {
+    return false;
+  }
+  if (isOutFaceA && !isOutFaceB) {
+    return true;
+  }
+
+  return a.getLastRenewed() > b.getLastRenewed();
 }
 
 void
 Forwarder::onOutgoingInterest(shared_ptr<pit::Entry> pitEntry, Face& outFace)
 {
-  NFD_LOG_DEBUG("onOutgoingInterest face=" << outFace.getId() << " interest=" << pitEntry->getName());
+  NFD_LOG_DEBUG("onOutgoingInterest face=" << outFace.getId() <<
+                " interest=" << pitEntry->getName());
 
   // /localhost scope control
-  bool violatesLocalhost = !outFace.isLocal() &&
-                           s_localhostName.isPrefixOf(pitEntry->getName());
-  if (violatesLocalhost) {
-    NFD_LOG_DEBUG("onOutgoingInterest face=" << outFace.getId()
-      << " interest=" << pitEntry->getName() << " violates /localhost");
+  bool isViolatingLocalhost = !outFace.isLocal() &&
+                              LOCALHOST_NAME.isPrefixOf(pitEntry->getName());
+  if (isViolatingLocalhost) {
+    NFD_LOG_DEBUG("onOutgoingInterest face=" << outFace.getId() <<
+                  " interest=" << pitEntry->getName() << " violates /localhost");
     return;
   }
 
   // pick Interest
-  const Interest& interest = pitEntry->getInterest();
-  // TODO pick the last incoming Interest
+  const pit::InRecordCollection& inRecords = pitEntry->getInRecords();
+  pit::InRecordCollection::const_iterator pickedInRecord = std::max_element(
+    inRecords.begin(), inRecords.end(), bind(&compare_pickInterest, _1, _2, &outFace));
+  BOOST_ASSERT(pickedInRecord != inRecords.end());
+  const Interest& interest = pickedInRecord->getInterest();
 
   // insert OutRecord
   pitEntry->insertOrUpdateOutRecord(outFace.shared_from_this(), interest);
@@ -139,9 +171,10 @@
   NFD_LOG_DEBUG("onInterestUnsatisfied interest=" << pitEntry->getName());
 
   // invoke PIT unsatisfied callback
-  // TODO
+  this->dispatchToStrategy(pitEntry, bind(&Strategy::beforeExpirePendingInterest, _1,
+    pitEntry));
 
-  // PIT erase
+  // PIT delete
   m_pit.erase(pitEntry);
 }
 
@@ -153,11 +186,12 @@
   const_cast<Data&>(data).setIncomingFaceId(inFace.getId());
 
   // /localhost scope control
-  bool violatesLocalhost = !inFace.isLocal() &&
-                           s_localhostName.isPrefixOf(data.getName());
-  if (violatesLocalhost) {
-    NFD_LOG_DEBUG("onIncomingData face=" << inFace.getId()
-      << " data=" << data.getName() << " violates /localhost");
+  bool isViolatingLocalhost = !inFace.isLocal() &&
+                              LOCALHOST_NAME.isPrefixOf(data.getName());
+  if (isViolatingLocalhost) {
+    NFD_LOG_DEBUG("onIncomingData face=" << inFace.getId() <<
+                  " data=" << data.getName() << " violates /localhost");
+    // (drop)
     return;
   }
 
@@ -199,7 +233,8 @@
     this->setStragglerTimer(pitEntry);
 
     // invoke PIT satisfy callback
-    // TODO
+    this->dispatchToStrategy(pitEntry, bind(&Strategy::beforeSatisfyPendingInterest, _1,
+      pitEntry, boost::cref(inFace), boost::cref(data)));
   }
 
   // foreach pending downstream
@@ -214,13 +249,15 @@
 Forwarder::onDataUnsolicited(Face& inFace, const Data& data)
 {
   // accept to cache?
-  bool acceptToCache = false;// TODO decision
+  bool acceptToCache = inFace.isLocal();
   if (acceptToCache) {
     // CS insert
-    m_cs.insert(data);
+    m_cs.insert(data, true);
   }
 
-  NFD_LOG_DEBUG("onDataUnsolicited face=" << inFace.getId() << " data=" << data.getName() << " acceptToCache=" << acceptToCache);
+  NFD_LOG_DEBUG("onDataUnsolicited face=" << inFace.getId() <<
+                " data=" << data.getName() <<
+                (acceptToCache ? " cached" : " not cached"));
 }
 
 void
@@ -229,16 +266,16 @@
   NFD_LOG_DEBUG("onOutgoingData face=" << outFace.getId() << " data=" << data.getName());
 
   // /localhost scope control
-  bool violatesLocalhost = !outFace.isLocal() &&
-                           s_localhostName.isPrefixOf(data.getName());
-  if (violatesLocalhost) {
-    NFD_LOG_DEBUG("onOutgoingData face=" << outFace.getId()
-      << " data=" << data.getName() << " violates /localhost");
+  bool isViolatingLocalhost = !outFace.isLocal() &&
+                              LOCALHOST_NAME.isPrefixOf(data.getName());
+  if (isViolatingLocalhost) {
+    NFD_LOG_DEBUG("onOutgoingData face=" << outFace.getId() <<
+                  " data=" << data.getName() << " violates /localhost");
+    // (drop)
     return;
   }
 
-  // traffic manager
-  // pass through
+  // TODO traffic manager
 
   // send Data
   outFace.sendData(data);
@@ -284,14 +321,4 @@
   scheduler::cancel(pitEntry->m_stragglerTimer);
 }
 
-void
-Forwarder::dispatchToStrategy(const Face& inFace,
-                              const Interest& interest,
-                              shared_ptr<fib::Entry> fibEntry,
-                              shared_ptr<pit::Entry> pitEntry)
-{
-  m_strategy->afterReceiveInterest(inFace, interest, fibEntry, pitEntry);
-  // TODO dispatch according to fibEntry
-}
-
 } // namespace nfd