class Iter: def __init__(self, prefix, impl): self.prefix = prefix if isinstance(self.prefix, bytes): self.prefix = self.prefix.decode("utf-8") if isinstance(self.prefix, str): self.prefix = Name(self.prefix) self.impl = impl def __next__(self): ret = next(self.impl) name = Name(ret.decode("utf-8")) if not self.prefix.isPrefixOf(name): raise StopIteration
class Namespace(object): def __init__(self, name): """ Create a Namespace object with the given name, and with no parent. This is the root of the name tree. To create child nodes, use myNamespace.getChild("foo") or myNamespace["foo"]. :param Name name: The name of this root node in the namespace. This makes a copy of the name. """ self._name = Name(name) self._parent = None # The dictionary key is a Name.Component. The value is the child Namespace. self._children = {} # The keys of _children in sorted order, kept in sync with _children. # (We don't use OrderedDict because it doesn't sort keys on insert.) self._sortedChildrenKeys = [] self._data = None self._content = Blob() self._face = None # The dictionary key is the callback ID. The value is the onNameAdded function. self._onNameAddedCallbacks = {} # The dictionary key is the callback ID. The value is the onContentSet function. self._onContentSetCallbacks = {} self._transformContent = None def getName(self): """ Get the name of this node in the name tree. This includes the name components of parent nodes. To get the name component of just this node, use getName()[-1]. :return: The name of this namespace. NOTE: You must not change the name - if you need to change it then make a copy. :rtype: Name """ return self._name def getParent(self): """ Get the parent namespace. :return: The parent namespace, or None if this is the root of the tree. :rtype: Namespace """ return self._parent def getRoot(self): """ Get the root namespace (which has no parent node). :return: The root namespace. :rtype: Namespace """ result = self while result._parent: result = result._parent return result def hasChild(self, component): """ Check if this node in the namespace has the given child. :param component: The name component of the child. :type component: Name.Component or value for the Name.Component constructor :return: True if this has a child with the name component. :rtype: bool """ if not isinstance(component, Name.Component): component = Name.Component(component) return component in self._children def getChild(self, nameOrComponent): """ Get a child (or descendant), creating it if needed. This is equivalent to namespace[component]. If a child is created, this calls callbacks as described by addOnNameAdded (but does not call the callbacks when creating intermediate nodes). :param nameOrComponent: If this is a Name, find or create the descendant node with the name (which must have this node's name as a prefix). Otherwise, this is the name component of the immediate child. :type nameOrComponent: Name or Name.Component or value for the Name.Component constructor :return: The child Namespace object. If nameOrComponent is a Name which equals the name of this Namespace, then just return this Namespace. :rtype: Namespace :raises RuntimeError: If the name of this Namespace node is not a prefix of the given Name. """ if isinstance(nameOrComponent, Name): descendantName = nameOrComponent if not self._name.isPrefixOf(descendantName): raise RuntimeError( "The name of this node is not a prefix of the descendant name" ) # Find or create the child node whose name equals the descendantName. # We know descendantNamespace is a prefix, so we can just go by # component count instead of a full compare. descendantNamespace = self while descendantNamespace._name.size() < descendantName.size(): nextComponent = descendantName[ descendantNamespace._name.size()] if nextComponent in descendantNamespace._children: descendantNamespace = descendantNamespace._children[ nextComponent] else: # Only fire the callbacks for the leaf node. isLeaf = (descendantNamespace._name.size() == descendantName.size() - 1) descendantNamespace = descendantNamespace._createChild( nextComponent, isLeaf) return descendantNamespace else: component = nameOrComponent if not isinstance(component, Name.Component): component = Name.Component(component) if component in self._children: return self._children[component] else: return self._createChild(component, True) def getChildComponents(self): """ Get a list of the name component of all child nodes. :return: A fresh sorted list of the name component of all child nodes. This remains the same if child nodes are added or deleted. :rtype: list of Name.Component """ return self._sortedChildrenKeys[:] def setData(self, data): """ Attach the Data packet to this Namespace. This calls callbacks as described by addOnContentSet. If a Data packet is already attached, do nothing. :param Data data: The Data packet object whose name must equal the name in this Namespace node. To get the right Namespace, you can use getChild(data.getName()). For efficiency, this does not copy the Data packet object. If your application may change the object later, then you must call setData with a copy of the object. :raises RuntimeError: If the Data packet name does not equal the name of this Namespace node. """ if self._data != None: # We already have an attached object. return if not data.name.equals(self._name): raise RuntimeError( "The Data packet name does not equal the name of this Namespace node." ) transformContent = self._getTransformContent() # TODO: TransformContent should take an OnError. if transformContent != None: transformContent(data, self._onContentTransformed) else: # Otherwise just invoke directly. self._onContentTransformed(data, data.content) def getData(self): """ Get the Data packet attached to this Namespace object. Note that getContent() may be different than the content in the attached Data packet (for example if the content is decrypted). To get the content, you should use getContent() instead of getData().getContent(). Also, the Data packet name is the same as the name of this Namespace node, so you can simply use getName() instead of getData().getName(). You should only use getData() to get other information such as the MetaInfo. :return: The Data packet object, or None if not set. :rtype: Data """ return self._data def getContent(self): """ Get the content attached to this Namespace object. Note that getContent() may be different than the content in the attached Data packet (for example if the content is decrypted). :return: The content Blob, or an isNull Blob if not set. :rtype: Blob """ return self._content def addOnNameAdded(self, onNameAdded): """ Add an onNameAdded callback. When a new name is added to this namespace at this node or any children, this calls onNameAdded as described below. :param onNameAdded: This calls onNameAdded(namespace, addedNamespace, callbackId) where namespace is this Namespace, addedNamespace is the Namespace of the added name, and callbackId is the callback ID returned by this method. NOTE: The library will log any exceptions raised by this callback, but for better error handling the callback should catch and properly handle any exceptions. :type onNameAdded: function object :return: The callback ID which you can use in removeCallback(). :rtype: int """ callbackId = Namespace.getNextCallbackId() self._onNameAddedCallbacks[callbackId] = onNameAdded return callbackId def addOnContentSet(self, onContentSet): """ Add an onContentSet callback. When the content has been set for this Namespace node or any children, this calls onContentSet as described below. :param onContentSet: This calls onContentSet(namespace, contentNamespace, callbackId) where namespace is this Namespace, contentNamespace is the Namespace where the content was set, and callbackId is the callback ID returned by this method. If you only care if the content has been set for this Namespace (and not any of its children) then your callback can check "if contentNamespace == namespace". To get the content or data packet, use contentNamespace.getContent() or contentNamespace.getData(). NOTE: The library will log any exceptions raised by this callback, but for better error handling the callback should catch and properly handle any exceptions. :type onContentSet: function object :return: The callback ID which you can use in removeCallback(). :rtype: int """ callbackId = Namespace.getNextCallbackId() self._onContentSetCallbacks[callbackId] = onContentSet return callbackId def setFace(self, face): """ Set the Face used when expressInterest is called on this or child nodes (unless a child node has a different Face). TODO: Replace this by a mechanism for requesting a Data object which is more general than a Face network operation. :param Face face: The Face object. If this Namespace object already has a Face object, it is replaced. """ self._face = face def expressInterest(self, interestTemplate=None): """ Call expressInterest on this (or a parent's) Face where the interest name is the name of this Namespace node. When the Data packet is received this calls setData, so you should use a callback with addOnContentSet. This uses ExponentialReExpress to re-express a timed-out interest with longer lifetimes. TODO: How to alert the application on a final interest timeout? TODO: Replace this by a mechanism for requesting a Data object which is more general than a Face network operation. :raises RuntimeError: If a Face object has not been set for this or a parent Namespace node. :param Interest interestTemplate: (optional) The interest template for expressInterest. If omitted, just use a default interest lifetime. """ face = self._getFace() if face == None: raise ValueError( "A Face object has not been set for this or a parent.") def onData(interest, data): self[data.name].setData(data) if interestTemplate == None: interestTemplate = Interest() interestTemplate.setInterestLifetimeMilliseconds(4000) face.expressInterest( self._name, interestTemplate, onData, ExponentialReExpress.makeOnTimeout(face, onData, None)) def _getFace(self): """ Get the Face set by setFace on this or a parent Namespace node. :return: The Face, or None if not set on this or any parent. :rtype: Face """ namespace = self while namespace != None: if namespace._face != None: return namespace._face namespace = namespace._parent return None def _getTransformContent(self): """ Get the TransformContent callback on this or a parent Namespace node. :return: The TransformContent callback, or None if not set on this or any parent. :rtype: function object """ namespace = self while namespace != None: if namespace._transformContent != None: return namespace._transformContent namespace = namespace._parent return None def removeCallback(self, callbackId): """ Remove the callback with the given callbackId. This does not search for the callbackId in child nodes. If the callbackId isn't found, do nothing. :param int callbackId: The callback ID returned, for example, from addOnNameAdded. """ self._onNameAddedCallbacks.pop(callbackId, None) self._onContentSetCallbacks.pop(callbackId, None) def __getitem__(self, key): """ Call self.getChild(key). """ if type(key) is slice: raise ValueError("Namespace[] does not support slices.") return self.getChild(key) def _createChild(self, component, fireCallbacks): """ Create the child with the given name component and add it to this namespace. This private method should only be called if the child does not already exist. The application should use getChild. :param component: The name component of the child. :type component: Name.Component or value for the Name.Component constructor :param fireCallbacks: If True, call _fireOnNameAdded for this and all parent nodes. If False, don't call callbacks (for example if creating intermediate nodes). :return: The child Namespace object. :rtype: Namespace """ child = Namespace(Name(self._name).append(component)) child._parent = self self._children[component] = child # Keep _sortedChildrenKeys synced with _children. bisect.insort(self._sortedChildrenKeys, component) if fireCallbacks: namespace = self while namespace: namespace._fireOnNameAdded(child) namespace = namespace._parent return child def _fireOnNameAdded(self, addedNamespace): # Copy the keys before iterating since callbacks can change the list. for id in list(self._onNameAddedCallbacks.keys()): # A callback on a previous pass may have removed this callback, so check. if id in self._onNameAddedCallbacks: try: self._onNameAddedCallbacks[id](self, addedNamespace, id) except: logging.exception("Error in onNameAdded") def _onContentTransformed(self, data, content): """ Set _data and _content to the given values and fire the OnContentSet callbacks. This may be called from a _transformContent handler invoked by setData. :param Data data: The Data packet object given to setData. :param Blob content: The content which may have been processed from the Data packet, e.g. by decrypting. """ self._data = data self._content = content # Fire callbacks. namespace = self while namespace: namespace._fireOnContentSet(self) namespace = namespace._parent def _fireOnContentSet(self, contentNamespace): # Copy the keys before iterating since callbacks can change the list. for id in list(self._onContentSetCallbacks.keys()): # A callback on a previous pass may have removed this callback, so check. if id in self._onContentSetCallbacks: try: self._onContentSetCallbacks[id](self, contentNamespace, id) except: logging.exception("Error in onContentSet") @staticmethod def getNextCallbackId(): """ Get the next unique callback ID. This uses a threading.Lock() to be thread safe. This is an internal method only meant to be called by library classes; the application should not call it. :return: The next callback ID. :rtype: int """ with Namespace._lastCallbackIdLock: Namespace._lastCallbackId += 1 return Namespace._lastCallbackId name = property(getName) parent = property(getParent) data = property(getData) content = property(getContent) _lastCallbackId = 0 _lastCallbackIdLock = threading.Lock()
def on_result_interest(self, _prefix, interest, face, _interest_filter_id, _filter_obj): # type: (Name, Interest, Face, int, InterestFilter) -> bool prefix = Name(SERVER_PREFIX).append(RESULT_PREFIX) if not prefix.isPrefixOf(interest.name): # Wrong prefix return False data_name = interest.name[prefix.size():] logging.info("On result interest: %s", data_name.toUri()) # key, stat = self._result_set_prefix_match(data_name) status = self.load_status(data_name) if status is None: # No such request self.nodata_reply(interest.name, RET_NO_REQUEST) return True if data_name[-1].isSegment(): # Segment no suffix seg_no = data_name[-1].toSegment() result = self.storage.get(data_name.getPrefix(-1)) elif data_name[-1] == Name("_meta")[0]: # MetaInfo suffix seg_no = -1 result = self.storage.get(data_name.getPrefix(-1)) else: # No suffix seg_no = None result = self.storage.get(data_name) if result is not None: # There are data segment_cnt = (len(result) + self.segment_size - 1) // self.segment_size # Note: I don't understand why namespace keep all data in memory metainfo = MetaInfo() # metainfo.setFinalBlockId(segment_cnt - 1) # WHY this doesn't work? metainfo.setFinalBlockId(Name().appendSegment(segment_cnt - 1)[0]) if segment_cnt > 1 and seg_no is None: # Fetch segmented data with no suffix will get only first segment seg_no = 0 data_name.appendSequenceNumber(seg_no) data = Data(Name(prefix).append(data_name)) data.setMetaInfo(metainfo) if seg_no == -1: # _meta data.content = self.storage.get(data_name) else: # data if segment_cnt > 1: # Segmented if seg_no < segment_cnt: start_offset = seg_no * self.segment_size end_offset = start_offset + self.segment_size data.content = Blob(bytearray(result[start_offset:end_offset])) else: data.content = None else: # No segmentation data.content = Blob(bytearray(result)) self.keychain.sign(data) face.putData(data) return True else: # Data are not ready if status.status == STATUS_NO_INPUT: self.nodata_reply(interest.name, RET_NO_INPUT) elif status.status == STATUS_FAILED: self.nodata_reply(interest.name, RET_EXECUTION_FAILED) else: self.nodata_reply(interest.name, RET_RETRY_AFTER, status.estimated_time - Common.getNowMilliseconds()) return True
class Namespace(object): def __init__(self, name): """ Create a Namespace object with the given name, and with no parent. This is the root of the name tree. To create child nodes, use myNamespace.getChild("foo") or myNamespace["foo"]. :param Name name: The name of this root node in the namespace. This makes a copy of the name. """ self._name = Name(name) self._parent = None # The dictionary key is a Name.Component. The value is the child Namespace. self._children = {} # The keys of _children in sorted order, kept in sync with _children. # (We don't use OrderedDict because it doesn't sort keys on insert.) self._sortedChildrenKeys = [] self._data = None self._content = None self._face = None # The dictionary key is the callback ID. The value is the onNameAdded function. self._onNameAddedCallbacks = {} # The dictionary key is the callback ID. The value is the onContentSet function. self._onContentSetCallbacks = {} self._transformContent = None def getName(self): """ Get the name of this node in the name tree. This includes the name components of parent nodes. To get the name component of just this node, use getName()[-1]. :return: The name of this namespace. NOTE: You must not change the name - if you need to change it then make a copy. :rtype: Name """ return self._name def getParent(self): """ Get the parent namespace. :return: The parent namespace, or None if this is the root of the tree. :rtype: Namespace """ return self._parent def getRoot(self): """ Get the root namespace (which has no parent node). :return: The root namespace. :rtype: Namespace """ result = self while result._parent: result = result._parent return result def hasChild(self, component): """ Check if this node in the namespace has the given child. :param component: The name component of the child. :type component: Name.Component or value for the Name.Component constructor :return: True if this has a child with the name component. :rtype: bool """ if not isinstance(component, Name.Component): component = Name.Component(component) return component in self._children def getChild(self, nameOrComponent): """ Get a child (or descendant), creating it if needed. This is equivalent to namespace[component]. If a child is created, this calls callbacks as described by addOnNameAdded (but does not call the callbacks when creating intermediate nodes). :param nameOrComponent: If this is a Name, find or create the descendant node with the name (which must have this node's name as a prefix). Otherwise, this is the name component of the immediate child. :type nameOrComponent: Name or Name.Component or value for the Name.Component constructor :return: The child Namespace object. If nameOrComponent is a Name which equals the name of this Namespace, then just return this Namespace. :rtype: Namespace :raises RuntimeError: If the name of this Namespace node is not a prefix of the given Name. """ if isinstance(nameOrComponent, Name): descendantName = nameOrComponent if not self._name.isPrefixOf(descendantName): raise RuntimeError( "The name of this node is not a prefix of the descendant name") # Find or create the child node whose name equals the descendantName. # We know descendantNamespace is a prefix, so we can just go by # component count instead of a full compare. descendantNamespace = self while descendantNamespace._name.size() < descendantName.size(): nextComponent = descendantName[descendantNamespace._name.size()] if nextComponent in descendantNamespace._children: descendantNamespace = descendantNamespace._children[nextComponent] else: # Only fire the callbacks for the leaf node. isLeaf = ( descendantNamespace._name.size() == descendantName.size() - 1) descendantNamespace = descendantNamespace._createChild( nextComponent, isLeaf) return descendantNamespace else: component = nameOrComponent if not isinstance(component, Name.Component): component = Name.Component(component) if component in self._children: return self._children[component] else: return self._createChild(component, True) def getChildComponents(self): """ Get a list of the name component of all child nodes. :return: A fresh sorted list of the name component of all child nodes. This remains the same if child nodes are added or deleted. :rtype: list of Name.Component """ return self._sortedChildrenKeys[:] def setData(self, data): """ Attach the Data packet to this Namespace. This calls callbacks as described by addOnContentSet. If a Data packet is already attached, do nothing. :param Data data: The Data packet object whose name must equal the name in this Namespace node. To get the right Namespace, you can use getChild(data.getName()). For efficiency, this does not copy the Data packet object. If your application may change the object later, then you must call setData with a copy of the object. :raises RuntimeError: If the Data packet name does not equal the name of this Namespace node. """ if self._data != None: # We already have an attached object. return if not data.name.equals(self._name): raise RuntimeError( "The Data packet name does not equal the name of this Namespace node.") transformContent = self._getTransformContent() # TODO: TransformContent should take an OnError. if transformContent != None: transformContent(data, self._onContentTransformed) else: # Otherwise just invoke directly. self._onContentTransformed(data, data.content) def getData(self): """ Get the Data packet attached to this Namespace object. Note that getContent() may be different than the content in the attached Data packet (for example if the content is decrypted). To get the content, you should use getContent() instead of getData().getContent(). Also, the Data packet name is the same as the name of this Namespace node, so you can simply use getName() instead of getData().getName(). You should only use getData() to get other information such as the MetaInfo. :return: The Data packet object, or None if not set. :rtype: Data """ return self._data def getContent(self): """ Get the content attached to this Namespace object. Note that getContent() may be different than the content in the attached Data packet (for example if the content is decrypted). :return: The content Blob, or None if not set. :rtype: Blob """ return self._content def addOnNameAdded(self, onNameAdded): """ Add an onNameAdded callback. When a new name is added to this namespace at this node or any children, this calls onNameAdded as described below. :param onNameAdded: This calls onNameAdded(namespace, addedNamespace, callbackId) where namespace is this Namespace, addedNamespace is the Namespace of the added name, and callbackId is the callback ID returned by this method. NOTE: The library will log any exceptions raised by this callback, but for better error handling the callback should catch and properly handle any exceptions. :type onNameAdded: function object :return: The callback ID which you can use in removeCallback(). :rtype: int """ callbackId = Namespace.getNextCallbackId() self._onNameAddedCallbacks[callbackId] = onNameAdded return callbackId def addOnContentSet(self, onContentSet): """ Add an onContentSet callback. When the content has been set for this Namespace node or any children and , this calls onContentSet as described below. :param onContentSet: This calls onContentSet(namespace, contentNamespace, callbackId) where namespace is this Namespace, contentNamespace is the Namespace where the content was set, and callbackId is the callback ID returned by this method. If you only care if the content has been set for this Namespace (and not any of its children) then your callback can check "if contentNamespace == namespace". To get the content or data packet, use contentNamespace.getContent() or contentNamespace.getData(). NOTE: The library will log any exceptions raised by this callback, but for better error handling the callback should catch and properly handle any exceptions. :type onContentSet: function object :return: The callback ID which you can use in removeCallback(). :rtype: int """ callbackId = Namespace.getNextCallbackId() self._onContentSetCallbacks[callbackId] = onContentSet return callbackId def setFace(self, face): """ Set the Face used when expressInterest is called on this or child nodes (unless a child node has a different Face). TODO: Replace this by a mechanism for requesting a Data object which is more general than a Face network operation. :param Face face: The Face object. If this Namespace object already has a Face object, it is replaced. """ self._face = face def expressInterest(self, interestTemplate = None): """ Call expressInterest on this (or a parent's) Face where the interest name is the name of this Namespace node. When the Data packet is received this calls setData, so you should use a callback with addOnContentSet. This uses ExponentialReExpress to re-express a timed-out interest with longer lifetimes. TODO: How to alert the application on a final interest timeout? TODO: Replace this by a mechanism for requesting a Data object which is more general than a Face network operation. :raises RuntimeError: If a Face object has not been set for this or a parent Namespace node. :param Interest interestTemplate: (optional) The interest template for expressInterest. If omitted, just use a default interest lifetime. """ face = self._getFace() if face == None: raise ValueError("A Face object has not been set for this or a parent.") def onData(interest, data): self[data.name].setData(data) if interestTemplate == None: interestTemplate = Interest() interestTemplate.setInterestLifetimeMilliseconds(4000) face.expressInterest( self._name, interestTemplate, onData, ExponentialReExpress.makeOnTimeout(face, onData, None)) def _getFace(self): """ Get the Face set by setFace on this or a parent Namespace node. :return: The Face, or None if not set on this or any parent. :rtype: Face """ namespace = self while namespace != None: if namespace._face != None: return namespace._face namespace = namespace._parent return None def _getTransformContent(self): """ Get the TransformContent callback on this or a parent Namespace node. :return: The TransformContent callback, or None if not set on this or any parent. :rtype: function object """ namespace = self while namespace != None: if namespace._transformContent != None: return namespace._transformContent namespace = namespace._parent return None def removeCallback(self, callbackId): """ Remove the callback with the given callbackId. This does not search for the callbackId in child nodes. If the callbackId isn't found, do nothing. :param int callbackId: The callback ID returned, for example, from addOnNameAdded. """ self._onNameAddedCallbacks.pop(callbackId, None) self._onContentSetCallbacks.pop(callbackId, None) def __getitem__(self, key): """ Call self.getChild(key). """ if type(key) is slice: raise ValueError("Namespace[] does not support slices.") return self.getChild(key) def _createChild(self, component, fireCallbacks): """ Create the child with the given name component and add it to this namespace. This private method should only be called if the child does not already exist. The application should use getChild. :param component: The name component of the child. :type component: Name.Component or value for the Name.Component constructor :return: The child Namespace object. :param fireCallbacks: If True, call _fireOnNameAdded for this and all parent nodes. If False, don't call callbacks (for example if creating intermediate nodes). :rtype: Namespace """ child = Namespace(Name(self._name).append(component)) child._parent = self self._children[component] = child # Keep _sortedChildrenKeys synced with _children. bisect.insort(self._sortedChildrenKeys, component) if fireCallbacks: namespace = self while namespace: namespace._fireOnNameAdded(child) namespace = namespace._parent return child def _fireOnNameAdded(self, addedNamespace): # Copy the keys before iterating since callbacks can change the list. for id in list(self._onNameAddedCallbacks.keys()): # A callback on a previous pass may have removed this callback, so check. if id in self._onNameAddedCallbacks: try: self._onNameAddedCallbacks[id](self, addedNamespace, id) except: logging.exception("Error in onNameAdded") def _onContentTransformed(self, data, content): """ Set _data and _content to the given values and fire the OnContentSet callbacks. This may be called from a _transformContent handler invoked by setData. :param Data data: The Data packet object given to setData. :param Blob content: The content which may have been processed from the Data packet, e.g. by decrypting. """ self._data = data self._content = content # Fire callbacks. namespace = self while namespace: namespace._fireOnContentSet(self) namespace = namespace._parent def _fireOnContentSet(self, contentNamespace): # Copy the keys before iterating since callbacks can change the list. for id in list(self._onContentSetCallbacks.keys()): # A callback on a previous pass may have removed this callback, so check. if id in self._onContentSetCallbacks: try: self._onContentSetCallbacks[id](self, contentNamespace, id) except: logging.exception("Error in onContentSet") @staticmethod def getNextCallbackId(): """ Get the next unique callback ID. This uses a threading.Lock() to be thread safe. This is an internal method only meant to be called by library classes; the application should not call it. :return: The next callback ID. :rtype: int """ with Namespace._lastCallbackIdLock: Namespace._lastCallbackId += 1 return Namespace._lastCallbackId name = property(getName) parent = property(getParent) data = property(getData) content = property(getContent) _lastCallbackId = 0 _lastCallbackIdLock = threading.Lock()