blob: df27aca2045f134c4d17656e53623338213bd863 [file] [log] [blame]
Yanbiao Li8ee37ed2015-05-19 12:44:04 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Davide Pesavento0f830802018-01-16 23:58:58 -05002/*
3 * Copyright (c) 2013-2018 Regents of the University of California.
Yanbiao Li8ee37ed2015-05-19 12:44:04 -07004 *
Alexander Afanasyev80b68e12015-09-17 17:01:04 -07005 * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
Yanbiao Li8ee37ed2015-05-19 12:44:04 -07006 *
Alexander Afanasyev80b68e12015-09-17 17:01:04 -07007 * 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.
Yanbiao Li8ee37ed2015-05-19 12:44:04 -070010 *
Alexander Afanasyev80b68e12015-09-17 17:01:04 -070011 * 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.
Yanbiao Li8ee37ed2015-05-19 12:44:04 -070014 *
Alexander Afanasyev80b68e12015-09-17 17:01:04 -070015 * 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.
Yanbiao Li8ee37ed2015-05-19 12:44:04 -070020 */
21
22#include "dispatcher.hpp"
Junxiao Shia1478db2016-09-09 04:13:15 +000023#include "../lp/tags.hpp"
Junxiao Shi43a79322016-08-08 05:48:43 +000024#include "../util/logger.hpp"
Yanbiao Li8ee37ed2015-05-19 12:44:04 -070025
Junxiao Shi43a79322016-08-08 05:48:43 +000026NDN_LOG_INIT(ndn.mgmt.Dispatcher);
Yanbiao Li8ee37ed2015-05-19 12:44:04 -070027
28namespace ndn {
29namespace mgmt {
30
Davide Pesavento0f830802018-01-16 23:58:58 -050031const time::milliseconds DEFAULT_FRESHNESS_PERIOD = 1_s;
Yanbiao Li4b4f7542016-03-11 02:04:43 +080032
Yanbiao Li8ee37ed2015-05-19 12:44:04 -070033Authorization
34makeAcceptAllAuthorization()
35{
36 return [] (const Name& prefix,
37 const Interest& interest,
38 const ControlParameters* params,
Junxiao Shif65a3362015-09-06 20:54:54 -070039 const AcceptContinuation& accept,
40 const RejectContinuation& reject) {
Yanbiao Li8ee37ed2015-05-19 12:44:04 -070041 accept("");
42 };
43}
44
Alexander Afanasyev80782e02017-01-04 13:16:54 -080045Dispatcher::Dispatcher(Face& face, KeyChain& keyChain,
Yanbiao Li4b4f7542016-03-11 02:04:43 +080046 const security::SigningInfo& signingInfo,
47 size_t imsCapacity)
Yanbiao Li8ee37ed2015-05-19 12:44:04 -070048 : m_face(face)
49 , m_keyChain(keyChain)
50 , m_signingInfo(signingInfo)
Yanbiao Li4b4f7542016-03-11 02:04:43 +080051 , m_storage(m_face.getIoService(), imsCapacity)
Yanbiao Li8ee37ed2015-05-19 12:44:04 -070052{
53}
54
55Dispatcher::~Dispatcher()
56{
57 std::vector<Name> topPrefixNames;
Davide Pesavento3c8a8b02018-03-01 14:46:55 -050058 std::transform(m_topLevelPrefixes.begin(), m_topLevelPrefixes.end(),
Yanbiao Li8ee37ed2015-05-19 12:44:04 -070059 std::back_inserter(topPrefixNames),
60 [] (const std::unordered_map<Name, TopPrefixEntry>::value_type& entry) {
61 return entry.second.topPrefix;
62 });
63
Davide Pesavento3c8a8b02018-03-01 14:46:55 -050064 for (const auto& name : topPrefixNames) {
Yanbiao Li8ee37ed2015-05-19 12:44:04 -070065 removeTopPrefix(name);
66 }
67}
68
69void
Davide Pesavento3c8a8b02018-03-01 14:46:55 -050070Dispatcher::addTopPrefix(const Name& prefix, bool wantRegister,
Yanbiao Li8ee37ed2015-05-19 12:44:04 -070071 const security::SigningInfo& signingInfo)
72{
Davide Pesavento3c8a8b02018-03-01 14:46:55 -050073 bool hasOverlap = std::any_of(m_topLevelPrefixes.begin(), m_topLevelPrefixes.end(),
Yanbiao Li8ee37ed2015-05-19 12:44:04 -070074 [&] (const std::unordered_map<Name, TopPrefixEntry>::value_type& x) {
75 return x.first.isPrefixOf(prefix) || prefix.isPrefixOf(x.first);
76 });
77 if (hasOverlap) {
Davide Pesavento3c8a8b02018-03-01 14:46:55 -050078 BOOST_THROW_EXCEPTION(std::out_of_range("top-level prefix overlaps"));
Yanbiao Li8ee37ed2015-05-19 12:44:04 -070079 }
80
Davide Pesavento3c8a8b02018-03-01 14:46:55 -050081 TopPrefixEntry& topPrefixEntry = m_topLevelPrefixes[prefix];
Yanbiao Li8ee37ed2015-05-19 12:44:04 -070082 topPrefixEntry.topPrefix = prefix;
Yanbiao Li8ee37ed2015-05-19 12:44:04 -070083
84 if (wantRegister) {
Davide Pesavento3c8a8b02018-03-01 14:46:55 -050085 topPrefixEntry.registeredPrefixId = m_face.registerPrefix(prefix,
86 nullptr,
87 [] (const Name&, const std::string& reason) {
88 BOOST_THROW_EXCEPTION(std::runtime_error("prefix registration failed: " + reason));
89 },
90 signingInfo);
Yanbiao Li8ee37ed2015-05-19 12:44:04 -070091 }
92
Davide Pesavento3c8a8b02018-03-01 14:46:55 -050093 for (const auto& entry : m_handlers) {
94 Name fullPrefix = Name(prefix).append(entry.first);
95 const InterestFilterId* filter = m_face.setInterestFilter(fullPrefix, bind(entry.second, prefix, _2));
96 topPrefixEntry.interestFilters.push_back(filter);
Yanbiao Li8ee37ed2015-05-19 12:44:04 -070097 }
98}
99
100void
101Dispatcher::removeTopPrefix(const Name& prefix)
102{
103 auto it = m_topLevelPrefixes.find(prefix);
104 if (it == m_topLevelPrefixes.end()) {
105 return;
106 }
107
108 const TopPrefixEntry& topPrefixEntry = it->second;
Davide Pesavento3c8a8b02018-03-01 14:46:55 -0500109 if (topPrefixEntry.registeredPrefixId) {
110 m_face.unregisterPrefix(*topPrefixEntry.registeredPrefixId, nullptr, nullptr);
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700111 }
Davide Pesavento3c8a8b02018-03-01 14:46:55 -0500112 for (const auto& filter : topPrefixEntry.interestFilters) {
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700113 m_face.unsetInterestFilter(filter);
114 }
115
116 m_topLevelPrefixes.erase(it);
117}
118
119bool
Davide Pesavento3c8a8b02018-03-01 14:46:55 -0500120Dispatcher::isOverlappedWithOthers(const PartialName& relPrefix) const
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700121{
122 bool hasOverlapWithHandlers =
123 std::any_of(m_handlers.begin(), m_handlers.end(),
Davide Pesavento3c8a8b02018-03-01 14:46:55 -0500124 [&] (const std::unordered_map<PartialName, InterestHandler>::value_type& entry) {
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700125 return entry.first.isPrefixOf(relPrefix) || relPrefix.isPrefixOf(entry.first);
126 });
127 bool hasOverlapWithStreams =
128 std::any_of(m_streams.begin(), m_streams.end(),
129 [&] (const std::unordered_map<PartialName, uint64_t>::value_type& entry) {
130 return entry.first.isPrefixOf(relPrefix) || relPrefix.isPrefixOf(entry.first);
131 });
132
133 return hasOverlapWithHandlers || hasOverlapWithStreams;
134}
135
136void
137Dispatcher::afterAuthorizationRejected(RejectReply act, const Interest& interest)
138{
139 if (act == RejectReply::STATUS403) {
140 sendControlResponse(ControlResponse(403, "authorization rejected"), interest);
141 }
142}
143
144void
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800145Dispatcher::queryStorage(const Name& prefix, const Interest& interest,
146 const InterestHandler& missContinuation)
147{
148 auto data = m_storage.find(interest);
149 if (data == nullptr) {
150 // invoke missContinuation to process this Interest if the query fails.
Davide Pesavento3c8a8b02018-03-01 14:46:55 -0500151 if (missContinuation)
152 missContinuation(prefix, interest);
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800153 }
154 else {
155 // send the fetched data through face if query succeeds.
156 sendOnFace(*data);
157 }
158}
159
160void
161Dispatcher::sendData(const Name& dataName, const Block& content, const MetaInfo& metaInfo,
162 SendDestination option, time::milliseconds imsFresh)
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700163{
Davide Pesavento3c8a8b02018-03-01 14:46:55 -0500164 auto data = make_shared<Data>(dataName);
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800165 data->setContent(content).setMetaInfo(metaInfo).setFreshnessPeriod(DEFAULT_FRESHNESS_PERIOD);
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700166
167 m_keyChain.sign(*data, m_signingInfo);
168
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800169 if (option == SendDestination::IMS || option == SendDestination::FACE_AND_IMS) {
170 lp::CachePolicy policy;
171 policy.setPolicy(lp::CachePolicyType::NO_CACHE);
172 data->setTag(make_shared<lp::CachePolicyTag>(policy));
173 m_storage.insert(*data, imsFresh);
174 }
175
176 if (option == SendDestination::FACE || option == SendDestination::FACE_AND_IMS) {
177 sendOnFace(*data);
178 }
179}
180
181void
182Dispatcher::sendOnFace(const Data& data)
183{
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700184 try {
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800185 m_face.put(data);
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700186 }
Junxiao Shi43a79322016-08-08 05:48:43 +0000187 catch (const Face::Error& e) {
188 NDN_LOG_ERROR("sendOnFace: " << e.what());
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700189 }
190}
191
192void
193Dispatcher::processControlCommandInterest(const Name& prefix,
194 const Name& relPrefix,
195 const Interest& interest,
196 const ControlParametersParser& parser,
197 const Authorization& authorization,
198 const AuthorizationAcceptedCallback& accepted,
199 const AuthorizationRejectedCallback& rejected)
200{
201 // /<prefix>/<relPrefix>/<parameters>
202 size_t parametersLoc = prefix.size() + relPrefix.size();
203 const name::Component& pc = interest.getName().get(parametersLoc);
204
205 shared_ptr<ControlParameters> parameters;
206 try {
207 parameters = parser(pc);
208 }
Junxiao Shi43a79322016-08-08 05:48:43 +0000209 catch (const tlv::Error&) {
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700210 return;
211 }
212
Junxiao Shid97c9532017-04-27 16:17:04 +0000213 AcceptContinuation accept = bind(accepted, _1, prefix, interest, parameters);
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700214 RejectContinuation reject = bind(rejected, _1, interest);
215 authorization(prefix, interest, parameters.get(), accept, reject);
216}
217
218void
219Dispatcher::processAuthorizedControlCommandInterest(const std::string& requester,
220 const Name& prefix,
221 const Interest& interest,
Junxiao Shid97c9532017-04-27 16:17:04 +0000222 const shared_ptr<ControlParameters>& parameters,
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700223 const ValidateParameters& validateParams,
224 const ControlCommandHandler& handler)
225{
226 if (validateParams(*parameters)) {
227 handler(prefix, interest, *parameters,
228 bind(&Dispatcher::sendControlResponse, this, _1, interest, false));
229 }
230 else {
231 sendControlResponse(ControlResponse(400, "failed in validating parameters"), interest);
232 }
233}
234
235void
Davide Pesavento3c8a8b02018-03-01 14:46:55 -0500236Dispatcher::sendControlResponse(const ControlResponse& resp, const Interest& interest, bool isNack)
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700237{
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800238 MetaInfo metaInfo;
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700239 if (isNack) {
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800240 metaInfo.setType(tlv::ContentType_Nack);
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700241 }
Davide Pesavento3c8a8b02018-03-01 14:46:55 -0500242
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800243 // control response is always sent out through the face
Davide Pesavento3c8a8b02018-03-01 14:46:55 -0500244 sendData(interest.getName(), resp.wireEncode(), metaInfo,
245 SendDestination::FACE, DEFAULT_FRESHNESS_PERIOD);
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700246}
247
248void
249Dispatcher::addStatusDataset(const PartialName& relPrefix,
Junxiao Shif65a3362015-09-06 20:54:54 -0700250 const Authorization& authorization,
251 const StatusDatasetHandler& handler)
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700252{
253 if (!m_topLevelPrefixes.empty()) {
254 BOOST_THROW_EXCEPTION(std::domain_error("one or more top-level prefix has been added"));
255 }
256
257 if (isOverlappedWithOthers(relPrefix)) {
Davide Pesavento3c8a8b02018-03-01 14:46:55 -0500258 BOOST_THROW_EXCEPTION(std::out_of_range("status dataset name overlaps"));
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700259 }
260
261 AuthorizationAcceptedCallback accepted =
Davide Pesavento3c8a8b02018-03-01 14:46:55 -0500262 bind(&Dispatcher::processAuthorizedStatusDatasetInterest, this, _1, _2, _3, handler);
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700263 AuthorizationRejectedCallback rejected =
264 bind(&Dispatcher::afterAuthorizationRejected, this, _1, _2);
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800265
266 // follow the general path if storage is a miss
267 InterestHandler missContinuation = bind(&Dispatcher::processStatusDatasetInterest, this,
268 _1, _2, authorization, accepted, rejected);
269 m_handlers[relPrefix] = bind(&Dispatcher::queryStorage, this, _1, _2, missContinuation);
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700270}
271
272void
273Dispatcher::processStatusDatasetInterest(const Name& prefix,
274 const Interest& interest,
275 const Authorization& authorization,
276 const AuthorizationAcceptedCallback& accepted,
277 const AuthorizationRejectedCallback& rejected)
278{
279 const Name& interestName = interest.getName();
280 bool endsWithVersionOrSegment = interestName.size() >= 1 &&
281 (interestName[-1].isVersion() || interestName[-1].isSegment());
282 if (endsWithVersionOrSegment) {
283 return;
284 }
285
286 AcceptContinuation accept = bind(accepted, _1, prefix, interest, nullptr);
287 RejectContinuation reject = bind(rejected, _1, interest);
288 authorization(prefix, interest, nullptr, accept, reject);
289}
290
291void
292Dispatcher::processAuthorizedStatusDatasetInterest(const std::string& requester,
293 const Name& prefix,
294 const Interest& interest,
295 const StatusDatasetHandler& handler)
296{
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800297 StatusDatasetContext context(interest,
298 bind(&Dispatcher::sendStatusDatasetSegment, this, _1, _2, _3, _4),
299 bind(&Dispatcher::sendControlResponse, this, _1, interest, true));
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700300 handler(prefix, interest, context);
301}
302
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800303void
304Dispatcher::sendStatusDatasetSegment(const Name& dataName, const Block& content,
305 time::milliseconds imsFresh, bool isFinalBlock)
306{
307 // the first segment will be sent to both places (the face and the in-memory storage)
308 // other segments will be inserted to the in-memory storage only
309 auto destination = SendDestination::IMS;
310 if (dataName[-1].toSegment() == 0) {
311 destination = SendDestination::FACE_AND_IMS;
312 }
313
314 MetaInfo metaInfo;
315 if (isFinalBlock) {
316 metaInfo.setFinalBlockId(dataName[-1]);
317 }
318
319 sendData(dataName, content, metaInfo, destination, imsFresh);
320}
321
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700322PostNotification
323Dispatcher::addNotificationStream(const PartialName& relPrefix)
324{
325 if (!m_topLevelPrefixes.empty()) {
Junxiao Shi43a79322016-08-08 05:48:43 +0000326 BOOST_THROW_EXCEPTION(std::domain_error("one or more top-level prefix has been added"));
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700327 }
328
329 if (isOverlappedWithOthers(relPrefix)) {
Davide Pesavento3c8a8b02018-03-01 14:46:55 -0500330 BOOST_THROW_EXCEPTION(std::out_of_range("notification stream name overlaps"));
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700331 }
332
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800333 // register a handler for the subscriber of this notification stream
Davide Pesavento3c8a8b02018-03-01 14:46:55 -0500334 // keep silent if Interest does not match a stored notification
335 m_handlers[relPrefix] = bind(&Dispatcher::queryStorage, this, _1, _2, nullptr);
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700336 m_streams[relPrefix] = 0;
Davide Pesavento3c8a8b02018-03-01 14:46:55 -0500337
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700338 return bind(&Dispatcher::postNotification, this, _1, relPrefix);
339}
340
341void
342Dispatcher::postNotification(const Block& notification, const PartialName& relPrefix)
343{
344 if (m_topLevelPrefixes.empty() || m_topLevelPrefixes.size() > 1) {
Junxiao Shi43a79322016-08-08 05:48:43 +0000345 NDN_LOG_WARN("postNotification: no top-level prefix or too many top-level prefixes");
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700346 return;
347 }
348
349 Name streamName(m_topLevelPrefixes.begin()->second.topPrefix);
350 streamName.append(relPrefix);
351 streamName.appendSequenceNumber(m_streams[streamName]++);
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800352
Davide Pesavento3c8a8b02018-03-01 14:46:55 -0500353 // notification is sent out via the face after inserting into the in-memory storage,
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800354 // because a request may be pending in the PIT
Davide Pesavento3c8a8b02018-03-01 14:46:55 -0500355 sendData(streamName, notification, {}, SendDestination::FACE_AND_IMS, DEFAULT_FRESHNESS_PERIOD);
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700356}
357
358} // namespace mgmt
359} // namespace ndn