blob: 050b92789a95ba08f07d1932be50dffaba09dc9d [file] [log] [blame]
Yanbiao Li8ee37ed2015-05-19 12:44:04 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (c) 2014-2015, Regents of the University of California,
4 * Arizona Board of Regents,
5 * Colorado State University,
6 * University Pierre & Marie Curie, Sorbonne University,
7 * Washington University in St. Louis,
8 * Beijing Institute of Technology,
9 * The University of Memphis.
10 *
11 * This file is part of NFD (Named Data Networking Forwarding Daemon).
12 * See AUTHORS.md for complete list of NFD authors and contributors.
13 *
14 * NFD is free software: you can redistribute it and/or modify it under the terms
15 * of the GNU General Public License as published by the Free Software Foundation,
16 * either version 3 of the License, or (at your option) any later version.
17 *
18 * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
19 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
20 * PURPOSE. See the GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License along with
23 * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
24 */
25
26#include "mgmt/dispatcher.hpp"
27#include "management/nfd-control-parameters.hpp"
28#include "util/dummy-client-face.hpp"
29
30#include "boost-test.hpp"
31#include "identity-management-fixture.hpp"
32#include "unit-tests/unit-test-time-fixture.hpp"
33#include "unit-tests/make-interest-data.hpp"
34
35namespace ndn {
36namespace mgmt {
37namespace tests {
38
39using namespace ndn::tests;
40
41BOOST_AUTO_TEST_SUITE(MgmtDispatcher)
42
43class DispatcherFixture : public UnitTestTimeFixture
44 , public security::IdentityManagementFixture
45{
46public:
47 DispatcherFixture()
48 : face(util::makeDummyClientFace(io, {true, true}))
49 , dispatcher(*face, m_keyChain, security::SigningInfo())
50 {
51 }
52
53public:
54 shared_ptr<util::DummyClientFace> face;
55 mgmt::Dispatcher dispatcher;
56};
57
58class VoidParameters : public mgmt::ControlParameters
59{
60public:
61 explicit
62 VoidParameters(const Block& wire)
63 {
64 wireDecode(wire);
65 }
66
67 virtual Block
68 wireEncode() const NDN_CXX_DECL_FINAL
69 {
70 return Block(128);
71 }
72
73 virtual void
74 wireDecode(const Block& wire) NDN_CXX_DECL_FINAL
75 {
76 if (wire.type() != 128)
77 throw tlv::Error("Expecting TLV type 128");
78 }
79};
80
81static Authorization
82makeTestAuthorization()
83{
84 return [] (const Name& prefix,
85 const Interest& interest,
86 const ControlParameters* params,
87 AcceptContinuation accept,
88 RejectContinuation reject) {
89 if (interest.getName()[-1] == name::Component("valid")) {
90 accept("");
91 }
92 else {
93 if (interest.getName()[-1] == name::Component("silent")) {
94 reject(RejectReply::SILENT);
95 }
96 else {
97 reject(RejectReply::STATUS403);
98 }
99 }
100 };
101}
102
103BOOST_FIXTURE_TEST_CASE(BasicUsageSemantics, DispatcherFixture)
104{
105 BOOST_CHECK_NO_THROW(dispatcher
106 .addControlCommand<VoidParameters>("test/1", makeAcceptAllAuthorization(),
107 bind([] { return true; }),
108 bind([]{})));
109 BOOST_CHECK_NO_THROW(dispatcher
110 .addControlCommand<VoidParameters>("test/2", makeAcceptAllAuthorization(),
111 bind([] { return true; }),
112 bind([]{})));
113
114 BOOST_CHECK_THROW(dispatcher
115 .addControlCommand<VoidParameters>("test", makeAcceptAllAuthorization(),
116 bind([] { return true; }),
117 bind([]{})),
118 std::out_of_range);
119
120 BOOST_CHECK_NO_THROW(dispatcher.addStatusDataset("status/1",
121 makeAcceptAllAuthorization(), bind([]{})));
122 BOOST_CHECK_NO_THROW(dispatcher.addStatusDataset("status/2",
123 makeAcceptAllAuthorization(), bind([]{})));
124 BOOST_CHECK_THROW(dispatcher.addStatusDataset("status",
125 makeAcceptAllAuthorization(), bind([]{})),
126 std::out_of_range);
127
128 BOOST_CHECK_NO_THROW(dispatcher.addNotificationStream("stream/1"));
129 BOOST_CHECK_NO_THROW(dispatcher.addNotificationStream("stream/2"));
130 BOOST_CHECK_THROW(dispatcher.addNotificationStream("stream"), std::out_of_range);
131
132
133 BOOST_CHECK_NO_THROW(dispatcher.addTopPrefix("/root/1"));
134 BOOST_CHECK_NO_THROW(dispatcher.addTopPrefix("/root/2"));
135 BOOST_CHECK_THROW(dispatcher.addTopPrefix("/root"), std::out_of_range);
136
137 BOOST_CHECK_THROW(dispatcher
138 .addControlCommand<VoidParameters>("test/3", makeAcceptAllAuthorization(),
139 bind([] { return true; }),
140 bind([]{})),
141 std::domain_error);
142
143 BOOST_CHECK_THROW(dispatcher.addStatusDataset("status/3",
144 makeAcceptAllAuthorization(), bind([]{})),
145 std::domain_error);
146
147 BOOST_CHECK_THROW(dispatcher.addNotificationStream("stream/3"), std::domain_error);
148}
149
150BOOST_FIXTURE_TEST_CASE(AddRemoveTopPrefix, DispatcherFixture)
151{
152 std::map<std::string, size_t> nCallbackCalled;
153 dispatcher
154 .addControlCommand<VoidParameters>("test/1", makeAcceptAllAuthorization(),
155 bind([] { return true; }),
156 bind([&nCallbackCalled] { ++nCallbackCalled["test/1"]; }));
157
158 dispatcher
159 .addControlCommand<VoidParameters>("test/2", makeAcceptAllAuthorization(),
160 bind([] { return true; }),
161 bind([&nCallbackCalled] { ++nCallbackCalled["test/2"]; }));
162
163 face->receive(*util::makeInterest("/root/1/test/1/%80%00"));
164 advanceClocks(time::milliseconds(1));
165 BOOST_CHECK_EQUAL(nCallbackCalled["test/1"], 0);
166 BOOST_CHECK_EQUAL(nCallbackCalled["test/2"], 0);
167
168 dispatcher.addTopPrefix("/root/1");
169 advanceClocks(time::milliseconds(1));
170
171 face->receive(*util::makeInterest("/root/1/test/1/%80%00"));
172 advanceClocks(time::milliseconds(1));
173 BOOST_CHECK_EQUAL(nCallbackCalled["test/1"], 1);
174 BOOST_CHECK_EQUAL(nCallbackCalled["test/2"], 0);
175
176 face->receive(*util::makeInterest("/root/1/test/2/%80%00"));
177 advanceClocks(time::milliseconds(1));
178 BOOST_CHECK_EQUAL(nCallbackCalled["test/1"], 1);
179 BOOST_CHECK_EQUAL(nCallbackCalled["test/2"], 1);
180
181 face->receive(*util::makeInterest("/root/2/test/1/%80%00"));
182 face->receive(*util::makeInterest("/root/2/test/2/%80%00"));
183 advanceClocks(time::milliseconds(1));
184 BOOST_CHECK_EQUAL(nCallbackCalled["test/1"], 1);
185 BOOST_CHECK_EQUAL(nCallbackCalled["test/2"], 1);
186
187 dispatcher.addTopPrefix("/root/2");
188 advanceClocks(time::milliseconds(1));
189
190 face->receive(*util::makeInterest("/root/1/test/1/%80%00"));
191 advanceClocks(time::milliseconds(1));
192 BOOST_CHECK_EQUAL(nCallbackCalled["test/1"], 2);
193
194 face->receive(*util::makeInterest("/root/2/test/1/%80%00"));
195 advanceClocks(time::milliseconds(1));
196 BOOST_CHECK_EQUAL(nCallbackCalled["test/1"], 3);
197
198 dispatcher.removeTopPrefix("/root/1");
199 advanceClocks(time::milliseconds(1));
200
201 face->receive(*util::makeInterest("/root/1/test/1/%80%00"));
202 advanceClocks(time::milliseconds(1));
203 BOOST_CHECK_EQUAL(nCallbackCalled["test/1"], 3);
204
205 face->receive(*util::makeInterest("/root/2/test/1/%80%00"));
206 advanceClocks(time::milliseconds(1));
207 BOOST_CHECK_EQUAL(nCallbackCalled["test/1"], 4);
208}
209
210BOOST_FIXTURE_TEST_CASE(ControlCommand, DispatcherFixture)
211{
212 size_t nCallbackCalled = 0;
213 dispatcher
214 .addControlCommand<VoidParameters>("test",
215 makeTestAuthorization(),
216 bind([] { return true; }),
217 bind([&nCallbackCalled] { ++nCallbackCalled; }));
218
219 dispatcher.addTopPrefix("/root");
220 advanceClocks(time::milliseconds(1));
221 face->sentDatas.clear();
222
223 face->receive(*util::makeInterest("/root/test/%80%00")); // returns 403
224 face->receive(*util::makeInterest("/root/test/%80%00/invalid")); // returns 403
225 face->receive(*util::makeInterest("/root/test/%80%00/silent")); // silently ignored
226 face->receive(*util::makeInterest("/root/test/.../invalid")); // silently ignored (wrong format)
227 face->receive(*util::makeInterest("/root/test/.../valid")); // silently ignored (wrong format)
228 advanceClocks(time::milliseconds(1), 20);
229 BOOST_CHECK_EQUAL(nCallbackCalled, 0);
230 BOOST_CHECK_EQUAL(face->sentDatas.size(), 2);
231
232 BOOST_CHECK(face->sentDatas[0].getContentType() == tlv::ContentType_Blob);
233 BOOST_CHECK_EQUAL(ControlResponse(face->sentDatas[0].getContent().blockFromValue()).getCode(), 403);
234 BOOST_CHECK(face->sentDatas[1].getContentType() == tlv::ContentType_Blob);
235 BOOST_CHECK_EQUAL(ControlResponse(face->sentDatas[1].getContent().blockFromValue()).getCode(), 403);
236
237 face->receive(*util::makeInterest("/root/test/%80%00/valid"));
238 advanceClocks(time::milliseconds(1), 10);
239 BOOST_CHECK_EQUAL(nCallbackCalled, 1);
240}
241
242BOOST_FIXTURE_TEST_CASE(StatusDataset, DispatcherFixture)
243{
244 static Block smallBlock("\x81\x01\0x01", 3);
245 static Block largeBlock = [] () -> Block {
246 EncodingBuffer encoder;
247 for (size_t i = 0; i < 2500; ++i) {
248 encoder.prependByte(1);
249 }
250 encoder.prependVarNumber(2500);
251 encoder.prependVarNumber(129);
252 return encoder.block();
253 }();
254
255 dispatcher.addStatusDataset("test/small",
256 makeTestAuthorization(),
257 [] (const Name& prefix, const Interest& interest,
258 StatusDatasetContext context) {
259 context.append(smallBlock);
260 context.append(smallBlock);
261 context.append(smallBlock);
262 context.end();
263 });
264
265 dispatcher.addStatusDataset("test/large",
266 makeTestAuthorization(),
267 [] (const Name& prefix, const Interest& interest,
268 StatusDatasetContext context) {
269 context.append(largeBlock);
270 context.append(largeBlock);
271 context.append(largeBlock);
272 context.end();
273 });
274
275 dispatcher.addStatusDataset("test/reject",
276 makeTestAuthorization(),
277 [] (const Name& prefix, const Interest& interest,
278 StatusDatasetContext context) {
279 context.reject();
280 });
281
282 dispatcher.addTopPrefix("/root");
283 advanceClocks(time::milliseconds(1));
284 face->sentDatas.clear();
285
286 face->receive(*util::makeInterest("/root/test/small/%80%00")); // returns 403
287 face->receive(*util::makeInterest("/root/test/small/%80%00/invalid")); // returns 403
288 face->receive(*util::makeInterest("/root/test/small/%80%00/silent")); // silently ignored
289 advanceClocks(time::milliseconds(1), 20);
290 BOOST_CHECK_EQUAL(face->sentDatas.size(), 2);
291
292 BOOST_CHECK(face->sentDatas[0].getContentType() == tlv::ContentType_Blob);
293 BOOST_CHECK_EQUAL(ControlResponse(face->sentDatas[0].getContent().blockFromValue()).getCode(), 403);
294 BOOST_CHECK(face->sentDatas[1].getContentType() == tlv::ContentType_Blob);
295 BOOST_CHECK_EQUAL(ControlResponse(face->sentDatas[1].getContent().blockFromValue()).getCode(), 403);
296
297 face->sentDatas.clear();
298 face->receive(*util::makeInterest("/root/test/small/valid"));
299 advanceClocks(time::milliseconds(1), 10);
300 BOOST_CHECK_EQUAL(face->sentDatas.size(), 1);
301
302 face->receive(*util::makeInterest(Name("/root/test/small/valid").appendVersion(10))); // should be ignored
303 face->receive(*util::makeInterest(Name("/root/test/small/valid").appendSegment(20))); // should be ignored
304 advanceClocks(time::milliseconds(1), 10);
305 BOOST_CHECK_EQUAL(face->sentDatas.size(), 1);
306
307 Block content = face->sentDatas[0].getContent();
308 BOOST_CHECK_NO_THROW(content.parse());
309
310 BOOST_CHECK_EQUAL(content.elements().size(), 3);
311 BOOST_CHECK(content.elements()[0] == smallBlock);
312 BOOST_CHECK(content.elements()[1] == smallBlock);
313 BOOST_CHECK(content.elements()[2] == smallBlock);
314
315 face->sentDatas.clear();
316 face->receive(*util::makeInterest("/root/test/large/valid"));
317 advanceClocks(time::milliseconds(1), 10);
318 BOOST_CHECK_EQUAL(face->sentDatas.size(), 2);
319
320 const auto& datas = face->sentDatas;
321 content = [&datas] () -> Block {
322 EncodingBuffer encoder;
323 size_t valueLength = encoder.prependByteArray(datas[1].getContent().value(),
324 datas[1].getContent().value_size());
325 valueLength += encoder.prependByteArray(datas[0].getContent().value(),
326 datas[0].getContent().value_size());
327 encoder.prependVarNumber(valueLength);
328 encoder.prependVarNumber(tlv::Content);
329 return encoder.block();
330 }();
331
332 BOOST_CHECK_NO_THROW(content.parse());
333
334 BOOST_CHECK_EQUAL(content.elements().size(), 3);
335 BOOST_CHECK(content.elements()[0] == largeBlock);
336 BOOST_CHECK(content.elements()[1] == largeBlock);
337 BOOST_CHECK(content.elements()[2] == largeBlock);
338
339 face->sentDatas.clear();
340 face->receive(*util::makeInterest("/root/test/reject/%80%00/valid")); // returns nack
341 advanceClocks(time::milliseconds(1));
342 BOOST_CHECK_EQUAL(face->sentDatas.size(), 1);
343 BOOST_CHECK(face->sentDatas[0].getContentType() == tlv::ContentType_Nack);
344 BOOST_CHECK_EQUAL(ControlResponse(face->sentDatas[0].getContent().blockFromValue()).getCode(), 400);
345}
346
347BOOST_FIXTURE_TEST_CASE(NotificationStream, DispatcherFixture)
348{
349 static Block block("\x82\x01\x02", 3);
350
351 auto post = dispatcher.addNotificationStream("test");
352
353 post(block);
354 advanceClocks(time::milliseconds(1));
355 BOOST_CHECK_EQUAL(face->sentDatas.size(), 0);
356
357 dispatcher.addTopPrefix("/root");
358 advanceClocks(time::milliseconds(1));
359 face->sentDatas.clear();
360
361 post(block);
362 advanceClocks(time::milliseconds(1));
363 BOOST_CHECK_EQUAL(face->sentDatas.size(), 1);
364
365 post(block);
366 post(block);
367 post(block);
368 advanceClocks(time::milliseconds(1), 10);
369
370 BOOST_CHECK_EQUAL(face->sentDatas.size(), 4);
371 BOOST_CHECK_EQUAL(face->sentDatas[0].getName(), "/root/test/%FE%00");
372 BOOST_CHECK_EQUAL(face->sentDatas[1].getName(), "/root/test/%FE%01");
373 BOOST_CHECK_EQUAL(face->sentDatas[2].getName(), "/root/test/%FE%02");
374 BOOST_CHECK_EQUAL(face->sentDatas[3].getName(), "/root/test/%FE%03");
375
376 BOOST_CHECK(face->sentDatas[0].getContent().blockFromValue() == block);
377 BOOST_CHECK(face->sentDatas[1].getContent().blockFromValue() == block);
378 BOOST_CHECK(face->sentDatas[2].getContent().blockFromValue() == block);
379 BOOST_CHECK(face->sentDatas[3].getContent().blockFromValue() == block);
380}
381
382BOOST_AUTO_TEST_SUITE_END()
383
384} // namespace tests
385} // namespace mgmt
386} // namespace ndn