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-2023 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"
24 #include "ndn-cxx/util/logger.hpp"
25 
26 #include <algorithm>
27 
28 namespace ndn::mgmt {
29 
31 
34 {
35  return [] (const Name& prefix,
36  const Interest& interest,
37  const ControlParameters* params,
38  const AcceptContinuation& accept,
39  const RejectContinuation& reject) {
40  accept("");
41  };
42 }
43 
45  const security::SigningInfo& signingInfo,
46  size_t imsCapacity)
47  : m_face(face)
48  , m_keyChain(keyChain)
49  , m_signingInfo(signingInfo)
50  , m_storage(m_face.getIoContext(), imsCapacity)
51 {
52 }
53 
54 Dispatcher::~Dispatcher() = default;
55 
56 void
57 Dispatcher::addTopPrefix(const Name& prefix, bool wantRegister,
58  const security::SigningInfo& signingInfo)
59 {
60  bool hasOverlap = std::any_of(m_topLevelPrefixes.begin(), m_topLevelPrefixes.end(),
61  [&prefix] (const auto& x) {
62  return x.first.isPrefixOf(prefix) || prefix.isPrefixOf(x.first);
63  });
64  if (hasOverlap) {
65  NDN_THROW(std::out_of_range("top-level prefix overlaps"));
66  }
67 
68  TopPrefixEntry& topPrefixEntry = m_topLevelPrefixes[prefix];
69 
70  if (wantRegister) {
71  topPrefixEntry.registeredPrefix = m_face.registerPrefix(prefix,
72  nullptr,
73  [] (const Name&, const std::string& reason) {
74  NDN_THROW(std::runtime_error("prefix registration failed: " + reason));
75  },
76  signingInfo);
77  }
78 
79  for (const auto& entry : m_handlers) {
80  Name fullPrefix = Name(prefix).append(entry.first);
81  auto filterHdl = m_face.setInterestFilter(fullPrefix,
82  [=, cb = entry.second] (const auto&, const auto& interest) {
83  cb(prefix, interest);
84  });
85  topPrefixEntry.interestFilters.emplace_back(std::move(filterHdl));
86  }
87 }
88 
89 void
91 {
92  m_topLevelPrefixes.erase(prefix);
93 }
94 
95 bool
96 Dispatcher::isOverlappedWithOthers(const PartialName& relPrefix) const
97 {
98  bool hasOverlapWithHandlers =
99  std::any_of(m_handlers.begin(), m_handlers.end(),
100  [&] (const auto& entry) {
101  return entry.first.isPrefixOf(relPrefix) || relPrefix.isPrefixOf(entry.first);
102  });
103  bool hasOverlapWithStreams =
104  std::any_of(m_streams.begin(), m_streams.end(),
105  [&] (const auto& entry) {
106  return entry.first.isPrefixOf(relPrefix) || relPrefix.isPrefixOf(entry.first);
107  });
108 
109  return hasOverlapWithHandlers || hasOverlapWithStreams;
110 }
111 
112 void
113 Dispatcher::afterAuthorizationRejected(RejectReply act, const Interest& interest)
114 {
115  if (act == RejectReply::STATUS403) {
116  sendControlResponse(ControlResponse(403, "authorization rejected"), interest);
117  }
118 }
119 
120 void
121 Dispatcher::queryStorage(const Name& prefix, const Interest& interest,
122  const InterestHandler& missContinuation)
123 {
124  auto data = m_storage.find(interest);
125  if (data == nullptr) {
126  // invoke missContinuation to process this Interest if the query fails.
127  if (missContinuation)
128  missContinuation(prefix, interest);
129  }
130  else {
131  // send the fetched data through face if query succeeds.
132  sendOnFace(*data);
133  }
134 }
135 
136 void
137 Dispatcher::sendData(const Name& dataName, const Block& content, const MetaInfo& metaInfo,
138  SendDestination option)
139 {
140  auto data = make_shared<Data>(dataName);
141  data->setContent(content).setMetaInfo(metaInfo).setFreshnessPeriod(1_s);
142 
143  m_keyChain.sign(*data, m_signingInfo);
144 
145  if (option == SendDestination::IMS || option == SendDestination::FACE_AND_IMS) {
146  lp::CachePolicy policy;
147  policy.setPolicy(lp::CachePolicyType::NO_CACHE);
148  data->setTag(make_shared<lp::CachePolicyTag>(policy));
149  m_storage.insert(*data, 1_s);
150  }
151 
152  if (option == SendDestination::FACE || option == SendDestination::FACE_AND_IMS) {
153  sendOnFace(*data);
154  }
155 }
156 
157 void
158 Dispatcher::sendOnFace(const Data& data)
159 {
160  try {
161  m_face.put(data);
162  }
163  catch (const Face::Error& e) {
164  NDN_LOG_ERROR("sendOnFace(" << data.getName() << "): " << e.what());
165  }
166 }
167 
168 void
169 Dispatcher::processControlCommandInterest(const Name& prefix,
170  const Name& relPrefix,
171  const Interest& interest,
172  const ControlParametersParser& parser,
173  const Authorization& authorization,
174  const AuthorizationAcceptedCallback& accepted,
175  const AuthorizationRejectedCallback& rejected)
176 {
177  // /<prefix>/<relPrefix>/<parameters>
178  size_t parametersLoc = prefix.size() + relPrefix.size();
179  const name::Component& pc = interest.getName().get(parametersLoc);
180 
181  shared_ptr<ControlParameters> parameters;
182  try {
183  parameters = parser(pc);
184  }
185  catch (const tlv::Error&) {
186  return;
187  }
188 
189  AcceptContinuation accept = [=] (const auto& req) { accepted(req, prefix, interest, parameters); };
190  RejectContinuation reject = [=] (RejectReply reply) { rejected(reply, interest); };
191  authorization(prefix, interest, parameters.get(), accept, reject);
192 }
193 
194 void
195 Dispatcher::processAuthorizedControlCommandInterest(const std::string& requester,
196  const Name& prefix,
197  const Interest& interest,
198  const shared_ptr<ControlParameters>& parameters,
199  const ValidateParameters& validateParams,
200  const ControlCommandHandler& handler)
201 {
202  if (validateParams(*parameters)) {
203  handler(prefix, interest, *parameters,
204  [=] (const auto& resp) { sendControlResponse(resp, interest); });
205  }
206  else {
207  sendControlResponse(ControlResponse(400, "failed in validating parameters"), interest);
208  }
209 }
210 
211 void
212 Dispatcher::sendControlResponse(const ControlResponse& resp, const Interest& interest, bool isNack)
213 {
214  MetaInfo metaInfo;
215  if (isNack) {
216  metaInfo.setType(tlv::ContentType_Nack);
217  }
218 
219  // control response is always sent out through the face
220  sendData(interest.getName(), resp.wireEncode(), metaInfo, SendDestination::FACE);
221 }
222 
223 void
225  Authorization auth,
226  StatusDatasetHandler handler)
227 {
228  if (!m_topLevelPrefixes.empty()) {
229  NDN_THROW(std::domain_error("one or more top-level prefix has been added"));
230  }
231 
232  if (isOverlappedWithOthers(relPrefix)) {
233  NDN_THROW(std::out_of_range("status dataset name overlaps"));
234  }
235 
236  AuthorizationAcceptedCallback accept =
237  [this, handler = std::move(handler)] (auto&&, const auto& prefix, const auto& interest, auto&&) {
238  processAuthorizedStatusDatasetInterest(prefix, interest, handler);
239  };
240  AuthorizationRejectedCallback reject =
241  [this] (auto&&... args) {
242  afterAuthorizationRejected(std::forward<decltype(args)>(args)...);
243  };
244  // follow the general path if storage is a miss
245  InterestHandler missContinuation =
246  [this, auth = std::move(auth), accept = std::move(accept), reject = std::move(reject)] (auto&&... args) {
247  processStatusDatasetInterest(std::forward<decltype(args)>(args)..., auth, accept, reject);
248  };
249 
250  m_handlers[relPrefix] = [this, miss = std::move(missContinuation)] (auto&&... args) {
251  queryStorage(std::forward<decltype(args)>(args)..., miss);
252  };
253 }
254 
255 void
256 Dispatcher::processStatusDatasetInterest(const Name& prefix,
257  const Interest& interest,
258  const Authorization& authorization,
259  const AuthorizationAcceptedCallback& accepted,
260  const AuthorizationRejectedCallback& rejected)
261 {
262  const Name& interestName = interest.getName();
263  bool endsWithVersionOrSegment = interestName.size() >= 1 &&
264  (interestName[-1].isVersion() || interestName[-1].isSegment());
265  if (endsWithVersionOrSegment) {
266  return;
267  }
268 
269  AcceptContinuation accept = [=] (const auto& req) { accepted(req, prefix, interest, nullptr); };
270  RejectContinuation reject = [=] (RejectReply reply) { rejected(reply, interest); };
271  authorization(prefix, interest, nullptr, accept, reject);
272 }
273 
274 void
275 Dispatcher::processAuthorizedStatusDatasetInterest(const Name& prefix,
276  const Interest& interest,
277  const StatusDatasetHandler& handler)
278 {
279  StatusDatasetContext context(interest,
280  [this] (auto&&... args) {
281  sendStatusDatasetSegment(std::forward<decltype(args)>(args)...);
282  },
283  [this, interest] (auto&&... args) {
284  sendControlResponse(std::forward<decltype(args)>(args)..., interest, true);
285  });
286  handler(prefix, interest, context);
287 }
288 
289 void
290 Dispatcher::sendStatusDatasetSegment(const Name& dataName, const Block& content, bool isFinalBlock)
291 {
292  // the first segment will be sent to both places (the face and the in-memory storage)
293  // other segments will be inserted to the in-memory storage only
294  auto destination = SendDestination::IMS;
295  if (dataName[-1].toSegment() == 0) {
296  destination = SendDestination::FACE_AND_IMS;
297  }
298 
299  MetaInfo metaInfo;
300  if (isFinalBlock) {
301  metaInfo.setFinalBlock(dataName[-1]);
302  }
303 
304  sendData(dataName, content, metaInfo, destination);
305 }
306 
309 {
310  if (!m_topLevelPrefixes.empty()) {
311  NDN_THROW(std::domain_error("one or more top-level prefix has been added"));
312  }
313 
314  if (isOverlappedWithOthers(relPrefix)) {
315  NDN_THROW(std::out_of_range("notification stream name overlaps"));
316  }
317 
318  // register a handler for the subscriber of this notification stream
319  // keep silent if Interest does not match a stored notification
320  m_handlers[relPrefix] = [this] (auto&&... args) {
321  queryStorage(std::forward<decltype(args)>(args)..., nullptr);
322  };
323  m_streams[relPrefix] = 0;
324 
325  return [=] (const Block& b) { postNotification(b, relPrefix); };
326 }
327 
328 void
329 Dispatcher::postNotification(const Block& notification, const PartialName& relPrefix)
330 {
331  if (m_topLevelPrefixes.size() != 1) {
332  NDN_LOG_WARN("postNotification: no top-level prefix or too many top-level prefixes");
333  return;
334  }
335 
336  Name streamName(m_topLevelPrefixes.begin()->first);
337  streamName.append(relPrefix);
338  streamName.appendSequenceNumber(m_streams[streamName]++);
339 
340  // notification is sent out via the face after inserting into the in-memory storage,
341  // because a request may be pending in the PIT
342  sendData(streamName, notification, {}, SendDestination::FACE_AND_IMS);
343 }
344 
345 } // 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
size_t size() const noexcept
Returns the number of components.
Definition: name.hpp:180
Base class for a struct that contains ControlCommand parameters.
Implements a request dispatcher on server side of NFD Management protocol.
Definition: dispatcher.hpp:137
PostNotification addNotificationStream(const PartialName &relPrefix)
Register a NotificationStream.
Definition: dispatcher.cpp:308
Dispatcher(Face &face, KeyChain &keyChain, const security::SigningInfo &signingInfo=security::SigningInfo(), size_t imsCapacity=256)
Constructor.
Definition: dispatcher.cpp:44
void addStatusDataset(const PartialName &relPrefix, Authorization authorize, StatusDatasetHandler handle)
Register a StatusDataset or a prefix under which StatusDatasets can be requested.
Definition: dispatcher.cpp:224
void addTopPrefix(const Name &prefix, bool wantRegister=true, const security::SigningInfo &signingInfo=security::SigningInfo())
Add a top-level prefix.
Definition: dispatcher.cpp:57
void removeTopPrefix(const Name &prefix)
Remove a top-level prefix.
Definition: dispatcher.cpp:90
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.
Definition: key-chain.cpp:400
Signing parameters passed to KeyChain.
#define NDN_THROW(e)
Definition: exception.hpp:56
#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
@ CachePolicy
Definition: tlv.hpp:47
std::function< void(const std::string &requester)> AcceptContinuation
A function to be called if authorization is successful.
Definition: dispatcher.hpp:44
std::function< void(const Name &prefix, const Interest &interest, const ControlParameters *params, const AcceptContinuation &accept, const RejectContinuation &reject)> Authorization
A function that performs authorization.
Definition: dispatcher.hpp:80
std::function< void(const Block &notification)> PostNotification
A function to post a notification.
Definition: dispatcher.hpp:129
std::function< void(RejectReply)> RejectContinuation
A function to be called if authorization is rejected.
Definition: dispatcher.hpp:63
RejectReply
Indicates how to reply in case authorization is rejected.
Definition: dispatcher.hpp:49
@ STATUS403
Reply with a ControlResponse where StatusCode is 403.
std::function< bool(const ControlParameters &params)> ValidateParameters
A function to validate input ControlParameters.
Definition: dispatcher.hpp:94
std::function< void(const Name &prefix, const Interest &interest, const ControlParameters &params, const CommandContinuation &done)> ControlCommandHandler
A function to handle an authorized ControlCommand.
Definition: dispatcher.hpp:110
Authorization makeAcceptAllAuthorization()
Return an Authorization that accepts all Interests, with empty string as requester.
Definition: dispatcher.cpp:33
std::function< void(const Name &prefix, const Interest &interest, StatusDatasetContext &context)> StatusDatasetHandler
A function to handle a StatusDataset request.
Definition: dispatcher.hpp:122
mgmt::ControlResponse ControlResponse
@ Name
Definition: tlv.hpp:71
@ Data
Definition: tlv.hpp:69
@ MetaInfo
Definition: tlv.hpp:92
@ Interest
Definition: tlv.hpp:68
@ ContentType_Nack
application-level nack
Definition: tlv.hpp:148
Definition: data.cpp:25