blob: b9d62a280fe5f3b09f2edb74773b8fa890b4b19d [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
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
Yanbiao Li4b4f7542016-03-11 02:04:43 +080031const time::milliseconds DEFAULT_FRESHNESS_PERIOD = time::milliseconds(1000);
32
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 Afanasyev4c9a3d52017-01-03 17:45:19 -080045Dispatcher::Dispatcher(Face& face, security::v1::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;
58
59 std::transform(m_topLevelPrefixes.begin(),
60 m_topLevelPrefixes.end(),
61 std::back_inserter(topPrefixNames),
62 [] (const std::unordered_map<Name, TopPrefixEntry>::value_type& entry) {
63 return entry.second.topPrefix;
64 });
65
66 for (auto&& name : topPrefixNames) {
67 removeTopPrefix(name);
68 }
69}
70
71void
72Dispatcher::addTopPrefix(const Name& prefix,
73 bool wantRegister,
74 const security::SigningInfo& signingInfo)
75{
76 bool hasOverlap = std::any_of(m_topLevelPrefixes.begin(),
77 m_topLevelPrefixes.end(),
78 [&] (const std::unordered_map<Name, TopPrefixEntry>::value_type& x) {
79 return x.first.isPrefixOf(prefix) || prefix.isPrefixOf(x.first);
80 });
81 if (hasOverlap) {
82 BOOST_THROW_EXCEPTION(std::out_of_range("Top-level Prefixes overlapped"));
83 }
84
85 TopPrefixEntry& topPrefixEntry = m_topLevelPrefixes[prefix];;
86 topPrefixEntry.topPrefix = prefix;
87 topPrefixEntry.wantRegister = wantRegister;
88
89 if (wantRegister) {
90 RegisterPrefixFailureCallback failure = [] (const Name& name, const std::string& reason) {
91 BOOST_THROW_EXCEPTION(std::runtime_error(reason));
92 };
93 topPrefixEntry.registerPrefixId =
94 m_face.registerPrefix(prefix, bind([]{}), failure, signingInfo);
95 }
96
97 for (auto&& entry : m_handlers) {
98 Name fullPrefix = prefix;
99 fullPrefix.append(entry.first);
100
101 const InterestFilterId* interestFilterId =
102 m_face.setInterestFilter(fullPrefix, std::bind(entry.second, prefix, _2));
103
104 topPrefixEntry.interestFilters.push_back(interestFilterId);
105 }
106}
107
108void
109Dispatcher::removeTopPrefix(const Name& prefix)
110{
111 auto it = m_topLevelPrefixes.find(prefix);
112 if (it == m_topLevelPrefixes.end()) {
113 return;
114 }
115
116 const TopPrefixEntry& topPrefixEntry = it->second;
117 if (topPrefixEntry.wantRegister) {
118 m_face.unregisterPrefix(topPrefixEntry.registerPrefixId, bind([]{}), bind([]{}));
119 }
120
121 for (auto&& filter : topPrefixEntry.interestFilters) {
122 m_face.unsetInterestFilter(filter);
123 }
124
125 m_topLevelPrefixes.erase(it);
126}
127
128bool
129Dispatcher::isOverlappedWithOthers(const PartialName& relPrefix)
130{
131 bool hasOverlapWithHandlers =
132 std::any_of(m_handlers.begin(), m_handlers.end(),
133 [&] (const HandlerMap::value_type& entry) {
134 return entry.first.isPrefixOf(relPrefix) || relPrefix.isPrefixOf(entry.first);
135 });
136 bool hasOverlapWithStreams =
137 std::any_of(m_streams.begin(), m_streams.end(),
138 [&] (const std::unordered_map<PartialName, uint64_t>::value_type& entry) {
139 return entry.first.isPrefixOf(relPrefix) || relPrefix.isPrefixOf(entry.first);
140 });
141
142 return hasOverlapWithHandlers || hasOverlapWithStreams;
143}
144
145void
146Dispatcher::afterAuthorizationRejected(RejectReply act, const Interest& interest)
147{
148 if (act == RejectReply::STATUS403) {
149 sendControlResponse(ControlResponse(403, "authorization rejected"), interest);
150 }
151}
152
153void
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800154Dispatcher::queryStorage(const Name& prefix, const Interest& interest,
155 const InterestHandler& missContinuation)
156{
157 auto data = m_storage.find(interest);
158 if (data == nullptr) {
159 // invoke missContinuation to process this Interest if the query fails.
160 missContinuation(prefix, interest);
161 }
162 else {
163 // send the fetched data through face if query succeeds.
164 sendOnFace(*data);
165 }
166}
167
168void
169Dispatcher::sendData(const Name& dataName, const Block& content, const MetaInfo& metaInfo,
170 SendDestination option, time::milliseconds imsFresh)
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700171{
172 shared_ptr<Data> data = make_shared<Data>(dataName);
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800173 data->setContent(content).setMetaInfo(metaInfo).setFreshnessPeriod(DEFAULT_FRESHNESS_PERIOD);
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700174
175 m_keyChain.sign(*data, m_signingInfo);
176
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800177 if (option == SendDestination::IMS || option == SendDestination::FACE_AND_IMS) {
178 lp::CachePolicy policy;
179 policy.setPolicy(lp::CachePolicyType::NO_CACHE);
180 data->setTag(make_shared<lp::CachePolicyTag>(policy));
181 m_storage.insert(*data, imsFresh);
182 }
183
184 if (option == SendDestination::FACE || option == SendDestination::FACE_AND_IMS) {
185 sendOnFace(*data);
186 }
187}
188
189void
190Dispatcher::sendOnFace(const Data& data)
191{
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700192 try {
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800193 m_face.put(data);
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700194 }
Junxiao Shi43a79322016-08-08 05:48:43 +0000195 catch (const Face::Error& e) {
196 NDN_LOG_ERROR("sendOnFace: " << e.what());
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700197 }
198}
199
200void
201Dispatcher::processControlCommandInterest(const Name& prefix,
202 const Name& relPrefix,
203 const Interest& interest,
204 const ControlParametersParser& parser,
205 const Authorization& authorization,
206 const AuthorizationAcceptedCallback& accepted,
207 const AuthorizationRejectedCallback& rejected)
208{
209 // /<prefix>/<relPrefix>/<parameters>
210 size_t parametersLoc = prefix.size() + relPrefix.size();
211 const name::Component& pc = interest.getName().get(parametersLoc);
212
213 shared_ptr<ControlParameters> parameters;
214 try {
215 parameters = parser(pc);
216 }
Junxiao Shi43a79322016-08-08 05:48:43 +0000217 catch (const tlv::Error&) {
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700218 return;
219 }
220
221 AcceptContinuation accept = bind(accepted, _1, prefix, interest, parameters.get());
222 RejectContinuation reject = bind(rejected, _1, interest);
223 authorization(prefix, interest, parameters.get(), accept, reject);
224}
225
226void
227Dispatcher::processAuthorizedControlCommandInterest(const std::string& requester,
228 const Name& prefix,
229 const Interest& interest,
230 const ControlParameters* parameters,
231 const ValidateParameters& validateParams,
232 const ControlCommandHandler& handler)
233{
234 if (validateParams(*parameters)) {
235 handler(prefix, interest, *parameters,
236 bind(&Dispatcher::sendControlResponse, this, _1, interest, false));
237 }
238 else {
239 sendControlResponse(ControlResponse(400, "failed in validating parameters"), interest);
240 }
241}
242
243void
244Dispatcher::sendControlResponse(const ControlResponse& resp, const Interest& interest,
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800245 bool isNack)
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700246{
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800247 MetaInfo metaInfo;
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700248 if (isNack) {
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800249 metaInfo.setType(tlv::ContentType_Nack);
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700250 }
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800251 // control response is always sent out through the face
252 sendData(interest.getName(), resp.wireEncode(), metaInfo, SendDestination::FACE,
253 DEFAULT_FRESHNESS_PERIOD);
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700254}
255
256void
257Dispatcher::addStatusDataset(const PartialName& relPrefix,
Junxiao Shif65a3362015-09-06 20:54:54 -0700258 const Authorization& authorization,
259 const StatusDatasetHandler& handler)
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700260{
261 if (!m_topLevelPrefixes.empty()) {
262 BOOST_THROW_EXCEPTION(std::domain_error("one or more top-level prefix has been added"));
263 }
264
265 if (isOverlappedWithOthers(relPrefix)) {
266 BOOST_THROW_EXCEPTION(std::out_of_range("relPrefix overlapped"));
267 }
268
269 AuthorizationAcceptedCallback accepted =
270 bind(&Dispatcher::processAuthorizedStatusDatasetInterest, this,
271 _1, _2, _3, handler);
272 AuthorizationRejectedCallback rejected =
273 bind(&Dispatcher::afterAuthorizationRejected, this, _1, _2);
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800274
275 // follow the general path if storage is a miss
276 InterestHandler missContinuation = bind(&Dispatcher::processStatusDatasetInterest, this,
277 _1, _2, authorization, accepted, rejected);
278 m_handlers[relPrefix] = bind(&Dispatcher::queryStorage, this, _1, _2, missContinuation);
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700279}
280
281void
282Dispatcher::processStatusDatasetInterest(const Name& prefix,
283 const Interest& interest,
284 const Authorization& authorization,
285 const AuthorizationAcceptedCallback& accepted,
286 const AuthorizationRejectedCallback& rejected)
287{
288 const Name& interestName = interest.getName();
289 bool endsWithVersionOrSegment = interestName.size() >= 1 &&
290 (interestName[-1].isVersion() || interestName[-1].isSegment());
291 if (endsWithVersionOrSegment) {
292 return;
293 }
294
295 AcceptContinuation accept = bind(accepted, _1, prefix, interest, nullptr);
296 RejectContinuation reject = bind(rejected, _1, interest);
297 authorization(prefix, interest, nullptr, accept, reject);
298}
299
300void
301Dispatcher::processAuthorizedStatusDatasetInterest(const std::string& requester,
302 const Name& prefix,
303 const Interest& interest,
304 const StatusDatasetHandler& handler)
305{
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800306 StatusDatasetContext context(interest,
307 bind(&Dispatcher::sendStatusDatasetSegment, this, _1, _2, _3, _4),
308 bind(&Dispatcher::sendControlResponse, this, _1, interest, true));
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700309 handler(prefix, interest, context);
310}
311
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800312void
313Dispatcher::sendStatusDatasetSegment(const Name& dataName, const Block& content,
314 time::milliseconds imsFresh, bool isFinalBlock)
315{
316 // the first segment will be sent to both places (the face and the in-memory storage)
317 // other segments will be inserted to the in-memory storage only
318 auto destination = SendDestination::IMS;
319 if (dataName[-1].toSegment() == 0) {
320 destination = SendDestination::FACE_AND_IMS;
321 }
322
323 MetaInfo metaInfo;
324 if (isFinalBlock) {
325 metaInfo.setFinalBlockId(dataName[-1]);
326 }
327
328 sendData(dataName, content, metaInfo, destination, imsFresh);
329}
330
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700331PostNotification
332Dispatcher::addNotificationStream(const PartialName& relPrefix)
333{
334 if (!m_topLevelPrefixes.empty()) {
Junxiao Shi43a79322016-08-08 05:48:43 +0000335 BOOST_THROW_EXCEPTION(std::domain_error("one or more top-level prefix has been added"));
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700336 }
337
338 if (isOverlappedWithOthers(relPrefix)) {
Junxiao Shi43a79322016-08-08 05:48:43 +0000339 BOOST_THROW_EXCEPTION(std::out_of_range("relPrefix overlaps with another relPrefix"));
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700340 }
341
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800342 // keep silent if Interest does not match a stored notification
343 InterestHandler missContinuation = bind([]{});
344
345 // register a handler for the subscriber of this notification stream
346 m_handlers[relPrefix] = bind(&Dispatcher::queryStorage, this, _1, _2, missContinuation);
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700347 m_streams[relPrefix] = 0;
348 return bind(&Dispatcher::postNotification, this, _1, relPrefix);
349}
350
351void
352Dispatcher::postNotification(const Block& notification, const PartialName& relPrefix)
353{
354 if (m_topLevelPrefixes.empty() || m_topLevelPrefixes.size() > 1) {
Junxiao Shi43a79322016-08-08 05:48:43 +0000355 NDN_LOG_WARN("postNotification: no top-level prefix or too many top-level prefixes");
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700356 return;
357 }
358
359 Name streamName(m_topLevelPrefixes.begin()->second.topPrefix);
360 streamName.append(relPrefix);
361 streamName.appendSequenceNumber(m_streams[streamName]++);
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800362
Junxiao Shi43a79322016-08-08 05:48:43 +0000363 // notification is sent out by the face after inserting into the in-memory storage,
Yanbiao Li4b4f7542016-03-11 02:04:43 +0800364 // because a request may be pending in the PIT
365 sendData(streamName, notification, MetaInfo(), SendDestination::FACE_AND_IMS,
366 DEFAULT_FRESHNESS_PERIOD);
Yanbiao Li8ee37ed2015-05-19 12:44:04 -0700367}
368
369} // namespace mgmt
370} // namespace ndn