All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
dispatcher.cpp
Go to the documentation of this file.
1/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/*
3 * Copyright (c) 2013-2025 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
23#include "ndn-cxx/lp/tags.hpp"
25
26#include <algorithm>
27
28namespace ndn::mgmt {
29
31
34{
35 return [] (const Name& prefix,
36 const Interest& interest,
37 const ControlParametersBase* params,
38 const AcceptContinuation& accept,
39 const RejectContinuation& reject) {
40 accept("");
41 };
42}
43
44Dispatcher::Dispatcher(Face& face, KeyChain& keyChain, const security::SigningInfo& signingInfo,
45 size_t imsCapacity)
46 : m_face(face)
47 , m_keyChain(keyChain)
48 , m_signingInfo(signingInfo)
49 , m_storage(m_face.getIoContext(), imsCapacity)
50{
51}
52
53void
54Dispatcher::addTopPrefix(const Name& prefix, bool wantRegister,
55 const security::SigningInfo& signingInfo)
56{
57 bool hasOverlap = std::any_of(m_topLevelPrefixes.begin(), m_topLevelPrefixes.end(), [&] (const auto& x) {
58 return x.first.isPrefixOf(prefix) || prefix.isPrefixOf(x.first);
59 });
60 if (hasOverlap) {
61 NDN_THROW(std::out_of_range("top-level prefix '" + prefix.toUri() + "' overlaps with another"));
62 }
63
64 TopPrefixEntry& topPrefixEntry = m_topLevelPrefixes[prefix];
65
66 if (wantRegister) {
67 topPrefixEntry.registeredPrefix = m_face.registerPrefix(prefix,
68 nullptr,
69 [] (const Name&, const std::string& reason) {
70 NDN_THROW(std::runtime_error("prefix registration failed: " + reason));
71 },
72 signingInfo);
73 }
74
75 for (const auto& entry : m_handlers) {
76 Name fullPrefix = Name(prefix).append(entry.first);
77 auto filterHdl = m_face.setInterestFilter(fullPrefix,
78 [=, cb = entry.second] (const auto&, const auto& interest) {
79 cb(prefix, interest);
80 });
81 topPrefixEntry.interestFilters.emplace_back(std::move(filterHdl));
82 }
83}
84
85void
87{
88 m_topLevelPrefixes.erase(prefix);
89}
90
91void
92Dispatcher::checkPrefix(const PartialName& relPrefix) const
93{
94 if (!m_topLevelPrefixes.empty()) {
95 NDN_THROW(std::domain_error("one or more top-level prefixes have already been added"));
96 }
97
98 bool hasOverlap = std::any_of(m_handlers.begin(), m_handlers.end(), [&] (const auto& entry) {
99 return entry.first.isPrefixOf(relPrefix) || relPrefix.isPrefixOf(entry.first);
100 });
101 if (hasOverlap) {
102 NDN_THROW(std::out_of_range("'" + relPrefix.toUri() + "' overlaps with another handler"));
103 }
104}
105
106void
107Dispatcher::afterAuthorizationRejected(RejectReply act, const Interest& interest)
108{
109 if (act == RejectReply::STATUS403) {
110 sendControlResponse(ControlResponse(403, "authorization rejected"), interest);
111 }
112}
113
114void
115Dispatcher::queryStorage(const Name& prefix, const Interest& interest,
116 const InterestHandler& missContinuation)
117{
118 auto data = m_storage.find(interest);
119 if (data == nullptr) {
120 // invoke missContinuation to process this Interest if the query fails.
121 if (missContinuation)
122 missContinuation(prefix, interest);
123 }
124 else {
125 // send the fetched data through face if query succeeds.
126 sendOnFace(*data);
127 }
128}
129
130void
131Dispatcher::sendData(const Name& dataName, const Block& content, const MetaInfo& metaInfo,
132 SendDestination option)
133{
134 auto data = make_shared<Data>(dataName);
135 data->setContent(content).setMetaInfo(metaInfo).setFreshnessPeriod(1_s);
136
137 m_keyChain.sign(*data, m_signingInfo);
138
139 if (option == SendDestination::IMS || option == SendDestination::FACE_AND_IMS) {
140 lp::CachePolicy policy;
141 policy.setPolicy(lp::CachePolicyType::NO_CACHE);
142 data->setTag(make_shared<lp::CachePolicyTag>(policy));
143 m_storage.insert(*data, 1_s);
144 }
145
146 if (option == SendDestination::FACE || option == SendDestination::FACE_AND_IMS) {
147 sendOnFace(*data);
148 }
149}
150
151void
152Dispatcher::sendOnFace(const Data& data)
153{
154 try {
155 m_face.put(data);
156 }
157 catch (const Face::Error& e) {
158 NDN_LOG_ERROR("sendOnFace(" << data.getName() << "): " << e.what());
159 }
160}
161
162void
163Dispatcher::processCommand(const Name& prefix,
164 const Interest& interest,
165 const ParametersParser& parse,
166 const Authorization& authorize,
167 ValidateParameters validate,
168 ControlCommandHandler handler)
169{
170 ControlParametersPtr parameters;
171 try {
172 parameters = parse(prefix, interest);
173 }
174 catch (const std::exception& e) {
175 NDN_LOG_DEBUG("malformed command " << interest.getName() << ": " << e.what());
176 return;
177 }
178
179 AcceptContinuation accept = [=, v = std::move(validate), h = std::move(handler)] (const auto&) {
180 processAuthorizedCommand(prefix, interest, parameters, v, h);
181 };
182 RejectContinuation reject = [=] (RejectReply reply) {
183 afterAuthorizationRejected(reply, interest);
184 };
185 authorize(prefix, interest, parameters.get(), accept, reject);
186}
187
188void
189Dispatcher::processAuthorizedCommand(const Name& prefix,
190 const Interest& interest,
191 const ControlParametersPtr& parameters,
192 const ValidateParameters& validate,
193 const ControlCommandHandler& handler)
194{
195 bool ok = false;
196 try {
197 ok = validate(*parameters);
198 }
199 catch (const std::exception& e) {
200 NDN_LOG_DEBUG("invalid parameters for command " << interest.getName() << ": " << e.what());
201 }
202
203 if (ok) {
204 handler(prefix, interest, *parameters, [this, interest] (const auto& resp) {
205 sendControlResponse(resp, interest);
206 });
207 }
208 else {
209 sendControlResponse(ControlResponse(400, "failed in validating parameters"), interest);
210 }
211}
212
213void
214Dispatcher::sendControlResponse(const ControlResponse& resp, const Interest& interest, bool isNack)
215{
216 MetaInfo metaInfo;
217 if (isNack) {
218 metaInfo.setType(tlv::ContentType_Nack);
219 }
220
221 // control response is always sent out through the face
222 sendData(interest.getName(), resp.wireEncode(), metaInfo, SendDestination::FACE);
223}
224
225void
227 Authorization authorize,
229{
230 checkPrefix(relPrefix);
231
232 // follow the general path if storage is a miss
233 InterestHandler afterMiss = [this,
234 authorizer = std::move(authorize),
235 handler = std::move(handle)] (const auto& prefix, const auto& interest) {
236 processStatusDatasetInterest(prefix, interest, authorizer, std::move(handler));
237 };
238
239 m_handlers[relPrefix] = [this, miss = std::move(afterMiss)] (auto&&... args) {
240 queryStorage(std::forward<decltype(args)>(args)..., miss);
241 };
242}
243
244void
245Dispatcher::processStatusDatasetInterest(const Name& prefix,
246 const Interest& interest,
247 const Authorization& authorize,
248 StatusDatasetHandler handler)
249{
250 const Name& interestName = interest.getName();
251 bool endsWithVersionOrSegment = interestName.size() >= 1 &&
252 (interestName[-1].isVersion() || interestName[-1].isSegment());
253 if (endsWithVersionOrSegment) {
254 return;
255 }
256
257 AcceptContinuation accept = [=, h = std::move(handler)] (const auto&) {
258 processAuthorizedStatusDatasetInterest(prefix, interest, h);
259 };
260 RejectContinuation reject = [=] (RejectReply reply) {
261 afterAuthorizationRejected(reply, interest);
262 };
263 authorize(prefix, interest, nullptr, accept, reject);
264}
265
266void
267Dispatcher::processAuthorizedStatusDatasetInterest(const Name& prefix,
268 const Interest& interest,
269 const StatusDatasetHandler& handler)
270{
271 StatusDatasetContext context(interest,
272 [this] (auto&&... args) {
273 sendStatusDatasetSegment(std::forward<decltype(args)>(args)...);
274 },
275 [this, interest] (auto&&... args) {
276 sendControlResponse(std::forward<decltype(args)>(args)..., interest, true);
277 });
278 handler(prefix, interest, context);
279}
280
281void
282Dispatcher::sendStatusDatasetSegment(const Name& dataName, const Block& content, bool isFinalBlock)
283{
284 // the first segment will be sent to both places (the face and the in-memory storage)
285 // other segments will be inserted to the in-memory storage only
286 auto destination = SendDestination::IMS;
287 if (dataName[-1].toSegment() == 0) {
288 destination = SendDestination::FACE_AND_IMS;
289 }
290
291 MetaInfo metaInfo;
292 if (isFinalBlock) {
293 metaInfo.setFinalBlock(dataName[-1]);
294 }
295
296 sendData(dataName, content, metaInfo, destination);
297}
298
301{
302 checkPrefix(relPrefix);
303
304 // register a handler for the subscriber of this notification stream
305 // keep silent if Interest does not match a stored notification
306 m_handlers[relPrefix] = [this] (auto&&... args) {
307 queryStorage(std::forward<decltype(args)>(args)..., nullptr);
308 };
309 m_streams[relPrefix] = 0;
310
311 return [=] (const Block& b) { postNotification(b, relPrefix); };
312}
313
314void
315Dispatcher::postNotification(const Block& notification, const PartialName& relPrefix)
316{
317 if (m_topLevelPrefixes.size() != 1) {
318 NDN_LOG_WARN("postNotification: no top-level prefix or too many top-level prefixes");
319 return;
320 }
321
322 Name streamName(m_topLevelPrefixes.begin()->first);
323 streamName.append(relPrefix);
324 streamName.appendSequenceNumber(m_streams[streamName]++);
325
326 // notification is sent out via the face after inserting into the in-memory storage,
327 // because a request may be pending in the PIT
328 sendData(streamName, notification, {}, SendDestination::FACE_AND_IMS);
329}
330
331} // namespace ndn::mgmt
Represents a TLV element of the NDN packet format.
Definition block.hpp:45
Provide a communication channel with local or remote NDN forwarder.
Definition face.hpp:91
RegisteredPrefixHandle registerPrefix(const Name &prefix, const RegisterPrefixSuccessCallback &onSuccess, const RegisterPrefixFailureCallback &onFailure, const security::SigningInfo &signingInfo=security::SigningInfo(), uint64_t flags=nfd::ROUTE_FLAG_CHILD_INHERIT)
Register prefix with the connected NDN forwarder.
Definition face.cpp:242
RegisteredPrefixHandle setInterestFilter(const InterestFilter &filter, const InterestCallback &onInterest, const RegisterPrefixFailureCallback &onFailure, const security::SigningInfo &signingInfo=security::SigningInfo(), uint64_t flags=nfd::ROUTE_FLAG_CHILD_INHERIT)
Set InterestFilter to dispatch incoming matching interest to onInterest callback and register the fil...
Definition face.cpp:206
void put(const Data &data)
Publish a Data packet.
Definition face.cpp:186
void insert(const Data &data, const time::milliseconds &mustBeFreshProcessingWindow=INFINITE_WINDOW)
Inserts a Data packet.
shared_ptr< const Data > find(const Interest &interest)
Finds the best match Data for an Interest.
Represents an Interest packet.
Definition interest.hpp:50
const Name & getName() const noexcept
Get the Interest name.
Definition interest.hpp:179
Represents an absolute name.
Definition name.hpp:45
Name & append(const Component &component)
Append a name component.
Definition name.hpp:308
size_t size() const noexcept
Returns the number of components.
Definition name.hpp:180
void toUri(std::ostream &os, name::UriFormat format=name::UriFormat::DEFAULT) const
Write URI representation of the name to the output stream.
Definition name.cpp:324
Base class for a struct that contains the parameters for a ControlCommand.
Implements a request dispatcher on server side of NFD Management protocol.
PostNotification addNotificationStream(const PartialName &relPrefix)
Register a NotificationStream.
Dispatcher(Face &face, KeyChain &keyChain, const security::SigningInfo &signingInfo=security::SigningInfo(), size_t imsCapacity=256)
Constructor.
void addStatusDataset(const PartialName &relPrefix, Authorization authorize, StatusDatasetHandler handle)
Register a StatusDataset or a prefix under which StatusDatasets can be requested.
void addTopPrefix(const Name &prefix, bool wantRegister=true, const security::SigningInfo &signingInfo=security::SigningInfo())
Add a top-level prefix.
void removeTopPrefix(const Name &prefix)
Remove a top-level prefix.
The main interface for signing key management.
Definition key-chain.hpp:87
void sign(Data &data, const SigningInfo &params=SigningInfo())
Sign a Data packet according to the supplied signing information.
Signing parameters passed to KeyChain.
#define NDN_THROW(e)
Definition exception.hpp:56
#define NDN_LOG_DEBUG(expression)
Log at DEBUG level.
Definition logger.hpp:260
#define NDN_LOG_WARN(expression)
Log at WARN level.
Definition logger.hpp:270
#define NDN_LOG_ERROR(expression)
Log at ERROR level.
Definition logger.hpp:275
#define NDN_LOG_INIT(name)
Define a non-member log module.
Definition logger.hpp:169
std::function< void(const std::string &requester)> AcceptContinuation
A function to be called if authorization is successful.
std::function< void(const Name &prefix, const Interest &interest, const ControlParametersBase &params, const CommandContinuation &done)> ControlCommandHandler
A function to handle an authorized ControlCommand.
std::function< void(const Block &notification)> PostNotification
A function to post a notification.
std::function< void(RejectReply)> RejectContinuation
A function to be called if authorization is rejected.
RejectReply
Indicates how to reply in case authorization is rejected.
@ STATUS403
Reply with a ControlResponse where StatusCode is 403.
std::function< bool(ControlParametersBase &params)> ValidateParameters
A function to validate and normalize the incoming request parameters.
std::function< void(const Name &prefix, const Interest &interest, const ControlParametersBase *params, const AcceptContinuation &accept, const RejectContinuation &reject)> Authorization
A function that performs authorization.
Authorization makeAcceptAllAuthorization()
Return an Authorization that accepts all Interests, with empty string as requester.
std::function< void(const Name &prefix, const Interest &interest, StatusDatasetContext &context)> StatusDatasetHandler
A function to handle a StatusDataset request.
mgmt::ControlResponse ControlResponse
@ MetaInfo
Definition tlv.hpp:92
@ ContentType_Nack
application-level nack
Definition tlv.hpp:148
Definition data.cpp:25