blob: 460d3168dfa107bbdaf420eb54c57dc9f00dcb84 [file] [log] [blame]
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2013-2014 Regents of the University of California.
*
* This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
*
* ndn-cxx library is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later version.
*
* ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received copies of the GNU General Public License and GNU Lesser
* General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
* <http://www.gnu.org/licenses/>.
*
* See AUTHORS.md for complete list of ndn-cxx authors and contributors.
*/
#ifndef NDN_DETAIL_FACE_IMPL_HPP
#define NDN_DETAIL_FACE_IMPL_HPP
#include "../common.hpp"
#include "../face.hpp"
#include "registered-prefix.hpp"
#include "pending-interest.hpp"
#include "../util/scheduler.hpp"
#include "../util/config-file.hpp"
#include "../transport/transport.hpp"
#include "../transport/unix-transport.hpp"
#include "../transport/tcp-transport.hpp"
#include "../management/nfd-controller.hpp"
#include "../management/nfd-command-options.hpp"
namespace ndn {
class Face::Impl : noncopyable
{
public:
typedef std::list<shared_ptr<PendingInterest> > PendingInterestTable;
typedef std::list<shared_ptr<InterestFilterRecord> > InterestFilterTable;
typedef std::list<shared_ptr<RegisteredPrefix> > RegisteredPrefixTable;
explicit
Impl(Face& face)
: m_face(face)
{
}
/////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////
void
satisfyPendingInterests(Data& data)
{
for (PendingInterestTable::iterator i = m_pendingInterestTable.begin();
i != m_pendingInterestTable.end();
)
{
if ((*i)->getInterest()->matchesData(data))
{
// Copy pointers to the objects and remove the PIT entry before calling the callback.
OnData onData = (*i)->getOnData();
shared_ptr<const Interest> interest = (*i)->getInterest();
PendingInterestTable::iterator next = i;
++next;
m_pendingInterestTable.erase(i);
i = next;
if (static_cast<bool>(onData)) {
onData(*interest, data);
}
}
else
++i;
}
}
void
processInterestFilters(Interest& interest)
{
for (InterestFilterTable::iterator i = m_interestFilterTable.begin();
i != m_interestFilterTable.end();
++i)
{
if ((*i)->doesMatch(interest.getName()))
{
(**i)(interest);
}
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////
void
ensureConnected(bool wantResume = true)
{
if (!m_face.m_transport->isConnected())
m_face.m_transport->connect(m_face.m_ioService,
bind(&Face::onReceiveElement, &m_face, _1));
if (wantResume && !m_face.m_transport->isExpectingData())
m_face.m_transport->resume();
}
void
asyncExpressInterest(const shared_ptr<const Interest>& interest,
const OnData& onData, const OnTimeout& onTimeout)
{
this->ensureConnected();
m_pendingInterestTable.push_back(make_shared<PendingInterest>(interest, onData, onTimeout));
if (!interest->getLocalControlHeader().empty(false, true))
{
// encode only NextHopFaceId towards the forwarder
m_face.m_transport->send(interest->getLocalControlHeader()
.wireEncode(*interest, false, true),
interest->wireEncode());
}
else
{
m_face.m_transport->send(interest->wireEncode());
}
if (!m_pitTimeoutCheckTimerActive) {
m_pitTimeoutCheckTimerActive = true;
m_pitTimeoutCheckTimer->expires_from_now(time::milliseconds(100));
m_pitTimeoutCheckTimer->async_wait(bind(&Impl::checkPitExpire, this));
}
}
void
asyncRemovePendingInterest(const PendingInterestId* pendingInterestId)
{
m_pendingInterestTable.remove_if(MatchPendingInterestId(pendingInterestId));
}
void
asyncPutData(const shared_ptr<const Data>& data)
{
this->ensureConnected();
if (!data->getLocalControlHeader().empty(false, true))
{
m_face.m_transport->send(data->getLocalControlHeader().wireEncode(*data, false, true),
data->wireEncode());
}
else
{
m_face.m_transport->send(data->wireEncode());
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////
void
asyncSetInterestFilter(const shared_ptr<InterestFilterRecord>& interestFilterRecord)
{
m_interestFilterTable.push_back(interestFilterRecord);
}
void
asyncUnsetInterestFilter(const InterestFilterId* interestFilterId)
{
InterestFilterTable::iterator i = std::find_if(m_interestFilterTable.begin(),
m_interestFilterTable.end(),
MatchInterestFilterId(interestFilterId));
if (i != m_interestFilterTable.end())
{
m_interestFilterTable.erase(i);
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////
const RegisteredPrefixId*
registerPrefix(const Name& prefix,
const shared_ptr<InterestFilterRecord>& filter,
const RegisterPrefixSuccessCallback& onSuccess,
const RegisterPrefixFailureCallback& onFailure,
uint64_t flags,
const nfd::CommandOptions& options)
{
using namespace nfd;
typedef void (Controller::*Registrator)
(const ControlParameters&,
const Controller::CommandSucceedCallback&,
const Controller::CommandFailCallback&,
const CommandOptions&);
ControlParameters registerParameters, unregisterParameters;
registerParameters.setName(prefix);
unregisterParameters.setName(prefix);
Registrator registrator, unregistrator;
if (!m_face.m_isDirectNfdFibManagementRequested) {
registrator = static_cast<Registrator>(&Controller::start<RibRegisterCommand>);
unregistrator = static_cast<Registrator>(&Controller::start<RibUnregisterCommand>);
registerParameters.setFlags(flags);
}
else {
registrator = static_cast<Registrator>(&Controller::start<FibAddNextHopCommand>);
unregistrator = static_cast<Registrator>(&Controller::start<FibRemoveNextHopCommand>);
}
RegisteredPrefix::Unregistrator bindedUnregistrator =
std::bind(unregistrator, m_face.m_nfdController, unregisterParameters, _1, _2,
options);
// @todo get rid of "std::" after #2109
shared_ptr<RegisteredPrefix> prefixToRegister =
make_shared<RegisteredPrefix>(prefix, filter, bindedUnregistrator);
((*m_face.m_nfdController).*registrator)(registerParameters,
bind(&Impl::afterPrefixRegistered, this,
prefixToRegister, onSuccess),
bind(onFailure, prefixToRegister->getPrefix(), _2),
options);
return reinterpret_cast<const RegisteredPrefixId*>(prefixToRegister.get());
}
void
afterPrefixRegistered(const shared_ptr<RegisteredPrefix>& registeredPrefix,
const RegisterPrefixSuccessCallback& onSuccess)
{
m_registeredPrefixTable.push_back(registeredPrefix);
if (static_cast<bool>(registeredPrefix->getFilter())) {
// it was a combined operation
m_interestFilterTable.push_back(registeredPrefix->getFilter());
}
if (static_cast<bool>(onSuccess)) {
onSuccess(registeredPrefix->getPrefix());
}
}
void
asyncUnregisterPrefix(const RegisteredPrefixId* registeredPrefixId,
const UnregisterPrefixSuccessCallback& onSuccess,
const UnregisterPrefixFailureCallback& onFailure)
{
RegisteredPrefixTable::iterator i = std::find_if(m_registeredPrefixTable.begin(),
m_registeredPrefixTable.end(),
MatchRegisteredPrefixId(registeredPrefixId));
if (i != m_registeredPrefixTable.end())
{
const shared_ptr<InterestFilterRecord>& filter = (*i)->getFilter();
if (static_cast<bool>(filter))
{
// it was a combined operation
m_interestFilterTable.remove(filter);
}
(*i)->unregister(bind(&Impl::finalizeUnregisterPrefix, this, i, onSuccess),
bind(onFailure, _2));
}
else {
if (static_cast<bool>(onFailure)) {
onFailure("Unrecognized PrefixId");
}
}
// there cannot be two registered prefixes with the same id
}
void
finalizeUnregisterPrefix(RegisteredPrefixTable::iterator item,
const UnregisterPrefixSuccessCallback& onSuccess)
{
m_registeredPrefixTable.erase(item);
if (!m_pitTimeoutCheckTimerActive && m_registeredPrefixTable.empty())
{
m_face.m_transport->pause();
if (!m_ioServiceWork) {
m_processEventsTimeoutTimer->cancel();
}
}
if (static_cast<bool>(onSuccess)) {
onSuccess();
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////
void
checkPitExpire()
{
// Check for PIT entry timeouts.
time::steady_clock::TimePoint now = time::steady_clock::now();
PendingInterestTable::iterator i = m_pendingInterestTable.begin();
while (i != m_pendingInterestTable.end())
{
if ((*i)->isTimedOut(now))
{
// Save the PendingInterest and remove it from the PIT. Then call the callback.
shared_ptr<PendingInterest> pendingInterest = *i;
i = m_pendingInterestTable.erase(i);
pendingInterest->callTimeout();
}
else
++i;
}
if (!m_pendingInterestTable.empty()) {
m_pitTimeoutCheckTimerActive = true;
m_pitTimeoutCheckTimer->expires_from_now(time::milliseconds(100));
m_pitTimeoutCheckTimer->async_wait(bind(&Impl::checkPitExpire, this));
}
else {
m_pitTimeoutCheckTimerActive = false;
if (m_registeredPrefixTable.empty()) {
m_face.m_transport->pause();
if (!m_ioServiceWork) {
m_processEventsTimeoutTimer->cancel();
}
}
}
}
private:
Face& m_face;
PendingInterestTable m_pendingInterestTable;
InterestFilterTable m_interestFilterTable;
RegisteredPrefixTable m_registeredPrefixTable;
ConfigFile m_config;
shared_ptr<boost::asio::io_service::work> m_ioServiceWork; // if thread needs to be preserved
shared_ptr<monotonic_deadline_timer> m_pitTimeoutCheckTimer;
bool m_pitTimeoutCheckTimerActive;
shared_ptr<monotonic_deadline_timer> m_processEventsTimeoutTimer;
friend class Face;
};
} // namespace ndn
#endif // NDN_DETAIL_FACE_IMPL_HPP