blob: 73527203e422f83bfba9c441a7bb8c33b248945a [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 Pesaventodb4da5e2018-06-15 11:37:52 -040058 std::transform(m_topLevelPrefixes.begin(), m_topLevelPrefixes.end(), std::back_inserter(topPrefixNames),
59 [] (const auto& entry) { return entry.second.topPrefix; });
Yanbiao Li8ee37ed2015-05-19 12:44:04 -070060
Davide Pesavento3c8a8b02018-03-01 14:46:55 -050061 for (const auto& name : topPrefixNames) {
Yanbiao Li8ee37ed2015-05-19 12:44:04 -070062 removeTopPrefix(name);
63 }
64}
65
66void
Davide Pesavento3c8a8b02018-03-01 14:46:55 -050067Dispatcher::addTopPrefix(const Name& prefix, bool wantRegister,
Yanbiao Li8ee37ed2015-05-19 12:44:04 -070068 const security::SigningInfo& signingInfo)
69{
Davide Pesavento3c8a8b02018-03-01 14:46:55 -050070 bool hasOverlap = std::any_of(m_topLevelPrefixes.begin(), m_topLevelPrefixes.end(),
Davide Pesaventodb4da5e2018-06-15 11:37:52 -040071 [&prefix] (const auto& x) {
Yanbiao Li8ee37ed2015-05-19 12:44:04 -070072 return x.first.isPrefixOf(prefix) || prefix.isPrefixOf(x.first);
73 });
74 if (hasOverlap) {
Davide Pesavento3c8a8b02018-03-01 14:46:55 -050075 BOOST_THROW_EXCEPTION(std::out_of_range("top-level prefix overlaps"));
Yanbiao Li8ee37ed2015-05-19 12:44:04 -070076 }
77
Davide Pesavento3c8a8b02018-03-01 14:46:55 -050078 TopPrefixEntry& topPrefixEntry = m_topLevelPrefixes[prefix];
Yanbiao Li8ee37ed2015-05-19 12:44:04 -070079 topPrefixEntry.topPrefix = prefix;
Yanbiao Li8ee37ed2015-05-19 12:44:04 -070080
81 if (wantRegister) {
Davide Pesavento3c8a8b02018-03-01 14:46:55 -050082 topPrefixEntry.registeredPrefixId = m_face.registerPrefix(prefix,
83 nullptr,
84 [] (const Name&, const std::string& reason) {
85 BOOST_THROW_EXCEPTION(std::runtime_error("prefix registration failed: " + reason));
86 },
87 signingInfo);
Yanbiao Li8ee37ed2015-05-19 12:44:04 -070088 }
89
Davide Pesavento3c8a8b02018-03-01 14:46:55 -050090 for (const auto& entry : m_handlers) {
91 Name fullPrefix = Name(prefix).append(entry.first);
Davide Pesaventodb4da5e2018-06-15 11:37:52 -040092 const auto* filterId = m_face.setInterestFilter(fullPrefix, bind(entry.second, prefix, _2));
93 topPrefixEntry.interestFilters.push_back(filterId);
Yanbiao Li8ee37ed2015-05-19 12:44:04 -070094 }
95}
96
97void
98Dispatcher::removeTopPrefix(const Name& prefix)
99{
100 auto it = m_topLevelPrefixes.find(prefix);
101 if (it == m_topLevelPrefixes.end()) {
102 return;
103 }
104
105 const TopPrefixEntry& topPrefixEntry = it->second;
Davide Pesavento3c8a8b02018-03-01 14:46:55 -0500106 if (topPrefixEntry.registeredPrefixId) {
107 m_face.unregisterPrefix(*topPrefixEntry.registeredPrefixId, nullptr, nullptr);
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700108 }
Davide Pesavento3c8a8b02018-03-01 14:46:55 -0500109 for (const auto& filter : topPrefixEntry.interestFilters) {
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700110 m_face.unsetInterestFilter(filter);
111 }
112
113 m_topLevelPrefixes.erase(it);
114}
115
116bool
Davide Pesavento3c8a8b02018-03-01 14:46:55 -0500117Dispatcher::isOverlappedWithOthers(const PartialName& relPrefix) const
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700118{
119 bool hasOverlapWithHandlers =
120 std::any_of(m_handlers.begin(), m_handlers.end(),
Davide Pesaventodb4da5e2018-06-15 11:37:52 -0400121 [&] (const auto& entry) {
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700122 return entry.first.isPrefixOf(relPrefix) || relPrefix.isPrefixOf(entry.first);
123 });
124 bool hasOverlapWithStreams =
125 std::any_of(m_streams.begin(), m_streams.end(),
Davide Pesaventodb4da5e2018-06-15 11:37:52 -0400126 [&] (const auto& entry) {
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700127 return entry.first.isPrefixOf(relPrefix) || relPrefix.isPrefixOf(entry.first);
128 });
129
130 return hasOverlapWithHandlers || hasOverlapWithStreams;
131}
132
133void
134Dispatcher::afterAuthorizationRejected(RejectReply act, const Interest& interest)
135{
136 if (act == RejectReply::STATUS403) {
137 sendControlResponse(ControlResponse(403, "authorization rejected"), interest);
138 }
139}
140
141void
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800142Dispatcher::queryStorage(const Name& prefix, const Interest& interest,
143 const InterestHandler& missContinuation)
144{
145 auto data = m_storage.find(interest);
146 if (data == nullptr) {
147 // invoke missContinuation to process this Interest if the query fails.
Davide Pesavento3c8a8b02018-03-01 14:46:55 -0500148 if (missContinuation)
149 missContinuation(prefix, interest);
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800150 }
151 else {
152 // send the fetched data through face if query succeeds.
153 sendOnFace(*data);
154 }
155}
156
157void
158Dispatcher::sendData(const Name& dataName, const Block& content, const MetaInfo& metaInfo,
159 SendDestination option, time::milliseconds imsFresh)
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700160{
Davide Pesavento3c8a8b02018-03-01 14:46:55 -0500161 auto data = make_shared<Data>(dataName);
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800162 data->setContent(content).setMetaInfo(metaInfo).setFreshnessPeriod(DEFAULT_FRESHNESS_PERIOD);
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700163
164 m_keyChain.sign(*data, m_signingInfo);
165
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800166 if (option == SendDestination::IMS || option == SendDestination::FACE_AND_IMS) {
167 lp::CachePolicy policy;
168 policy.setPolicy(lp::CachePolicyType::NO_CACHE);
169 data->setTag(make_shared<lp::CachePolicyTag>(policy));
170 m_storage.insert(*data, imsFresh);
171 }
172
173 if (option == SendDestination::FACE || option == SendDestination::FACE_AND_IMS) {
174 sendOnFace(*data);
175 }
176}
177
178void
179Dispatcher::sendOnFace(const Data& data)
180{
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700181 try {
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800182 m_face.put(data);
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700183 }
Junxiao Shi43a79322016-08-08 05:48:43 +0000184 catch (const Face::Error& e) {
185 NDN_LOG_ERROR("sendOnFace: " << e.what());
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700186 }
187}
188
189void
190Dispatcher::processControlCommandInterest(const Name& prefix,
191 const Name& relPrefix,
192 const Interest& interest,
193 const ControlParametersParser& parser,
194 const Authorization& authorization,
195 const AuthorizationAcceptedCallback& accepted,
196 const AuthorizationRejectedCallback& rejected)
197{
198 // /<prefix>/<relPrefix>/<parameters>
199 size_t parametersLoc = prefix.size() + relPrefix.size();
200 const name::Component& pc = interest.getName().get(parametersLoc);
201
202 shared_ptr<ControlParameters> parameters;
203 try {
204 parameters = parser(pc);
205 }
Junxiao Shi43a79322016-08-08 05:48:43 +0000206 catch (const tlv::Error&) {
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700207 return;
208 }
209
Davide Pesaventodb4da5e2018-06-15 11:37:52 -0400210 AcceptContinuation accept = [=] (const auto& req) { accepted(req, prefix, interest, parameters); };
211 RejectContinuation reject = [=] (RejectReply reply) { rejected(reply, interest); };
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700212 authorization(prefix, interest, parameters.get(), accept, reject);
213}
214
215void
216Dispatcher::processAuthorizedControlCommandInterest(const std::string& requester,
217 const Name& prefix,
218 const Interest& interest,
Junxiao Shid97c9532017-04-27 16:17:04 +0000219 const shared_ptr<ControlParameters>& parameters,
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700220 const ValidateParameters& validateParams,
221 const ControlCommandHandler& handler)
222{
223 if (validateParams(*parameters)) {
224 handler(prefix, interest, *parameters,
Davide Pesaventodb4da5e2018-06-15 11:37:52 -0400225 [=] (const auto& resp) { this->sendControlResponse(resp, interest); });
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700226 }
227 else {
228 sendControlResponse(ControlResponse(400, "failed in validating parameters"), interest);
229 }
230}
231
232void
Davide Pesavento3c8a8b02018-03-01 14:46:55 -0500233Dispatcher::sendControlResponse(const ControlResponse& resp, const Interest& interest, bool isNack)
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700234{
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800235 MetaInfo metaInfo;
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700236 if (isNack) {
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800237 metaInfo.setType(tlv::ContentType_Nack);
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700238 }
Davide Pesavento3c8a8b02018-03-01 14:46:55 -0500239
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800240 // control response is always sent out through the face
Davide Pesavento3c8a8b02018-03-01 14:46:55 -0500241 sendData(interest.getName(), resp.wireEncode(), metaInfo,
242 SendDestination::FACE, DEFAULT_FRESHNESS_PERIOD);
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700243}
244
245void
246Dispatcher::addStatusDataset(const PartialName& relPrefix,
Davide Pesaventodb4da5e2018-06-15 11:37:52 -0400247 Authorization authorize,
248 StatusDatasetHandler handle)
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700249{
250 if (!m_topLevelPrefixes.empty()) {
251 BOOST_THROW_EXCEPTION(std::domain_error("one or more top-level prefix has been added"));
252 }
253
254 if (isOverlappedWithOthers(relPrefix)) {
Davide Pesavento3c8a8b02018-03-01 14:46:55 -0500255 BOOST_THROW_EXCEPTION(std::out_of_range("status dataset name overlaps"));
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700256 }
257
258 AuthorizationAcceptedCallback accepted =
Davide Pesaventodb4da5e2018-06-15 11:37:52 -0400259 bind(&Dispatcher::processAuthorizedStatusDatasetInterest, this, _1, _2, _3, std::move(handle));
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700260 AuthorizationRejectedCallback rejected =
261 bind(&Dispatcher::afterAuthorizationRejected, this, _1, _2);
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800262
263 // follow the general path if storage is a miss
Davide Pesaventodb4da5e2018-06-15 11:37:52 -0400264 InterestHandler missContinuation = bind(&Dispatcher::processStatusDatasetInterest, this, _1, _2,
265 std::move(authorize), std::move(accepted), std::move(rejected));
266
267 m_handlers[relPrefix] = [this, miss = std::move(missContinuation)] (auto&&... args) {
268 this->queryStorage(std::forward<decltype(args)>(args)..., miss);
269 };
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
Davide Pesaventodb4da5e2018-06-15 11:37:52 -0400286 AcceptContinuation accept = [=] (const auto& req) { accepted(req, prefix, interest, nullptr); };
287 RejectContinuation reject = [=] (RejectReply reply) { rejected(reply, interest); };
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700288 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) {
Junxiao Shiebfe4a22018-04-01 23:53:40 +0000316 metaInfo.setFinalBlock(dataName[-1]);
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800317 }
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
Davide Pesaventodb4da5e2018-06-15 11:37:52 -0400335 m_handlers[relPrefix] = [this] (auto&&... args) {
336 this->queryStorage(std::forward<decltype(args)>(args)..., nullptr);
337 };
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700338 m_streams[relPrefix] = 0;
Davide Pesavento3c8a8b02018-03-01 14:46:55 -0500339
Davide Pesaventodb4da5e2018-06-15 11:37:52 -0400340 return [=] (const Block& b) { postNotification(b, relPrefix); };
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700341}
342
343void
344Dispatcher::postNotification(const Block& notification, const PartialName& relPrefix)
345{
346 if (m_topLevelPrefixes.empty() || m_topLevelPrefixes.size() > 1) {
Junxiao Shi43a79322016-08-08 05:48:43 +0000347 NDN_LOG_WARN("postNotification: no top-level prefix or too many top-level prefixes");
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700348 return;
349 }
350
351 Name streamName(m_topLevelPrefixes.begin()->second.topPrefix);
352 streamName.append(relPrefix);
353 streamName.appendSequenceNumber(m_streams[streamName]++);
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800354
Davide Pesavento3c8a8b02018-03-01 14:46:55 -0500355 // notification is sent out via the face after inserting into the in-memory storage,
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800356 // because a request may be pending in the PIT
Davide Pesavento3c8a8b02018-03-01 14:46:55 -0500357 sendData(streamName, notification, {}, SendDestination::FACE_AND_IMS, DEFAULT_FRESHNESS_PERIOD);
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700358}
359
360} // namespace mgmt
361} // namespace ndn