blob: a7fac52082c43ec3750aab6972aab98115a9bb45 [file] [log] [blame]
Yanbiao Li8ee37ed2015-05-19 12:44:04 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Alexander Afanasyev4c9a3d52017-01-03 17:45:19 -08003 * Copyright (c) 2013-2017 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
26#include <algorithm>
27
Junxiao Shi43a79322016-08-08 05:48:43 +000028NDN_LOG_INIT(ndn.mgmt.Dispatcher);
Yanbiao Li8ee37ed2015-05-19 12:44:04 -070029
30namespace ndn {
31namespace mgmt {
32
Yanbiao Li4b4f7542016-03-11 02:04:43 +080033const time::milliseconds DEFAULT_FRESHNESS_PERIOD = time::milliseconds(1000);
34
Yanbiao Li8ee37ed2015-05-19 12:44:04 -070035Authorization
36makeAcceptAllAuthorization()
37{
38 return [] (const Name& prefix,
39 const Interest& interest,
40 const ControlParameters* params,
Junxiao Shif65a3362015-09-06 20:54:54 -070041 const AcceptContinuation& accept,
42 const RejectContinuation& reject) {
Yanbiao Li8ee37ed2015-05-19 12:44:04 -070043 accept("");
44 };
45}
46
Alexander Afanasyev4c9a3d52017-01-03 17:45:19 -080047Dispatcher::Dispatcher(Face& face, security::v1::KeyChain& keyChain,
Yanbiao Li4b4f7542016-03-11 02:04:43 +080048 const security::SigningInfo& signingInfo,
49 size_t imsCapacity)
Yanbiao Li8ee37ed2015-05-19 12:44:04 -070050 : m_face(face)
51 , m_keyChain(keyChain)
52 , m_signingInfo(signingInfo)
Yanbiao Li4b4f7542016-03-11 02:04:43 +080053 , m_storage(m_face.getIoService(), imsCapacity)
Yanbiao Li8ee37ed2015-05-19 12:44:04 -070054{
55}
56
57Dispatcher::~Dispatcher()
58{
59 std::vector<Name> topPrefixNames;
60
61 std::transform(m_topLevelPrefixes.begin(),
62 m_topLevelPrefixes.end(),
63 std::back_inserter(topPrefixNames),
64 [] (const std::unordered_map<Name, TopPrefixEntry>::value_type& entry) {
65 return entry.second.topPrefix;
66 });
67
68 for (auto&& name : topPrefixNames) {
69 removeTopPrefix(name);
70 }
71}
72
73void
74Dispatcher::addTopPrefix(const Name& prefix,
75 bool wantRegister,
76 const security::SigningInfo& signingInfo)
77{
78 bool hasOverlap = std::any_of(m_topLevelPrefixes.begin(),
79 m_topLevelPrefixes.end(),
80 [&] (const std::unordered_map<Name, TopPrefixEntry>::value_type& x) {
81 return x.first.isPrefixOf(prefix) || prefix.isPrefixOf(x.first);
82 });
83 if (hasOverlap) {
84 BOOST_THROW_EXCEPTION(std::out_of_range("Top-level Prefixes overlapped"));
85 }
86
87 TopPrefixEntry& topPrefixEntry = m_topLevelPrefixes[prefix];;
88 topPrefixEntry.topPrefix = prefix;
89 topPrefixEntry.wantRegister = wantRegister;
90
91 if (wantRegister) {
92 RegisterPrefixFailureCallback failure = [] (const Name& name, const std::string& reason) {
93 BOOST_THROW_EXCEPTION(std::runtime_error(reason));
94 };
95 topPrefixEntry.registerPrefixId =
96 m_face.registerPrefix(prefix, bind([]{}), failure, signingInfo);
97 }
98
99 for (auto&& entry : m_handlers) {
100 Name fullPrefix = prefix;
101 fullPrefix.append(entry.first);
102
103 const InterestFilterId* interestFilterId =
104 m_face.setInterestFilter(fullPrefix, std::bind(entry.second, prefix, _2));
105
106 topPrefixEntry.interestFilters.push_back(interestFilterId);
107 }
108}
109
110void
111Dispatcher::removeTopPrefix(const Name& prefix)
112{
113 auto it = m_topLevelPrefixes.find(prefix);
114 if (it == m_topLevelPrefixes.end()) {
115 return;
116 }
117
118 const TopPrefixEntry& topPrefixEntry = it->second;
119 if (topPrefixEntry.wantRegister) {
120 m_face.unregisterPrefix(topPrefixEntry.registerPrefixId, bind([]{}), bind([]{}));
121 }
122
123 for (auto&& filter : topPrefixEntry.interestFilters) {
124 m_face.unsetInterestFilter(filter);
125 }
126
127 m_topLevelPrefixes.erase(it);
128}
129
130bool
131Dispatcher::isOverlappedWithOthers(const PartialName& relPrefix)
132{
133 bool hasOverlapWithHandlers =
134 std::any_of(m_handlers.begin(), m_handlers.end(),
135 [&] (const HandlerMap::value_type& entry) {
136 return entry.first.isPrefixOf(relPrefix) || relPrefix.isPrefixOf(entry.first);
137 });
138 bool hasOverlapWithStreams =
139 std::any_of(m_streams.begin(), m_streams.end(),
140 [&] (const std::unordered_map<PartialName, uint64_t>::value_type& entry) {
141 return entry.first.isPrefixOf(relPrefix) || relPrefix.isPrefixOf(entry.first);
142 });
143
144 return hasOverlapWithHandlers || hasOverlapWithStreams;
145}
146
147void
148Dispatcher::afterAuthorizationRejected(RejectReply act, const Interest& interest)
149{
150 if (act == RejectReply::STATUS403) {
151 sendControlResponse(ControlResponse(403, "authorization rejected"), interest);
152 }
153}
154
155void
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800156Dispatcher::queryStorage(const Name& prefix, const Interest& interest,
157 const InterestHandler& missContinuation)
158{
159 auto data = m_storage.find(interest);
160 if (data == nullptr) {
161 // invoke missContinuation to process this Interest if the query fails.
162 missContinuation(prefix, interest);
163 }
164 else {
165 // send the fetched data through face if query succeeds.
166 sendOnFace(*data);
167 }
168}
169
170void
171Dispatcher::sendData(const Name& dataName, const Block& content, const MetaInfo& metaInfo,
172 SendDestination option, time::milliseconds imsFresh)
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700173{
174 shared_ptr<Data> data = make_shared<Data>(dataName);
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800175 data->setContent(content).setMetaInfo(metaInfo).setFreshnessPeriod(DEFAULT_FRESHNESS_PERIOD);
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700176
177 m_keyChain.sign(*data, m_signingInfo);
178
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800179 if (option == SendDestination::IMS || option == SendDestination::FACE_AND_IMS) {
180 lp::CachePolicy policy;
181 policy.setPolicy(lp::CachePolicyType::NO_CACHE);
182 data->setTag(make_shared<lp::CachePolicyTag>(policy));
183 m_storage.insert(*data, imsFresh);
184 }
185
186 if (option == SendDestination::FACE || option == SendDestination::FACE_AND_IMS) {
187 sendOnFace(*data);
188 }
189}
190
191void
192Dispatcher::sendOnFace(const Data& data)
193{
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700194 try {
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800195 m_face.put(data);
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700196 }
Junxiao Shi43a79322016-08-08 05:48:43 +0000197 catch (const Face::Error& e) {
198 NDN_LOG_ERROR("sendOnFace: " << e.what());
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700199 }
200}
201
202void
203Dispatcher::processControlCommandInterest(const Name& prefix,
204 const Name& relPrefix,
205 const Interest& interest,
206 const ControlParametersParser& parser,
207 const Authorization& authorization,
208 const AuthorizationAcceptedCallback& accepted,
209 const AuthorizationRejectedCallback& rejected)
210{
211 // /<prefix>/<relPrefix>/<parameters>
212 size_t parametersLoc = prefix.size() + relPrefix.size();
213 const name::Component& pc = interest.getName().get(parametersLoc);
214
215 shared_ptr<ControlParameters> parameters;
216 try {
217 parameters = parser(pc);
218 }
Junxiao Shi43a79322016-08-08 05:48:43 +0000219 catch (const tlv::Error&) {
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700220 return;
221 }
222
223 AcceptContinuation accept = bind(accepted, _1, prefix, interest, parameters.get());
224 RejectContinuation reject = bind(rejected, _1, interest);
225 authorization(prefix, interest, parameters.get(), accept, reject);
226}
227
228void
229Dispatcher::processAuthorizedControlCommandInterest(const std::string& requester,
230 const Name& prefix,
231 const Interest& interest,
232 const ControlParameters* parameters,
233 const ValidateParameters& validateParams,
234 const ControlCommandHandler& handler)
235{
236 if (validateParams(*parameters)) {
237 handler(prefix, interest, *parameters,
238 bind(&Dispatcher::sendControlResponse, this, _1, interest, false));
239 }
240 else {
241 sendControlResponse(ControlResponse(400, "failed in validating parameters"), interest);
242 }
243}
244
245void
246Dispatcher::sendControlResponse(const ControlResponse& resp, const Interest& interest,
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800247 bool isNack)
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700248{
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800249 MetaInfo metaInfo;
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700250 if (isNack) {
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800251 metaInfo.setType(tlv::ContentType_Nack);
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700252 }
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800253 // control response is always sent out through the face
254 sendData(interest.getName(), resp.wireEncode(), metaInfo, SendDestination::FACE,
255 DEFAULT_FRESHNESS_PERIOD);
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700256}
257
258void
259Dispatcher::addStatusDataset(const PartialName& relPrefix,
Junxiao Shif65a3362015-09-06 20:54:54 -0700260 const Authorization& authorization,
261 const StatusDatasetHandler& handler)
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700262{
263 if (!m_topLevelPrefixes.empty()) {
264 BOOST_THROW_EXCEPTION(std::domain_error("one or more top-level prefix has been added"));
265 }
266
267 if (isOverlappedWithOthers(relPrefix)) {
268 BOOST_THROW_EXCEPTION(std::out_of_range("relPrefix overlapped"));
269 }
270
271 AuthorizationAcceptedCallback accepted =
272 bind(&Dispatcher::processAuthorizedStatusDatasetInterest, this,
273 _1, _2, _3, handler);
274 AuthorizationRejectedCallback rejected =
275 bind(&Dispatcher::afterAuthorizationRejected, this, _1, _2);
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800276
277 // follow the general path if storage is a miss
278 InterestHandler missContinuation = bind(&Dispatcher::processStatusDatasetInterest, this,
279 _1, _2, authorization, accepted, rejected);
280 m_handlers[relPrefix] = bind(&Dispatcher::queryStorage, this, _1, _2, missContinuation);
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700281}
282
283void
284Dispatcher::processStatusDatasetInterest(const Name& prefix,
285 const Interest& interest,
286 const Authorization& authorization,
287 const AuthorizationAcceptedCallback& accepted,
288 const AuthorizationRejectedCallback& rejected)
289{
290 const Name& interestName = interest.getName();
291 bool endsWithVersionOrSegment = interestName.size() >= 1 &&
292 (interestName[-1].isVersion() || interestName[-1].isSegment());
293 if (endsWithVersionOrSegment) {
294 return;
295 }
296
297 AcceptContinuation accept = bind(accepted, _1, prefix, interest, nullptr);
298 RejectContinuation reject = bind(rejected, _1, interest);
299 authorization(prefix, interest, nullptr, accept, reject);
300}
301
302void
303Dispatcher::processAuthorizedStatusDatasetInterest(const std::string& requester,
304 const Name& prefix,
305 const Interest& interest,
306 const StatusDatasetHandler& handler)
307{
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800308 StatusDatasetContext context(interest,
309 bind(&Dispatcher::sendStatusDatasetSegment, this, _1, _2, _3, _4),
310 bind(&Dispatcher::sendControlResponse, this, _1, interest, true));
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700311 handler(prefix, interest, context);
312}
313
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800314void
315Dispatcher::sendStatusDatasetSegment(const Name& dataName, const Block& content,
316 time::milliseconds imsFresh, bool isFinalBlock)
317{
318 // the first segment will be sent to both places (the face and the in-memory storage)
319 // other segments will be inserted to the in-memory storage only
320 auto destination = SendDestination::IMS;
321 if (dataName[-1].toSegment() == 0) {
322 destination = SendDestination::FACE_AND_IMS;
323 }
324
325 MetaInfo metaInfo;
326 if (isFinalBlock) {
327 metaInfo.setFinalBlockId(dataName[-1]);
328 }
329
330 sendData(dataName, content, metaInfo, destination, imsFresh);
331}
332
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700333PostNotification
334Dispatcher::addNotificationStream(const PartialName& relPrefix)
335{
336 if (!m_topLevelPrefixes.empty()) {
Junxiao Shi43a79322016-08-08 05:48:43 +0000337 BOOST_THROW_EXCEPTION(std::domain_error("one or more top-level prefix has been added"));
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700338 }
339
340 if (isOverlappedWithOthers(relPrefix)) {
Junxiao Shi43a79322016-08-08 05:48:43 +0000341 BOOST_THROW_EXCEPTION(std::out_of_range("relPrefix overlaps with another relPrefix"));
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700342 }
343
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800344 // keep silent if Interest does not match a stored notification
345 InterestHandler missContinuation = bind([]{});
346
347 // register a handler for the subscriber of this notification stream
348 m_handlers[relPrefix] = bind(&Dispatcher::queryStorage, this, _1, _2, missContinuation);
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700349 m_streams[relPrefix] = 0;
350 return bind(&Dispatcher::postNotification, this, _1, relPrefix);
351}
352
353void
354Dispatcher::postNotification(const Block& notification, const PartialName& relPrefix)
355{
356 if (m_topLevelPrefixes.empty() || m_topLevelPrefixes.size() > 1) {
Junxiao Shi43a79322016-08-08 05:48:43 +0000357 NDN_LOG_WARN("postNotification: no top-level prefix or too many top-level prefixes");
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700358 return;
359 }
360
361 Name streamName(m_topLevelPrefixes.begin()->second.topPrefix);
362 streamName.append(relPrefix);
363 streamName.appendSequenceNumber(m_streams[streamName]++);
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800364
Junxiao Shi43a79322016-08-08 05:48:43 +0000365 // notification is sent out by the face after inserting into the in-memory storage,
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800366 // because a request may be pending in the PIT
367 sendData(streamName, notification, MetaInfo(), SendDestination::FACE_AND_IMS,
368 DEFAULT_FRESHNESS_PERIOD);
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700369}
370
371} // namespace mgmt
372} // namespace ndn