// # NDNLPv2 API: NACK in client Face // ## extending ndn::Face class namespace ndn { typedef function<void(const Interest&, const Data&)> DataCallback; typedef function<void(const Interest&, const lp::Nack&)> NackCallback; typedef function<void(const Interest&)> TimeoutCallback; // note: OnData and OnTimeout typedefs were mistakes, because type names should be a noun. // Also, OnData shouldn't need a mutable reference for the Data. // Therefore, this API is using a new set of callbacks. partial class Face { public: // consumer /** \brief sends an Interest * \param interest the Interest; a copy will be made, so that the caller * is not required to maintain the argument unchanged * \param afterSatisfied a function to be invoked if a Data is returned * \param afterNacked a function to be invoked if a Network NACK is returned * \param afterTimeout a function to be invoked if neither Data nor Network NACK * is returned within InterestLifetime */ const PendingInterestId* expressInterest(const Interest& interest, const DataCallback& afterSatisfied, const NackCallback& afterNacked, const TimeoutCallback& afterTimeout); // note: I wanted to make the last two parameters optional, but it would cause compilation error // because std::function<> template has aggresive constructor which takes everything, // so that the compiler thinks an expressInterest call with two or three arguments // is ambiguous with the deprecated form below. // Declaring XCallback as plain function signatures instead of std::function helps, // but that would reject std::bind arguments. // Thus, this has to require four arguments for now. // After the other form is removed, the last two arguments could default to nullptr. /** \deprecated use expressInterest(Interest, DataCallback, NackCallback, TimeoutCallback) */ const PendingInterestId* expressInterest(const Interest& interest, const OnData& onData, const OnTimeout& onTimeout = nullptr); public: // producer /** \brief sends a Network NACK * \param nack the Nack; a copy will be made, so that the caller * is not required to maintain the argument unchanged */ void put(const lp::Nack& nack); }; } // namespace ndn // ## implementation detail: PendingInterest struct /** \brief represents a pending Interest * \note This type is implementation detail of Face class. * * The Face maintains a collection of PendingInterests. * A PendingInterest is added when an Interest is expressed, * and is removed when it's satisified by a Data packet, * rejected by a Nack, or timed out. */ partial class PendingInterest { public: PendingInterest(shared_ptr<const Interest> interest, const DataCallback& dataCallback, const NackCallback& nackCallback, const TimeoutCallback& timeoutCallback, Scheduler& scheduler); /** \return the Interest */ shared_ptr<const Interest> getInterest() const; /** \brief invokes the DataCallback * \note If the DataCallback is an empty function, this method does nothing. */ void invokeDataCallback(const Data& data); /** \brief invokes the NackCallback * \note If the NackCallback is an empty function, this method does nothing. */ void invokeNackCallback(const lp::Nack& nack); private: shared_ptr<const Interest> m_interest; DataCallback m_dataCallback; NackCallback m_nackCallback; TimeoutCallback m_timeoutCallback; }; // unchanged: setDeleter, invokeTimeoutCallback, m_timeoutEvent, m_deleter // ## implementation detail: FaceImpl partial class FaceImpl { public: void nackPendingInterests(const Nack& nack); // note: The procedure is similar to satisfyPendingInterests. void asyncExpressInterest(shared_ptr<const Interest> interest, const DataCallback& afterSatisfied, const NackCallback& afterNacked, const TimeoutCallback& afterTimeout); void asyncPutNack(shared_ptr<const Nack> nack); // note: It's intentional to use shared_ptr<const Nack> instead of const shared_ptr<const Nack>& because that's semantically wrong. }; // asyncExpressInterest, asyncPutData, asyncPutNack should construct lp::Packet from every network layer packet, // and set LocalControlHeader fields into LpPacket. // ## implementation detail: Face // expressInterest is updated to call asyncExpressInterest with afterNacked argument. /** \brief extract local fields from NDNLPv2 packet and tag onto a network layer packet */ template<typename NETPKT> static void extractLpLocalFields(NETPKT& netPacket, const lp::Packet& lpPacket); // note: This extracts IncomingFaceId field and puts it into LocalControlHeader. // Eventually LocalControlHeader will be deprecated and replaced with a Tag. // This template requires Nack class to be extended with 'nfd::LocalControlHeader& getLocalControlHeader()' API, // but the storage of this field can be redirected to the Interest contained within. void Face::onReceiveElement(const Block& blockFromDaemon) { lp::Packet lpPacket(blockFromDaemon); // bare Interest/Data is a valid lp::Packet, no need to distinguish Block netPacket = /* extract Fragment as a Block */; switch (netPacket.type()) { case tlv::Interest: shared_ptr<Interest> interest = /* parse netPacket as Interest */; if (lpPacket.has<lp::NackField>()) { auto nack = make_shared<lp::Nack>(std::move(*interest)); interest.reset(); extractLpLocalFields(*nack, lpPacket); m_impl->nackPendingInterests(*nack); } else { extractLpLocalFields(*interest, lpPacket); /* incoming Interest processing */ } break; case tlv::Data: shared_ptr<Data> data = /* parse netPacket as Data */; extractLpLocalFields(*data, lpPacket); /* incoming Data processing */ break; } }