face.cpp
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2013-2024 Regents of the University of California.
4  *
5  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
6  *
7  * ndn-cxx library is free software: you can redistribute it and/or modify it under the
8  * terms of the GNU Lesser General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later version.
10  *
11  * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
12  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13  * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
14  *
15  * You should have received copies of the GNU General Public License and GNU Lesser
16  * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
17  * <http://www.gnu.org/licenses/>.
18  *
19  * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
20  */
21 
22 #include "ndn-cxx/face.hpp"
23 
24 #include "ndn-cxx/encoding/tlv.hpp"
25 #include "ndn-cxx/impl/face-impl.hpp"
26 #include "ndn-cxx/net/face-uri.hpp"
30 #include "ndn-cxx/util/scope.hpp"
31 #include "ndn-cxx/util/time.hpp"
32 
33 namespace ndn {
34 
35 // NDN_LOG_INIT(ndn.Face) is declared in face-impl.hpp
36 
37 Face::OversizedPacketError::OversizedPacketError(char pktType, const Name& name, size_t wireSize)
38  : Error((pktType == 'I' ? "Interest " : pktType == 'D' ? "Data " : "Nack ") +
39  name.toUri() + " encodes into " + std::to_string(wireSize) + " octets, "
40  "exceeding the implementation limit of " + std::to_string(MAX_NDN_PACKET_SIZE) + " octets")
41  , pktType(pktType)
42  , name(name)
43  , wireSize(wireSize)
44 {
45 }
46 
47 Face::Face(shared_ptr<Transport> transport)
48  : m_internalIoCtx(make_unique<boost::asio::io_context>())
49  , m_ioCtx(*m_internalIoCtx)
50  , m_internalKeyChain(make_unique<KeyChain>())
51 {
52  construct(std::move(transport), *m_internalKeyChain);
53 }
54 
55 Face::Face(boost::asio::io_context& ioCtx)
56  : m_ioCtx(ioCtx)
57  , m_internalKeyChain(make_unique<KeyChain>())
58 {
59  construct(nullptr, *m_internalKeyChain);
60 }
61 
62 Face::Face(const std::string& host, const std::string& port)
63  : m_internalIoCtx(make_unique<boost::asio::io_context>())
64  , m_ioCtx(*m_internalIoCtx)
65  , m_internalKeyChain(make_unique<KeyChain>())
66 {
67  construct(make_shared<TcpTransport>(host, port), *m_internalKeyChain);
68 }
69 
70 Face::Face(shared_ptr<Transport> transport, KeyChain& keyChain)
71  : m_internalIoCtx(make_unique<boost::asio::io_context>())
72  , m_ioCtx(*m_internalIoCtx)
73 {
74  construct(std::move(transport), keyChain);
75 }
76 
77 Face::Face(shared_ptr<Transport> transport, boost::asio::io_context& ioCtx)
78  : m_ioCtx(ioCtx)
79  , m_internalKeyChain(make_unique<KeyChain>())
80 {
81  construct(std::move(transport), *m_internalKeyChain);
82 }
83 
84 Face::Face(shared_ptr<Transport> transport, boost::asio::io_context& ioCtx, KeyChain& keyChain)
85  : m_ioCtx(ioCtx)
86 {
87  construct(std::move(transport), keyChain);
88 }
89 
90 static shared_ptr<Transport>
91 makeDefaultTransport()
92 {
93  std::string transportUri;
94 
95  const char* transportEnviron = getenv("NDN_CLIENT_TRANSPORT");
96  if (transportEnviron != nullptr) {
97  transportUri = transportEnviron;
98  }
99  else {
100  ConfigFile config;
101  transportUri = config.getParsedConfiguration().get<std::string>("transport", "");
102  }
103 
104  if (transportUri.empty()) {
105  // transport not specified, use default Unix transport.
106  return UnixTransport::create("");
107  }
108 
109  try {
110  std::string protocol = FaceUri(transportUri).getScheme();
111  if (protocol == "unix") {
112  return UnixTransport::create(transportUri);
113  }
114  else if (protocol == "tcp" || protocol == "tcp4" || protocol == "tcp6") {
115  return TcpTransport::create(transportUri);
116  }
117  else {
118  NDN_THROW(ConfigFile::Error("Unsupported transport protocol \"" + protocol + "\""));
119  }
120  }
121  catch (const Transport::Error& e) {
122  NDN_THROW_NESTED(ConfigFile::Error("Failed to create transport: "s + e.what()));
123  }
124  catch (const FaceUri::Error& e) {
125  NDN_THROW_NESTED(ConfigFile::Error("Failed to create transport: "s + e.what()));
126  }
127 }
128 
129 void
130 Face::construct(shared_ptr<Transport> transport, KeyChain& keyChain)
131 {
132  BOOST_ASSERT(m_impl == nullptr);
133  m_impl = make_shared<Impl>(*this, keyChain);
134 
135  if (transport == nullptr) {
136  transport = makeDefaultTransport();
137  BOOST_ASSERT(transport != nullptr);
138  }
139  m_transport = std::move(transport);
140 
141  boost::asio::post(m_ioCtx, [w = m_impl->weak_from_this()] {
142  if (auto impl = w.lock(); impl != nullptr) {
143  impl->ensureConnected(false);
144  }
145  });
146 }
147 
148 Face::~Face() = default;
149 
150 PendingInterestHandle
151 Face::expressInterest(const Interest& interest,
152  const DataCallback& afterSatisfied,
153  const NackCallback& afterNacked,
154  const TimeoutCallback& afterTimeout)
155 {
156  auto id = m_impl->m_pendingInterestTable.allocateId();
157  auto interest2 = make_shared<Interest>(interest);
158  interest2->getNonce();
159 
160  boost::asio::post(m_ioCtx, [=, w = m_impl->weak_from_this()] {
161  if (auto impl = w.lock(); impl != nullptr) {
162  impl->expressInterest(id, interest2, afterSatisfied, afterNacked, afterTimeout);
163  }
164  });
165 
166  return PendingInterestHandle(m_impl, id);
167 }
168 
169 void
170 Face::removeAllPendingInterests()
171 {
172  boost::asio::post(m_ioCtx, [w = m_impl->weak_from_this()] {
173  if (auto impl = w.lock(); impl != nullptr) {
174  impl->removeAllPendingInterests();
175  }
176  });
177 }
178 
179 size_t
180 Face::getNPendingInterests() const
181 {
182  return m_impl->m_pendingInterestTable.size();
183 }
184 
185 void
186 Face::put(const Data& data)
187 {
188  boost::asio::post(m_ioCtx, [data, w = m_impl->weak_from_this()] {
189  if (auto impl = w.lock(); impl != nullptr) {
190  impl->putData(data);
191  }
192  });
193 }
194 
195 void
196 Face::put(const lp::Nack& nack)
197 {
198  boost::asio::post(m_ioCtx, [nack, w = m_impl->weak_from_this()] {
199  if (auto impl = w.lock(); impl != nullptr) {
200  impl->putNack(nack);
201  }
202  });
203 }
204 
205 RegisteredPrefixHandle
206 Face::setInterestFilter(const InterestFilter& filter, const InterestCallback& onInterest,
207  const RegisterPrefixFailureCallback& onFailure,
208  const security::SigningInfo& signingInfo, uint64_t flags)
209 {
210  return setInterestFilter(filter, onInterest, nullptr, onFailure, signingInfo, flags);
211 }
212 
214 Face::setInterestFilter(const InterestFilter& filter, const InterestCallback& onInterest,
215  const RegisterPrefixSuccessCallback& onSuccess,
216  const RegisterPrefixFailureCallback& onFailure,
217  const security::SigningInfo& signingInfo, uint64_t flags)
218 {
219  nfd::CommandOptions options;
220  options.setSigningInfo(signingInfo);
221 
222  auto id = m_impl->registerPrefix(filter.getPrefix(), onSuccess, onFailure, flags, options,
223  filter, onInterest);
224  return RegisteredPrefixHandle(m_impl, id);
225 }
226 
228 Face::setInterestFilter(const InterestFilter& filter, const InterestCallback& onInterest)
229 {
230  auto id = m_impl->m_interestFilterTable.allocateId();
231 
232  boost::asio::post(m_ioCtx, [=, w = m_impl->weak_from_this()] {
233  if (auto impl = w.lock(); impl != nullptr) {
234  impl->setInterestFilter(id, filter, onInterest);
235  }
236  });
237 
238  return InterestFilterHandle(m_impl, id);
239 }
240 
241 RegisteredPrefixHandle
242 Face::registerPrefix(const Name& prefix,
243  const RegisterPrefixSuccessCallback& onSuccess,
244  const RegisterPrefixFailureCallback& onFailure,
245  const security::SigningInfo& signingInfo,
246  uint64_t flags)
247 {
248  nfd::CommandOptions options;
249  options.setSigningInfo(signingInfo);
250 
251  auto id = m_impl->registerPrefix(prefix, onSuccess, onFailure, flags, options, std::nullopt, nullptr);
252  return RegisteredPrefixHandle(m_impl, id);
253 }
254 
255 void
256 Face::doProcessEvents(time::milliseconds timeout, bool keepRunning)
257 {
258  if (m_ioCtx.stopped()) {
259  m_ioCtx.restart(); // ensure that run()/poll() will do some work
260  }
261 
262  auto onThrow = make_scope_fail([this] { m_impl->shutdown(); });
263 
264  if (timeout < 0_ms) {
265  // do not block if timeout is negative, but process pending events
266  m_ioCtx.poll();
267  return;
268  }
269 
270  if (timeout > 0_ms) {
271  // TODO: consider using m_ioCtx.run_for(timeout) in this case
272  m_impl->m_processEventsTimeoutEvent = m_impl->m_scheduler.schedule(timeout,
273  [&io = m_ioCtx, &work = m_impl->m_workGuard] {
274  io.stop();
275  work.reset();
276  });
277  }
278 
279  if (keepRunning) {
280  // m_workGuard ensures that m_ioCtx keeps running even when it runs out of work (events)
281  m_impl->m_workGuard = make_unique<Impl::IoContextWorkGuard>(m_ioCtx.get_executor());
282  }
283 
284  m_ioCtx.run();
285 }
286 
287 void
288 Face::shutdown()
289 {
290  boost::asio::post(m_ioCtx, [this, w = m_impl->weak_from_this()] {
291  if (auto impl = w.lock(); impl != nullptr) {
292  impl->shutdown();
293  if (m_transport->getState() != Transport::State::CLOSED)
294  m_transport->close();
295  }
296  });
297 }
298 
302 template<typename NetPkt>
303 static void
304 extractLpLocalFields(NetPkt& netPacket, const lp::Packet& lpPacket)
305 {
306  addTagFromField<lp::IncomingFaceIdTag, lp::IncomingFaceIdField>(netPacket, lpPacket);
307  addTagFromField<lp::CongestionMarkTag, lp::CongestionMarkField>(netPacket, lpPacket);
308 }
309 
310 void
311 Face::onReceiveElement(const Block& blockFromDaemon)
312 {
313  lp::Packet lpPacket(blockFromDaemon); // bare Interest/Data is a valid lp::Packet,
314  // no need to distinguish
315 
316  auto frag = lpPacket.get<lp::FragmentField>();
317  Block netPacket({frag.first, frag.second});
318  switch (netPacket.type()) {
319  case tlv::Interest: {
320  auto interest = make_shared<Interest>(netPacket);
321  if (lpPacket.has<lp::NackField>()) {
322  auto nack = make_shared<lp::Nack>(std::move(*interest));
323  nack->setHeader(lpPacket.get<lp::NackField>());
324  extractLpLocalFields(*nack, lpPacket);
325  NDN_LOG_DEBUG(">N " << nack->getInterest() << '~' << nack->getHeader().getReason());
326  m_impl->nackPendingInterests(*nack);
327  }
328  else {
329  extractLpLocalFields(*interest, lpPacket);
330  NDN_LOG_DEBUG(">I " << *interest);
331  m_impl->processIncomingInterest(std::move(interest));
332  }
333  break;
334  }
335  case tlv::Data: {
336  auto data = make_shared<Data>(netPacket);
337  extractLpLocalFields(*data, lpPacket);
338  NDN_LOG_DEBUG(">D " << data->getName());
339  m_impl->satisfyPendingInterests(*data);
340  break;
341  }
342  }
343 }
344 
345 PendingInterestHandle::PendingInterestHandle(weak_ptr<Face::Impl> weakImpl, detail::RecordId id)
346  : CancelHandle([w = std::move(weakImpl), id] {
347  if (auto impl = w.lock(); impl != nullptr) {
348  impl->asyncRemovePendingInterest(id);
349  }
350  })
351 {
352 }
353 
354 RegisteredPrefixHandle::RegisteredPrefixHandle(weak_ptr<Face::Impl> weakImpl, detail::RecordId id)
355  : CancelHandle([=] { unregister(weakImpl, id, nullptr, nullptr); })
356  , m_weakImpl(std::move(weakImpl))
357  , m_id(id)
358 {
359  // The lambda passed to CancelHandle constructor cannot call the non-static unregister(),
360  // because the base class destructor cannot access the member fields of this subclass.
361 }
362 
363 void
365  const UnregisterPrefixFailureCallback& onFailure)
366 {
367  if (m_id == 0) {
368  if (onFailure) {
369  onFailure("RegisteredPrefixHandle is empty");
370  }
371  return;
372  }
373 
374  unregister(m_weakImpl, m_id, onSuccess, onFailure);
375  *this = {};
376 }
377 
378 void
379 RegisteredPrefixHandle::unregister(const weak_ptr<Face::Impl>& weakImpl, detail::RecordId id,
380  const UnregisterPrefixSuccessCallback& onSuccess,
381  const UnregisterPrefixFailureCallback& onFailure)
382 {
383  if (auto impl = weakImpl.lock(); impl != nullptr) {
384  impl->asyncUnregisterPrefix(id, onSuccess, onFailure);
385  }
386  else if (onFailure) {
387  onFailure("Face already closed");
388  }
389 }
390 
391 InterestFilterHandle::InterestFilterHandle(weak_ptr<Face::Impl> weakImpl, detail::RecordId id)
392  : CancelHandle([w = std::move(weakImpl), id] {
393  if (auto impl = w.lock(); impl != nullptr) {
394  impl->asyncUnsetInterestFilter(id);
395  }
396  })
397 {
398 }
399 
400 } // namespace ndn
Represents a Data packet.
Definition: data.hpp:39
OversizedPacketError(char pktType, const Name &name, size_t wireSize)
Constructor.
Definition: face.cpp:37
Face(shared_ptr< Transport > transport=nullptr)
Create Face using the given Transport (or default transport if omitted).
Definition: face.cpp:47
Handle for a registered Interest filter.
Definition: face.hpp:569
InterestFilterHandle() noexcept=default
Declares the set of Interests a producer can serve.
const Name & getPrefix() const
Represents an Interest packet.
Definition: interest.hpp:50
Represents an absolute name.
Definition: name.hpp:45
Handle for a pending Interest.
Definition: face.hpp:494
Handle for a registered prefix.
Definition: face.hpp:521
void unregister(const UnregisterPrefixSuccessCallback &onSuccess=nullptr, const UnregisterPrefixFailureCallback &onFailure=nullptr)
Unregister the prefix.
Definition: face.cpp:364
RegisteredPrefixHandle() noexcept=default
static shared_ptr< TcpTransport > create(const std::string &uri)
Create transport with parameters defined in URI.
static shared_ptr< UnixTransport > create(const std::string &uri)
Create transport with parameters defined in URI.
Represents a Network Nack.
Definition: nack.hpp:39
Contains options for ControlCommand execution.
CommandOptions & setSigningInfo(security::SigningInfo signingInfo)
Sets the signing parameters.
The main interface for signing key management.
Definition: key-chain.hpp:87
Signing parameters passed to KeyChain.
#define NDN_THROW_NESTED(e)
Definition: exception.hpp:65
#define NDN_THROW(e)
Definition: exception.hpp:56
#define NDN_LOG_DEBUG(expression)
Log at DEBUG level.
Definition: logger.hpp:260
uint64_t RecordId
Definition: face.hpp:44
std::string to_string(const errinfo_stacktrace &x)
Definition: exception.cpp:30
FieldDecl< field_location_tags::Fragment, std::pair< Buffer::const_iterator, Buffer::const_iterator >, tlv::Fragment > FragmentField
Declare the Fragment field.
Definition: fields.hpp:47
FieldDecl< field_location_tags::Header, NackHeader, tlv::Nack > NackField
Definition: fields.hpp:73
::boost::chrono::milliseconds milliseconds
Definition: time.hpp:52
@ Data
Definition: tlv.hpp:69
@ Interest
Definition: tlv.hpp:68
Definition: data.cpp:25
std::function< void(const Name &)> RegisterPrefixSuccessCallback
Callback invoked when registerPrefix or setInterestFilter command succeeds.
Definition: face.hpp:70
std::function< void(const Interest &)> TimeoutCallback
Callback invoked when an expressed Interest times out.
Definition: face.hpp:60
std::function< void(const std::string &)> UnregisterPrefixFailureCallback
Callback invoked when unregistering a prefix fails.
Definition: face.hpp:85
std::function< void(const Interest &, const lp::Nack &)> NackCallback
Callback invoked when a Nack is received in response to an expressed Interest.
Definition: face.hpp:55
std::function< void(const Interest &, const Data &)> DataCallback
Callback invoked when an expressed Interest is satisfied by a Data packet.
Definition: face.hpp:50
std::function< void()> UnregisterPrefixSuccessCallback
Callback invoked when unregistering a prefix succeeds.
Definition: face.hpp:80
std::function< void(const Name &, const std::string &)> RegisterPrefixFailureCallback
Callback invoked when registerPrefix or setInterestFilter command fails.
Definition: face.hpp:75
std::function< void(const InterestFilter &, const Interest &)> InterestCallback
Callback invoked when an incoming Interest matches the specified InterestFilter.
Definition: face.hpp:65
constexpr size_t MAX_NDN_PACKET_SIZE
Practical size limit of a network-layer packet.
Definition: tlv.hpp:41