# -*- Mode:python; c-file-style:"gnu"; indent-tabs-mode:nil -*- */
#
# Copyright (C) 2014-2016 Regents of the University of California.
# Author: Jeff Thompson <jefft0@remap.ucla.edu>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# A copy of the GNU Lesser General Public License is in the file COPYING.
"""
This module defines the NDN Interest class.
"""
from pyndn.encoding.wire_format import WireFormat
from pyndn.util.blob import Blob
from pyndn.util.signed_blob import SignedBlob
from pyndn.util.change_counter import ChangeCounter
from pyndn.name import Name
from pyndn.key_locator import KeyLocator
from pyndn.exclude import Exclude
[docs]class Interest(object):
def __init__(self, value = None):
if type(value) is Interest:
# Copy the values.
self._name = ChangeCounter(Name(value.getName()))
self._minSuffixComponents = value._minSuffixComponents
self._maxSuffixComponents = value._maxSuffixComponents
self._keyLocator = ChangeCounter(KeyLocator(value.getKeyLocator()))
self._exclude = ChangeCounter(Exclude(value.getExclude()))
self._childSelector = value._childSelector
self._mustBeFresh = value._mustBeFresh
self._nonce = value.getNonce()
self._interestLifetimeMilliseconds = value._interestLifetimeMilliseconds
self._defaultWireEncoding = value.getDefaultWireEncoding()
self._defaultWireEncodingFormat = value._defaultWireEncodingFormat
else:
self._name = ChangeCounter(Name(value) if type(value) is Name
else Name())
self._minSuffixComponents = None
self._maxSuffixComponents = None
self._keyLocator = ChangeCounter(KeyLocator())
self._exclude = ChangeCounter(Exclude())
self._childSelector = None
self._mustBeFresh = True
self._nonce = Blob()
self._interestLifetimeMilliseconds = None
self._defaultWireEncoding = SignedBlob()
self._defaultWireEncodingFormat = None
self._getNonceChangeCount = 0
self._getDefaultWireEncodingChangeCount = 0
self._changeCount = 0
[docs] def getName(self):
"""
Get the interest Name.
:return: The name. The name size() may be 0 if not specified.
:rtype: Name
"""
return self._name.get()
[docs] def getMinSuffixComponents(self):
"""
Get the min suffix components.
:return: The min suffix components, or None if not specified.
:rtype: int
"""
return self._minSuffixComponents
[docs] def getMaxSuffixComponents(self):
"""
Get the max suffix components.
:return: The max suffix components, or None if not specified.
:rtype: int
"""
return self._maxSuffixComponents
[docs] def getKeyLocator(self):
"""
Get the interest key locator.
:return: The key locator. If getType() is None, then the key locator
is not specified.
:rtype: KeyLocator
"""
return self._keyLocator.get()
[docs] def getExclude(self):
"""
Get the exclude object.
:return: The exclude object. If the exclude size() is zero, then
the exclude is not specified.
:rtype: Exclude
"""
return self._exclude.get()
[docs] def getChildSelector(self):
"""
Get the child selector.
:return: The child selector, or None if not specified.
:rtype: int
"""
return self._childSelector
[docs] def getMustBeFresh(self):
"""
Get the must be fresh flag.
:return: The must be fresh flag. If not specified, the default is
True.
:rtype: bool
"""
return self._mustBeFresh
[docs] def getNonce(self):
"""
Return the nonce value from the incoming interest. If you change any of
the fields in this Interest object, then the nonce value is cleared.
:return: The nonce. If isNull() then the nonce is omitted.
:rtype: Blob
"""
if self._getNonceChangeCount != self.getChangeCount():
# The values have changed, so the existing nonce is invalidated.
self._nonce = Blob()
self._getNonceChangeCount = self.getChangeCount()
return self._nonce
[docs] def getInterestLifetimeMilliseconds(self):
"""
Get the interest lifetime.
:return: The interest lifetime in milliseconds, or None if not specified.
:rtype: float
"""
return self._interestLifetimeMilliseconds
[docs] def setName(self, name):
"""
Set the interest name.
:note: You can also call getName and change the name values directly.
:param Name name: The interest name. This makes a copy of the name.
:return: This Interest so that you can chain calls to update values.
:rtype: Interest
"""
self._name.set(name if type(name) is Name else Name(name))
self._changeCount += 1
return self
[docs] def setMinSuffixComponents(self, minSuffixComponents):
"""
Set the min suffix components count.
:param int minSuffixComponents: The min suffix components count. If not
specified, set to None.
:return: This Interest so that you can chain calls to update values.
:rtype: Interest
"""
self._minSuffixComponents = minSuffixComponents
self._changeCount += 1
return self
[docs] def setMaxSuffixComponents(self, maxSuffixComponents):
"""
Set the max suffix components count.
:param int maxSuffixComponents: The max suffix components count. If not
specified, set to None.
:return: This Interest so that you can chain calls to update values.
:rtype: Interest
"""
self._maxSuffixComponents = maxSuffixComponents
self._changeCount += 1
return self
[docs] def setKeyLocator(self, keyLocator):
"""
Set this interest to use a copy of the given KeyLocator object.
:note: You can also call getKeyLocator and change the key locator directly.
:param KeyLocator keyLocator: The KeyLocator object. This makes a copy
of the object. If no key locator is specified, set to a new default
KeyLocator(), or to a KeyLocator with an unspecified type.
:return: This Interest so that you can chain calls to update values.
:rtype: Interest
"""
self._keyLocator.set(
KeyLocator(keyLocator) if type(keyLocator) is KeyLocator
else KeyLocator())
self._changeCount += 1
return self
[docs] def setExclude(self, exclude):
"""
Set this interest to use a copy of the given Exclude object.
:note: You can also call getExclude and change the exclude entries directly.
:param Exclude exclude: The Exclude object. This makes a copy of the
object. If no exclude is specified, set to a new default Exclude(), or
to an Exclude with size() 0.
:return: This Interest so that you can chain calls to update values.
:rtype: Interest
"""
self._exclude.set(
Exclude(exclude) if type(exclude) is Exclude else Exclude())
self._changeCount += 1
return self
[docs] def setChildSelector(self, childSelector):
"""
Set the child selector.
:param int childSelector: The child selector. If not specified, set to None.
:return: This Interest so that you can chain calls to update values.
:rtype: Interest
"""
self._childSelector = childSelector
self._changeCount += 1
return self
[docs] def setMustBeFresh(self, mustBeFresh):
"""
Set the MustBeFresh flag.
:param bool mustBeFresh: True if the content must be fresh, otherwise
False. If you do not set this flag, the default value is true.
:return: This Interest so that you can chain calls to update values.
:rtype: Interest
"""
self._mustBeFresh = True if mustBeFresh else False
self._changeCount += 1
return self
[docs] def setNonce(self, nonce):
"""
:deprecated: You should let the wire encoder generate a random nonce
internally before sending the interest.
"""
self._nonce = nonce if type(nonce) is Blob else Blob(nonce)
# Set _getNonceChangeCount so that the next call to getNonce() won't
# clear _nonce.
self._changeCount += 1
self._getNonceChangeCount = self.getChangeCount()
return self
[docs] def setInterestLifetimeMilliseconds(self, interestLifetimeMilliseconds):
"""
Set the interest lifetime.
:param float interestLifetimeMilliseconds: The interest lifetime in
milliseconds. If not specified, set to -1.
:return: This Interest so that you can chain calls to update values.
:rtype: Interest
"""
self._interestLifetimeMilliseconds = (None
if interestLifetimeMilliseconds == None
else float(interestLifetimeMilliseconds))
self._changeCount += 1
return self
[docs] def wireEncode(self, wireFormat = None):
"""
Encode this Interest for a particular wire format. If wireFormat is the
default wire format, also set the defaultWireEncoding field to the
encoded result.
:param wireFormat: (optional) A WireFormat object used to encode this
Interest. If omitted, use WireFormat.getDefaultWireFormat().
:type wireFormat: A subclass of WireFormat
:return: The encoded buffer.
:rtype: SignedBlob
"""
if wireFormat == None:
# Don't use a default argument since getDefaultWireFormat can change.
wireFormat = WireFormat.getDefaultWireFormat()
if (not self.getDefaultWireEncoding().isNull() and
self.getDefaultWireEncodingFormat() == wireFormat):
# We already have an encoding in the desired format.
return self.getDefaultWireEncoding()
(encoding, signedPortionBeginOffset, signedPortionEndOffset) = \
wireFormat.encodeInterest(self)
wireEncoding = SignedBlob(
encoding, signedPortionBeginOffset, signedPortionEndOffset)
if wireFormat == WireFormat.getDefaultWireFormat():
# This is the default wire encoding.
self._setDefaultWireEncoding(
wireEncoding, WireFormat.getDefaultWireFormat())
return wireEncoding
[docs] def wireDecode(self, input, wireFormat = None):
"""
Decode the input using a particular wire format and update this Interest.
If wireFormat is the default wire format, also set the
defaultWireEncoding to another pointer to the input.
:param input: The array with the bytes to decode. If input is not a
Blob, then copy the bytes to save the defaultWireEncoding (otherwise
take another pointer to the same Blob).
:type input: A Blob or an array type with int elements
:param wireFormat: (optional) A WireFormat object used to decode this
Interest. If omitted, use WireFormat.getDefaultWireFormat().
:type wireFormat: A subclass of WireFormat
"""
if wireFormat == None:
# Don't use a default argument since getDefaultWireFormat can change.
wireFormat = WireFormat.getDefaultWireFormat()
# If input is a Blob, get its buf().
decodeBuffer = input.buf() if isinstance(input, Blob) else input
(signedPortionBeginOffset, signedPortionEndOffset) = \
wireFormat.decodeInterest(self, decodeBuffer)
if wireFormat == WireFormat.getDefaultWireFormat():
# This is the default wire encoding. In the Blob constructor, set
# copy true, but if input is already a Blob, it won't copy.
self._setDefaultWireEncoding(SignedBlob(
Blob(input, True),
signedPortionBeginOffset, signedPortionEndOffset),
WireFormat.getDefaultWireFormat())
else:
self._setDefaultWireEncoding(SignedBlob(), None)
[docs] def toUri(self):
"""
Encode the name according to the "NDN URI Scheme". If there are
interest selectors, append "?" and add the selectors as a query string.
For example "/test/name?ndn.ChildSelector=1".
:note: This is an experimental feature. See the API docs for more detail at
http://named-data.net/doc/ndn-ccl-api/interest.html#interest-touri-method .
:return: The URI string.
:rtype: string
"""
selectors = ""
if self._minSuffixComponents != None:
selectors += "&ndn.MinSuffixComponents=" + repr(
self._minSuffixComponents)
if self._maxSuffixComponents != None:
selectors += "&ndn.MaxSuffixComponents=" + repr(
self._maxSuffixComponents)
if self._childSelector != None:
selectors += "&ndn.ChildSelector=" + repr(self._childSelector)
if self._mustBeFresh:
selectors += "&ndn.MustBeFresh=true"
if self._interestLifetimeMilliseconds != None:
selectors += "&ndn.InterestLifetime=" + repr(
int(round(self._interestLifetimeMilliseconds)))
if self.getNonce().size() > 0:
selectors += ("&ndn.Nonce=" +
Name.toEscapedString(self.getNonce().buf()))
if self.getExclude().size() > 0:
selectors += "&ndn.Exclude=" + self.getExclude().toUri()
result = self.getName().toUri()
if selectors != "":
# Replace the first & with ?.
result += "?" + selectors[1:]
return result
[docs] def matchesName(self, name):
"""
Check if this interest's name matches the given name (using Name.match)
and the given name also conforms to the interest selectors.
:param Name name: The name to check.
:return: True if the name and interest selectors match, False otherwise.
:rtype: bool
"""
if not self.getName().match(name):
return False
if (self._minSuffixComponents != None and
# Add 1 for the implicit digest.
not (name.size() + 1 - self.getName().size() >=
self._minSuffixComponents)):
return False
if (self._maxSuffixComponents != None and
# Add 1 for the implicit digest.
not (name.size() + 1 - self.getName().size() <=
self._maxSuffixComponents)):
return False
if (self.getExclude().size() > 0 and
name.size() > self.getName().size() and
self.getExclude().matches(name[self.getName().size()])):
return False
return True
[docs] def getDefaultWireEncoding(self):
"""
Return the default wire encoding, which was encoded with
getDefaultWireEncodingFormat().
:return: The default wire encoding, whose isNull() may be true if there
is no default wire encoding.
:rtype: SignedBlob
"""
if self._getDefaultWireEncodingChangeCount != self.getChangeCount():
# The values have changed, so the default wire encoding is
# invalidated.
self._defaultWireEncoding = SignedBlob()
self._defaultWireEncodingFormat = None
self._getDefaultWireEncodingChangeCount = self.getChangeCount()
return self._defaultWireEncoding
[docs] def getChangeCount(self):
"""
Get the change count, which is incremented each time this object
(or a child object) is changed.
:return: The change count.
:rtype: int
"""
# Make sure each of the checkChanged is called.
changed = self._name.checkChanged()
changed = self._keyLocator.checkChanged() or changed
changed = self._exclude.checkChanged() or changed
if changed:
# A child object has changed, so update the change count.
self._changeCount += 1
return self._changeCount
def _setDefaultWireEncoding(
self, defaultWireEncoding, defaultWireEncodingFormat):
self._defaultWireEncoding = defaultWireEncoding
self._defaultWireEncodingFormat = defaultWireEncodingFormat
# Set _getDefaultWireEncodingChangeCount so that the next call to
# getDefaultWireEncoding() won't clear _defaultWireEncoding.
self._getDefaultWireEncodingChangeCount = self.getChangeCount()
# Create managed properties for read/write properties of the class for more pythonic syntax.
name = property(getName, setName)
minSuffixComponents = property(getMinSuffixComponents, setMinSuffixComponents)
maxSuffixComponents = property(getMaxSuffixComponents, setMaxSuffixComponents)
keyLocator = property(getKeyLocator, setKeyLocator)
exclude = property(getExclude, setExclude)
childSelector = property(getChildSelector, setChildSelector)
mustBeFresh = property(getMustBeFresh, setMustBeFresh)
nonce = property(getNonce, setNonce)
interestLifetimeMilliseconds = property(getInterestLifetimeMilliseconds, setInterestLifetimeMilliseconds)