face: Interest loopback
refs #3979
Change-Id: I6f65b30451939397975a7af40c59002e330afc25
diff --git a/src/detail/face-impl.hpp b/src/detail/face-impl.hpp
index f0c7231..86945ba 100644
--- a/src/detail/face-impl.hpp
+++ b/src/detail/face-impl.hpp
@@ -88,21 +88,26 @@
const NackCallback& afterNacked,
const TimeoutCallback& afterTimeout)
{
+ NDN_LOG_DEBUG("<I " << *interest);
this->ensureConnected(true);
const Interest& interest2 = *interest;
auto i = m_pendingInterestTable.insert(make_shared<PendingInterest>(
std::move(interest), afterSatisfied, afterNacked, afterTimeout, ref(m_scheduler))).first;
- PendingInterest& entry = **i;
- entry.setDeleter([this, i] { m_pendingInterestTable.erase(i); });
+ // In dispatchInterest, an InterestCallback may respond with Data right away and delete
+ // the PendingInterestTable entry. shared_ptr is retained to ensure PendingInterest instance
+ // remains valid in this case.
+ shared_ptr<PendingInterest> entry = *i;
+ entry->setDeleter([this, i] { m_pendingInterestTable.erase(i); });
lp::Packet lpPacket;
addFieldFromTag<lp::NextHopFaceIdField, lp::NextHopFaceIdTag>(lpPacket, interest2);
addFieldFromTag<lp::CongestionMarkField, lp::CongestionMarkTag>(lpPacket, interest2);
- entry.recordForwarding();
+ entry->recordForwarding();
m_face.m_transport->send(finishEncoding(std::move(lpPacket), interest2.wireEncode(),
'I', interest2.getName()));
+ dispatchInterest(*entry, interest2);
}
void
@@ -206,16 +211,23 @@
const Interest& interest2 = *interest;
auto i = m_pendingInterestTable.insert(make_shared<PendingInterest>(
std::move(interest), ref(m_scheduler))).first;
- // InterestCallback may put Data right away and delete the entry from PendingInterestTable.
- // shared_ptr is retained to ensure PendingInterest instance is valid throughout the loop.
+ // In dispatchInterest, an InterestCallback may respond with Data right away and delete
+ // the PendingInterestTable entry. shared_ptr is retained to ensure PendingInterest instance
+ // remains valid in this case.
shared_ptr<PendingInterest> entry = *i;
entry->setDeleter([this, i] { m_pendingInterestTable.erase(i); });
+ this->dispatchInterest(*entry, interest2);
+ }
+
+ void
+ dispatchInterest(PendingInterest& entry, const Interest& interest)
+ {
for (const auto& filter : m_interestFilterTable) {
- if (filter->doesMatch(interest2.getName())) {
+ if (filter->doesMatch(entry)) {
NDN_LOG_DEBUG(" matches " << filter->getFilter());
- entry->recordForwarding();
- filter->invokeInterestCallback(interest2);
+ entry.recordForwarding();
+ filter->invokeInterestCallback(interest);
}
}
}
@@ -223,6 +235,7 @@
void
asyncPutData(const Data& data)
{
+ NDN_LOG_DEBUG("<D " << data.getName());
bool shouldSendToForwarder = satisfyPendingInterests(data);
if (!shouldSendToForwarder) {
return;
@@ -241,6 +254,7 @@
void
asyncPutNack(const lp::Nack& nack)
{
+ NDN_LOG_DEBUG("<N " << nack.getInterest() << '~' << nack.getHeader().getReason());
optional<lp::Nack> outNack = nackPendingInterests(nack);
if (!outNack) {
return;
diff --git a/src/detail/interest-filter-record.hpp b/src/detail/interest-filter-record.hpp
index f771d16..154112c 100644
--- a/src/detail/interest-filter-record.hpp
+++ b/src/detail/interest-filter-record.hpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2013-2016 Regents of the University of California.
+/*
+ * Copyright (c) 2013-2017 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
@@ -22,8 +22,7 @@
#ifndef NDN_DETAIL_INTEREST_FILTER_RECORD_HPP
#define NDN_DETAIL_INTEREST_FILTER_RECORD_HPP
-#include "../name.hpp"
-#include "../interest.hpp"
+#include "pending-interest.hpp"
namespace ndn {
@@ -60,9 +59,10 @@
* @param name Interest Name
*/
bool
- doesMatch(const Name& name) const
+ doesMatch(const PendingInterest& entry) const
{
- return m_filter.doesMatch(name);
+ return (entry.getOrigin() == PendingInterestOrigin::FORWARDER || m_filter.allowsLoopback()) &&
+ m_filter.doesMatch(entry.getInterest()->getName());
}
/**
diff --git a/src/face.cpp b/src/face.cpp
index 92c46ab..fdb6b29 100644
--- a/src/face.cpp
+++ b/src/face.cpp
@@ -182,7 +182,6 @@
{
shared_ptr<Interest> interest2 = make_shared<Interest>(interest);
interest2->getNonce();
- NDN_LOG_DEBUG("<I " << *interest2);
IO_CAPTURE_WEAK_IMPL(dispatch) {
impl->asyncExpressInterest(interest2, afterSatisfied, afterNacked, afterTimeout);
@@ -216,7 +215,6 @@
void
Face::put(Data data)
{
- NDN_LOG_DEBUG("<D " << data.getName());
IO_CAPTURE_WEAK_IMPL(dispatch) {
impl->asyncPutData(data);
} IO_CAPTURE_WEAK_IMPL_END
@@ -225,7 +223,6 @@
void
Face::put(lp::Nack nack)
{
- NDN_LOG_DEBUG("<N " << nack.getInterest() << '~' << nack.getHeader().getReason());
IO_CAPTURE_WEAK_IMPL(dispatch) {
impl->asyncPutNack(nack);
} IO_CAPTURE_WEAK_IMPL_END
diff --git a/src/interest-filter.hpp b/src/interest-filter.hpp
index d6241a1..f2a09c0 100644
--- a/src/interest-filter.hpp
+++ b/src/interest-filter.hpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
-/**
- * Copyright (c) 2013-2016 Regents of the University of California.
+/*
+ * Copyright (c) 2013-2017 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
@@ -125,9 +125,30 @@
return *m_regexFilter;
}
+ /** \brief Get whether Interest loopback is allowed
+ */
+ bool
+ allowsLoopback() const
+ {
+ return m_allowsLoopback;
+ }
+
+ /** \brief Set whether Interest loopback is allowed
+ * \param wantLoopback if true, this InterestFilter may receive Interests that are expressed
+ * locally on the same \p ndn::Face ; if false, this InterestFilter can only
+ * receive Interests received from the forwarder. The default is true.
+ */
+ InterestFilter&
+ allowLoopback(bool wantLoopback)
+ {
+ m_allowsLoopback = wantLoopback;
+ return *this;
+ }
+
private:
Name m_prefix;
shared_ptr<RegexPatternListMatcher> m_regexFilter;
+ bool m_allowsLoopback = true;
};
std::ostream&
diff --git a/tests/unit-tests/face.t.cpp b/tests/unit-tests/face.t.cpp
index ceee9f7..3c903e9 100644
--- a/tests/unit-tests/face.t.cpp
+++ b/tests/unit-tests/face.t.cpp
@@ -325,6 +325,34 @@
BOOST_CHECK(face.sentData[1].getTag<lp::CongestionMarkTag>() != nullptr);
}
+BOOST_AUTO_TEST_CASE(PutDataLoopback)
+{
+ bool hasInterest1 = false, hasData = false;
+
+ // first InterestFilter allows loopback and should receive Interest
+ face.setInterestFilter("/", [&] (const InterestFilter&, const Interest& interest) {
+ hasInterest1 = true;
+ // do not respond with Data right away, so Face must send Interest to forwarder
+ });
+ // second InterestFilter disallows loopback and should not receive Interest
+ face.setInterestFilter(InterestFilter("/").allowLoopback(false),
+ bind([] { BOOST_ERROR("Unexpected Interest on second InterestFilter"); }));
+
+ face.expressInterest(Interest("/A"),
+ bind([&] { hasData = true; }),
+ bind([] { BOOST_FAIL("Unexpected nack"); }),
+ bind([] { BOOST_FAIL("Unexpected timeout"); }));
+ advanceClocks(time::milliseconds(1));
+ BOOST_CHECK_EQUAL(hasInterest1, true); // Interest looped back
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 1); // Interest sent to forwarder
+ BOOST_CHECK_EQUAL(hasData, false); // waiting for Data
+
+ face.put(*makeData("/A/B")); // first InterestFilter responds with Data
+ advanceClocks(time::milliseconds(1));
+ BOOST_CHECK_EQUAL(hasData, true);
+ BOOST_CHECK_EQUAL(face.sentData.size(), 0); // do not spill Data to forwarder
+}
+
BOOST_AUTO_TEST_CASE(PutMultipleData)
{
bool hasInterest1 = false;
@@ -406,6 +434,36 @@
BOOST_CHECK_EQUAL(face.sentNacks.size(), 1); // additional Nacks are ignored
}
+BOOST_AUTO_TEST_CASE(PutMultipleNackLoopback)
+{
+ bool hasInterest1 = false, hasNack = false;
+
+ // first InterestFilter allows loopback and should receive Interest
+ face.setInterestFilter("/", [&] (const InterestFilter&, const Interest& interest) {
+ hasInterest1 = true;
+ face.put(makeNack(interest, lp::NackReason::CONGESTION));
+ });
+ // second InterestFilter disallows loopback and should not receive Interest
+ face.setInterestFilter(InterestFilter("/").allowLoopback(false),
+ bind([] { BOOST_ERROR("Unexpected Interest on second InterestFilter"); }));
+
+ face.expressInterest(*makeInterest("/A", 28395852),
+ bind([] { BOOST_FAIL("Unexpected data"); }),
+ [&] (const Interest&, const lp::Nack& nack) {
+ hasNack = true;
+ BOOST_CHECK_EQUAL(nack.getReason(), lp::NackReason::CONGESTION);
+ },
+ bind([] { BOOST_FAIL("Unexpected timeout"); }));
+ advanceClocks(time::milliseconds(1));
+ BOOST_CHECK_EQUAL(hasInterest1, true); // Interest looped back
+ BOOST_CHECK_EQUAL(face.sentInterests.size(), 1); // Interest sent to forwarder
+ BOOST_CHECK_EQUAL(hasNack, false); // waiting for Nack from forwarder
+
+ face.receive(makeNack("/A", 28395852, lp::NackReason::NO_ROUTE));
+ advanceClocks(time::milliseconds(1));
+ BOOST_CHECK_EQUAL(hasNack, true);
+}
+
BOOST_AUTO_TEST_CASE(SetUnsetInterestFilter)
{
size_t nInterests = 0;
@@ -548,6 +606,7 @@
BOOST_FIXTURE_TEST_CASE(RegisterUnregisterPrefixFail, FacesNoRegistrationReplyFixture)
{
+ // don't enable registration reply
size_t nRegFailures = 0;
face.registerPrefix("/Hello/World",
bind([] { BOOST_FAIL("Unexpected registerPrefix success"); }),
diff --git a/tests/unit-tests/interest-filter.t.cpp b/tests/unit-tests/interest-filter.t.cpp
index 26be797..8277c23 100644
--- a/tests/unit-tests/interest-filter.t.cpp
+++ b/tests/unit-tests/interest-filter.t.cpp
@@ -61,6 +61,14 @@
BOOST_CHECK_THROW(face.receive(Interest("/Hello/World/a/b/c")), InterestFilter::Error);
}
+BOOST_AUTO_TEST_CASE(AllowLoopback)
+{
+ InterestFilter filter("/A");
+ BOOST_CHECK_EQUAL(filter.allowsLoopback(), true);
+ BOOST_CHECK_EQUAL(&filter.allowLoopback(false), &filter);
+ BOOST_CHECK_EQUAL(filter.allowsLoopback(), false);
+}
+
BOOST_AUTO_TEST_SUITE_END() // TestInterestFilter
} // namespace tests