blob: 017711ac814f101018ac0d559d37dbab129b935a [file] [log] [blame]
Alexander Afanasyevc169a812014-05-20 20:37:29 -04001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Jeff Thompson47eecfc2013-07-07 22:56:46 -07002/**
Alexander Afanasyev74633892015-02-08 18:08:46 -08003 * Copyright (c) 2013-2015 Regents of the University of California.
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -07004 *
5 * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -07006 *
Alexander Afanasyevc169a812014-05-20 20:37:29 -04007 * 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.
10 *
11 * 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.
14 *
15 * 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.
Jeff Thompsonb7f95562013-07-03 18:36:42 -070020 */
21
Alexander Afanasyev09c613f2014-01-29 00:23:58 -080022#include "interest.hpp"
Alexander Afanasyeve9fdb802014-02-05 17:36:51 -080023#include "util/random.hpp"
Junxiao Shi42c23622014-07-03 00:55:11 -070024#include "util/crypto.hpp"
Junxiao Shic2b8d242014-11-04 08:35:29 -070025#include "util/concepts.hpp"
Junxiao Shiaf8eeea2014-03-31 20:10:56 -070026#include "data.hpp"
Alexander Afanasyev840139f2013-12-28 15:02:50 -080027
Jeff Thompsonb7f95562013-07-03 18:36:42 -070028namespace ndn {
Alexander Afanasyev84681982014-01-03 13:26:09 -080029
Junxiao Shic2b8d242014-11-04 08:35:29 -070030BOOST_CONCEPT_ASSERT((boost::EqualityComparable<Interest>));
31BOOST_CONCEPT_ASSERT((WireEncodable<Interest>));
32BOOST_CONCEPT_ASSERT((WireDecodable<Interest>));
33static_assert(std::is_base_of<tlv::Error, Interest::Error>::value,
34 "Interest::Error must inherit from tlv::Error");
35
Junxiao Shi2af905b2014-11-27 13:10:54 -070036Interest::Interest()
37 : m_scope(-1)
38 , m_interestLifetime(time::milliseconds::min())
Spyridon Mastorakisc8188b32015-04-18 18:33:38 -070039 , m_selectedDelegationIndex(INVALID_SELECTED_DELEGATION_INDEX)
Junxiao Shi2af905b2014-11-27 13:10:54 -070040{
41}
42
43Interest::Interest(const Name& name)
44 : m_name(name)
45 , m_scope(-1)
46 , m_interestLifetime(time::milliseconds::min())
Spyridon Mastorakisc8188b32015-04-18 18:33:38 -070047 , m_selectedDelegationIndex(INVALID_SELECTED_DELEGATION_INDEX)
Junxiao Shi2af905b2014-11-27 13:10:54 -070048{
49}
50
51Interest::Interest(const Name& name, const time::milliseconds& interestLifetime)
52 : m_name(name)
53 , m_scope(-1)
54 , m_interestLifetime(interestLifetime)
Spyridon Mastorakisc8188b32015-04-18 18:33:38 -070055 , m_selectedDelegationIndex(INVALID_SELECTED_DELEGATION_INDEX)
Junxiao Shi2af905b2014-11-27 13:10:54 -070056{
57}
58
59Interest::Interest(const Name& name,
60 const Selectors& selectors,
61 int scope,
62 const time::milliseconds& interestLifetime,
63 uint32_t nonce)
64 : m_name(name)
65 , m_selectors(selectors)
66 , m_scope(scope)
67 , m_interestLifetime(interestLifetime)
Spyridon Mastorakisc8188b32015-04-18 18:33:38 -070068 , m_selectedDelegationIndex(INVALID_SELECTED_DELEGATION_INDEX)
Junxiao Shi2af905b2014-11-27 13:10:54 -070069{
70 if (nonce > 0) {
71 setNonce(nonce);
72 }
73}
74
75Interest::Interest(const Block& wire)
76{
77 wireDecode(wire);
78}
79
Alexander Afanasyeve881e932014-06-08 14:47:03 +030080uint32_t
Alexander Afanasyev840139f2013-12-28 15:02:50 -080081Interest::getNonce() const
82{
Alexander Afanasyeve881e932014-06-08 14:47:03 +030083 if (!m_nonce.hasWire())
84 const_cast<Interest*>(this)->setNonce(random::generateWord32());
Alexander Afanasyev840139f2013-12-28 15:02:50 -080085
Alexander Afanasyeve881e932014-06-08 14:47:03 +030086 if (m_nonce.value_size() == sizeof(uint32_t))
87 return *reinterpret_cast<const uint32_t*>(m_nonce.value());
88 else {
89 // for compatibility reasons. Should be removed eventually
90 return readNonNegativeInteger(m_nonce);
91 }
Alexander Afanasyev840139f2013-12-28 15:02:50 -080092}
93
Alexander Afanasyeve881e932014-06-08 14:47:03 +030094Interest&
95Interest::setNonce(uint32_t nonce)
96{
97 if (m_wire.hasWire() && m_nonce.value_size() == sizeof(uint32_t)) {
98 std::memcpy(const_cast<uint8_t*>(m_nonce.value()), &nonce, sizeof(nonce));
99 }
100 else {
Steve DiBenedetto54ce6682014-07-22 13:22:57 -0600101 m_nonce = dataBlock(tlv::Nonce,
Alexander Afanasyeve881e932014-06-08 14:47:03 +0300102 reinterpret_cast<const uint8_t*>(&nonce),
103 sizeof(nonce));
104 m_wire.reset();
105 }
106 return *this;
107}
Alexander Afanasyev840139f2013-12-28 15:02:50 -0800108
Alexander Afanasyevc3932172014-07-10 18:53:56 -0700109void
110Interest::refreshNonce()
111{
112 if (!hasNonce())
113 return;
114
115 uint32_t oldNonce = getNonce();
116 uint32_t newNonce = oldNonce;
117 while (newNonce == oldNonce)
118 newNonce = random::generateWord32();
119
120 setNonce(newNonce);
121}
122
Alexander Afanasyev84681982014-01-03 13:26:09 -0800123bool
Alexander Afanasyevff2d08f2014-04-07 18:28:25 -0700124Interest::matchesName(const Name& name) const
Jeff Thompson25b4e612013-10-10 16:03:24 -0700125{
Alexander Afanasyev1dd95c52014-03-22 19:11:36 -0700126 if (name.size() < m_name.size())
127 return false;
128
Alexander Afanasyevc348f832014-02-17 16:35:17 -0800129 if (!m_name.isPrefixOf(name))
Alexander Afanasyev84681982014-01-03 13:26:09 -0800130 return false;
Alexander Afanasyevaa0e7da2014-03-17 14:37:33 -0700131
Alexander Afanasyevc348f832014-02-17 16:35:17 -0800132 if (getMinSuffixComponents() >= 0 &&
Alexander Afanasyev3b703102014-06-13 17:01:14 -0700133 // name must include implicit digest
134 !(name.size() - m_name.size() >= static_cast<size_t>(getMinSuffixComponents())))
Alexander Afanasyev84681982014-01-03 13:26:09 -0800135 return false;
136
Alexander Afanasyevc348f832014-02-17 16:35:17 -0800137 if (getMaxSuffixComponents() >= 0 &&
Alexander Afanasyev3b703102014-06-13 17:01:14 -0700138 // name must include implicit digest
139 !(name.size() - m_name.size() <= static_cast<size_t>(getMaxSuffixComponents())))
Alexander Afanasyev84681982014-01-03 13:26:09 -0800140 return false;
141
Alexander Afanasyev1dd95c52014-03-22 19:11:36 -0700142 if (!getExclude().empty() &&
143 name.size() > m_name.size() &&
Alexander Afanasyevc348f832014-02-17 16:35:17 -0800144 getExclude().isExcluded(name[m_name.size()]))
Alexander Afanasyev84681982014-01-03 13:26:09 -0800145 return false;
146
147 return true;
Jeff Thompson25b4e612013-10-10 16:03:24 -0700148}
149
Junxiao Shiaf8eeea2014-03-31 20:10:56 -0700150bool
151Interest::matchesData(const Data& data) const
152{
Junxiao Shi42c23622014-07-03 00:55:11 -0700153 size_t interestNameLength = m_name.size();
154 const Name& dataName = data.getName();
155 size_t fullNameLength = dataName.size() + 1;
156
157 // check MinSuffixComponents
158 bool hasMinSuffixComponents = getMinSuffixComponents() >= 0;
159 size_t minSuffixComponents = hasMinSuffixComponents ?
160 static_cast<size_t>(getMinSuffixComponents()) : 0;
161 if (!(interestNameLength + minSuffixComponents <= fullNameLength))
Junxiao Shiaf8eeea2014-03-31 20:10:56 -0700162 return false;
Junxiao Shi42c23622014-07-03 00:55:11 -0700163
164 // check MaxSuffixComponents
165 bool hasMaxSuffixComponents = getMaxSuffixComponents() >= 0;
166 if (hasMaxSuffixComponents &&
167 !(interestNameLength + getMaxSuffixComponents() >= fullNameLength))
168 return false;
169
170 // check prefix
171 if (interestNameLength == fullNameLength) {
Alexander Afanasyev56860f52014-11-07 11:51:17 -0800172 if (m_name.get(-1).isImplicitSha256Digest()) {
173 if (m_name != data.getFullName())
Junxiao Shi42c23622014-07-03 00:55:11 -0700174 return false;
175 }
176 else {
177 // Interest Name is same length as Data full Name, but last component isn't digest
178 // so there's no possibility of matching
179 return false;
180 }
181 }
182 else {
183 // Interest Name is a strict prefix of Data full Name
184 if (!m_name.isPrefixOf(dataName))
185 return false;
Junxiao Shiaf8eeea2014-03-31 20:10:56 -0700186 }
187
Junxiao Shi42c23622014-07-03 00:55:11 -0700188 // check Exclude
189 // Exclude won't be violated if Interest Name is same as Data full Name
190 if (!getExclude().empty() && fullNameLength > interestNameLength) {
191 if (interestNameLength == fullNameLength - 1) {
192 // component to exclude is the digest
193 if (getExclude().isExcluded(data.getFullName().get(interestNameLength)))
194 return false;
195 // There's opportunity to inspect the Exclude filter and determine whether
196 // the digest would make a difference.
Junxiao Shi08d07082014-12-03 11:31:44 -0700197 // eg. "<NameComponent>AA</NameComponent><Any/>" doesn't exclude any digest -
198 // fullName not needed;
199 // "<Any/><NameComponent>AA</NameComponent>" and
200 // "<Any/><ImplicitSha256DigestComponent>ffffffffffffffffffffffffffffffff
201 // </ImplicitSha256DigestComponent>"
202 // excludes all digests - fullName not needed;
203 // "<Any/><ImplicitSha256DigestComponent>80000000000000000000000000000000
204 // </ImplicitSha256DigestComponent>"
205 // excludes some digests - fullName required
Junxiao Shi42c23622014-07-03 00:55:11 -0700206 // But Interests that contain the exact Data Name before digest and also
207 // contain Exclude filter is too rare to optimize for, so we request
208 // fullName no mater what's in the Exclude filter.
209 }
210 else {
211 // component to exclude is not the digest
212 if (getExclude().isExcluded(dataName.get(interestNameLength)))
213 return false;
214 }
215 }
216
217 // check PublisherPublicKeyLocator
Junxiao Shiaf8eeea2014-03-31 20:10:56 -0700218 const KeyLocator& publisherPublicKeyLocator = this->getPublisherPublicKeyLocator();
219 if (!publisherPublicKeyLocator.empty()) {
220 const Signature& signature = data.getSignature();
221 const Block& signatureInfo = signature.getInfo();
Steve DiBenedetto54ce6682014-07-22 13:22:57 -0600222 Block::element_const_iterator it = signatureInfo.find(tlv::KeyLocator);
Junxiao Shiaf8eeea2014-03-31 20:10:56 -0700223 if (it == signatureInfo.elements_end()) {
224 return false;
225 }
226 if (publisherPublicKeyLocator.wireEncode() != *it) {
227 return false;
228 }
229 }
230
231 return true;
232}
233
Alexander Afanasyev74633892015-02-08 18:08:46 -0800234template<encoding::Tag TAG>
Alexander Afanasyev197e5652014-06-13 16:56:31 -0700235size_t
Alexander Afanasyev74633892015-02-08 18:08:46 -0800236Interest::wireEncode(EncodingImpl<TAG>& block) const
Alexander Afanasyev197e5652014-06-13 16:56:31 -0700237{
238 size_t totalLength = 0;
239
240 // Interest ::= INTEREST-TYPE TLV-LENGTH
241 // Name
242 // Selectors?
243 // Nonce
244 // Scope?
245 // InterestLifetime?
Spyridon Mastorakisc8188b32015-04-18 18:33:38 -0700246 // Link?
247 // SelectedDelegation?
Alexander Afanasyev197e5652014-06-13 16:56:31 -0700248
249 // (reverse encoding)
250
Spyridon Mastorakisc8188b32015-04-18 18:33:38 -0700251 if (hasLink()) {
252 if (hasSelectedDelegation()) {
253 totalLength += prependNonNegativeIntegerBlock(block,
254 tlv::SelectedDelegation,
255 m_selectedDelegationIndex);
256 }
257 totalLength += prependBlock(block, m_link);
258 }
259 else {
260 BOOST_ASSERT(!hasSelectedDelegation());
261 }
262
Alexander Afanasyev197e5652014-06-13 16:56:31 -0700263 // InterestLifetime
264 if (getInterestLifetime() >= time::milliseconds::zero() &&
265 getInterestLifetime() != DEFAULT_INTEREST_LIFETIME)
266 {
267 totalLength += prependNonNegativeIntegerBlock(block,
Steve DiBenedetto54ce6682014-07-22 13:22:57 -0600268 tlv::InterestLifetime,
Alexander Afanasyev197e5652014-06-13 16:56:31 -0700269 getInterestLifetime().count());
270 }
271
272 // Scope
273 if (getScope() >= 0)
274 {
Steve DiBenedetto54ce6682014-07-22 13:22:57 -0600275 totalLength += prependNonNegativeIntegerBlock(block, tlv::Scope, getScope());
Alexander Afanasyev197e5652014-06-13 16:56:31 -0700276 }
277
278 // Nonce
279 getNonce(); // to ensure that Nonce is properly set
280 totalLength += block.prependBlock(m_nonce);
281
282 // Selectors
283 if (hasSelectors())
284 {
285 totalLength += getSelectors().wireEncode(block);
286 }
287
288 // Name
289 totalLength += getName().wireEncode(block);
290
291 totalLength += block.prependVarNumber(totalLength);
Steve DiBenedetto54ce6682014-07-22 13:22:57 -0600292 totalLength += block.prependVarNumber(tlv::Interest);
Alexander Afanasyev197e5652014-06-13 16:56:31 -0700293 return totalLength;
294}
295
Alexander Afanasyev15f67312014-07-22 15:11:09 -0700296template size_t
Alexander Afanasyev74633892015-02-08 18:08:46 -0800297Interest::wireEncode<encoding::EncoderTag>(EncodingImpl<encoding::EncoderTag>& block) const;
Alexander Afanasyev15f67312014-07-22 15:11:09 -0700298
299template size_t
Alexander Afanasyev74633892015-02-08 18:08:46 -0800300Interest::wireEncode<encoding::EstimatorTag>(EncodingImpl<encoding::EstimatorTag>& block) const;
Alexander Afanasyev15f67312014-07-22 15:11:09 -0700301
Alexander Afanasyev197e5652014-06-13 16:56:31 -0700302const Block&
303Interest::wireEncode() const
304{
305 if (m_wire.hasWire())
306 return m_wire;
307
308 EncodingEstimator estimator;
309 size_t estimatedSize = wireEncode(estimator);
310
311 EncodingBuffer buffer(estimatedSize, 0);
312 wireEncode(buffer);
313
314 // to ensure that Nonce block points to the right memory location
315 const_cast<Interest*>(this)->wireDecode(buffer.block());
316
317 return m_wire;
318}
319
320void
321Interest::wireDecode(const Block& wire)
322{
323 m_wire = wire;
324 m_wire.parse();
325
326 // Interest ::= INTEREST-TYPE TLV-LENGTH
327 // Name
328 // Selectors?
329 // Nonce
330 // Scope?
331 // InterestLifetime?
Spyridon Mastorakisc8188b32015-04-18 18:33:38 -0700332 // Link?
333 // SelectedDelegation?
Alexander Afanasyev197e5652014-06-13 16:56:31 -0700334
Steve DiBenedetto54ce6682014-07-22 13:22:57 -0600335 if (m_wire.type() != tlv::Interest)
Junxiao Shic2b8d242014-11-04 08:35:29 -0700336 throw Error("Unexpected TLV number when decoding Interest");
Alexander Afanasyev197e5652014-06-13 16:56:31 -0700337
338 // Name
Steve DiBenedetto54ce6682014-07-22 13:22:57 -0600339 m_name.wireDecode(m_wire.get(tlv::Name));
Alexander Afanasyev197e5652014-06-13 16:56:31 -0700340
341 // Selectors
Steve DiBenedetto54ce6682014-07-22 13:22:57 -0600342 Block::element_const_iterator val = m_wire.find(tlv::Selectors);
Alexander Afanasyev197e5652014-06-13 16:56:31 -0700343 if (val != m_wire.elements_end())
344 {
345 m_selectors.wireDecode(*val);
346 }
347 else
348 m_selectors = Selectors();
349
350 // Nonce
Steve DiBenedetto54ce6682014-07-22 13:22:57 -0600351 m_nonce = m_wire.get(tlv::Nonce);
Alexander Afanasyev197e5652014-06-13 16:56:31 -0700352
353 // Scope
Steve DiBenedetto54ce6682014-07-22 13:22:57 -0600354 val = m_wire.find(tlv::Scope);
Alexander Afanasyev197e5652014-06-13 16:56:31 -0700355 if (val != m_wire.elements_end())
356 {
357 m_scope = readNonNegativeInteger(*val);
358 }
359 else
360 m_scope = -1;
361
362 // InterestLifetime
Steve DiBenedetto54ce6682014-07-22 13:22:57 -0600363 val = m_wire.find(tlv::InterestLifetime);
Alexander Afanasyev197e5652014-06-13 16:56:31 -0700364 if (val != m_wire.elements_end())
365 {
366 m_interestLifetime = time::milliseconds(readNonNegativeInteger(*val));
367 }
368 else
369 {
370 m_interestLifetime = DEFAULT_INTEREST_LIFETIME;
371 }
Spyridon Mastorakisc8188b32015-04-18 18:33:38 -0700372
373 // Link object
374 val = m_wire.find(tlv::Data);
375 if (val != m_wire.elements_end())
376 {
377 m_link = (*val);
378 }
379
380 // SelectedDelegation
381 val = m_wire.find(tlv::SelectedDelegation);
382 if (val != m_wire.elements_end()) {
383 if (!this->hasLink()) {
384 throw Error("Interest contains selectedDelegation, but no LINK object");
385 }
386 uint64_t selectedDelegation = readNonNegativeInteger(*val);
387 if (selectedDelegation < uint64_t(Link::countDelegationsFromWire(m_link))) {
388 m_selectedDelegationIndex = static_cast<size_t>(selectedDelegation);
389 }
390 else {
391 throw Error("Invalid selected delegation index when decoding Interest");
392 }
393 }
394}
395
396bool
397Interest::hasLink() const
398{
399 if (m_link.hasWire())
400 return true;
401 return false;
402}
403
404Link
405Interest::getLink() const
406{
407 if (hasLink())
408 {
409 return Link(m_link);
410 }
411 throw Error("There is no encapsulated link object");
412}
413
414void
415Interest::setLink(const Block& link)
416{
417 m_link = link;
418 if (!link.hasWire()) {
419 throw Error("The given link does not have a wire format");
420 }
421 m_wire.reset();
422 this->unsetSelectedDelegation();
423}
424
425void
426Interest::unsetLink()
427{
428 m_link.reset();
429 m_wire.reset();
430 this->unsetSelectedDelegation();
431}
432
433bool
434Interest::hasSelectedDelegation() const
435{
436 if (m_selectedDelegationIndex != INVALID_SELECTED_DELEGATION_INDEX)
437 {
438 return true;
439 }
440 return false;
441}
442
443Name
444Interest::getSelectedDelegation() const
445{
446 if (!hasSelectedDelegation()) {
447 throw Error("There is no encapsulated selected delegation");
448 }
449 return std::get<1>(Link::getDelegationFromWire(m_link, m_selectedDelegationIndex));
450}
451
452void
453Interest::setSelectedDelegation(const Name& delegationName)
454{
455 size_t delegationIndex = Link::findDelegationFromWire(m_link, delegationName);
456 if (delegationIndex != INVALID_SELECTED_DELEGATION_INDEX) {
457 m_selectedDelegationIndex = delegationIndex;
458 }
459 else {
460 throw std::invalid_argument("Invalid selected delegation name");
461 }
462 m_wire.reset();
463}
464
465void
466Interest::setSelectedDelegation(size_t delegationIndex)
467{
468 if (delegationIndex >= Link(m_link).getDelegations().size()) {
469 throw Error("Invalid selected delegation index");
470 }
471 m_selectedDelegationIndex = delegationIndex;
472 m_wire.reset();
473}
474
475void
476Interest::unsetSelectedDelegation()
477{
478 m_selectedDelegationIndex = INVALID_SELECTED_DELEGATION_INDEX;
479 m_wire.reset();
Alexander Afanasyev197e5652014-06-13 16:56:31 -0700480}
481
Alexander Afanasyevff2d08f2014-04-07 18:28:25 -0700482std::ostream&
483operator<<(std::ostream& os, const Interest& interest)
Jeff Thompsonfe556862013-07-09 13:52:55 -0700484{
Alexander Afanasyev84681982014-01-03 13:26:09 -0800485 os << interest.getName();
Jeff Thompsonfe556862013-07-09 13:52:55 -0700486
Alexander Afanasyev84681982014-01-03 13:26:09 -0800487 char delim = '?';
488
489 if (interest.getMinSuffixComponents() >= 0) {
490 os << delim << "ndn.MinSuffixComponents=" << interest.getMinSuffixComponents();
491 delim = '&';
Jeff Thompsonfe556862013-07-09 13:52:55 -0700492 }
Alexander Afanasyev84681982014-01-03 13:26:09 -0800493 if (interest.getMaxSuffixComponents() >= 0) {
494 os << delim << "ndn.MaxSuffixComponents=" << interest.getMaxSuffixComponents();
495 delim = '&';
Jeff Thompson37527d62013-08-21 11:15:54 -0700496 }
Alexander Afanasyev84681982014-01-03 13:26:09 -0800497 if (interest.getChildSelector() >= 0) {
498 os << delim << "ndn.ChildSelector=" << interest.getChildSelector();
499 delim = '&';
Jeff Thompson13e280b2013-12-03 13:12:23 -0800500 }
Alexander Afanasyev84681982014-01-03 13:26:09 -0800501 if (interest.getMustBeFresh()) {
502 os << delim << "ndn.MustBeFresh=" << interest.getMustBeFresh();
503 delim = '&';
Jeff Thompson13e280b2013-12-03 13:12:23 -0800504 }
Alexander Afanasyev84681982014-01-03 13:26:09 -0800505 if (interest.getScope() >= 0) {
506 os << delim << "ndn.Scope=" << interest.getScope();
507 delim = '&';
Jeff Thompson13e280b2013-12-03 13:12:23 -0800508 }
Alexander Afanasyevaa0e7da2014-03-17 14:37:33 -0700509 if (interest.getInterestLifetime() >= time::milliseconds::zero()
510 && interest.getInterestLifetime() != DEFAULT_INTEREST_LIFETIME) {
Alexander Afanasyeva0c5f832014-06-19 13:27:56 -0700511 os << delim << "ndn.InterestLifetime=" << interest.getInterestLifetime().count();
Alexander Afanasyev84681982014-01-03 13:26:09 -0800512 delim = '&';
513 }
514
Alexander Afanasyeve881e932014-06-08 14:47:03 +0300515 if (interest.hasNonce()) {
Alexander Afanasyev84681982014-01-03 13:26:09 -0800516 os << delim << "ndn.Nonce=" << interest.getNonce();
517 delim = '&';
518 }
519 if (!interest.getExclude().empty()) {
520 os << delim << "ndn.Exclude=" << interest.getExclude();
521 delim = '&';
522 }
523
524 return os;
Jeff Thompson13e280b2013-12-03 13:12:23 -0800525}
526
Junxiao Shi08d07082014-12-03 11:31:44 -0700527} // namespace ndn