ndn-cxx: NDN C++ Library 0.9.0-33-g832ea91d
Loading...
Searching...
No Matches
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
25#include "ndn-cxx/impl/face-impl.hpp"
31#include "ndn-cxx/util/time.hpp"
32
33namespace ndn {
34
35// NDN_LOG_INIT(ndn.Face) is declared in face-impl.hpp
36
37Face::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
47Face::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
55Face::Face(boost::asio::io_context& ioCtx)
56 : m_ioCtx(ioCtx)
57 , m_internalKeyChain(make_unique<KeyChain>())
58{
59 construct(nullptr, *m_internalKeyChain);
60}
61
62Face::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
70Face::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
77Face::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
84Face::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
90static shared_ptr<Transport>
91makeDefaultTransport()
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
129void
130Face::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
148Face::~Face() = default;
149
150PendingInterestHandle
151Face::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
169void
170Face::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
179size_t
180Face::getNPendingInterests() const
181{
182 return m_impl->m_pendingInterestTable.size();
183}
184
185void
186Face::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
195void
196Face::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
205RegisteredPrefixHandle
206Face::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
214Face::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
228Face::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
241RegisteredPrefixHandle
242Face::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
255void
256Face::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
287void
288Face::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
302template<typename NetPkt>
303static void
304extractLpLocalFields(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
310void
311Face::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
345PendingInterestHandle::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
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
363void
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
378void
379RegisteredPrefixHandle::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
391InterestFilterHandle::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:559
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:484
Handle for a registered prefix.
Definition face.hpp:511
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
::boost::chrono::milliseconds milliseconds
Definition time.hpp:52
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
STL namespace.