from twisted.python import log
from khash import intify
+from util import uncompact
class ActionBase:
""" base class for some long running asynchronous proccesses like finding nodes or values """
return
self.outstanding = self.outstanding - 1
self.answered[dict["id"]] = 1
- for node in l:
+ for compact_node in l:
+ node = uncompact(compact_node)
n = self.caller.Node(node)
if not self.found.has_key(n.id):
self.found[n.id] = n
# go through nodes
# if we have any closer than what we already got, query them
if dict.has_key('nodes'):
- for node in dict['nodes']:
+ for compact_node in dict['nodes']:
+ node = uncompact(compact_node)
n = self.caller.Node(node)
if not self.found.has_key(n.id):
self.found[n.id] = n
n = self.Node(id, _krpc_sender[0], _krpc_sender[1])
self.insertNode(n, contacted=0)
nodes = self.table.findNodes(target)
- nodes = map(lambda node: node.senderDict(), nodes)
+ nodes = map(lambda node: node.contactInfo(), nodes)
return {"nodes" : nodes, "id" : self.node.id}
return {'values' : l, "id": self.node.id}
else:
nodes = self.table.findNodes(key)
- nodes = map(lambda node: node.senderDict(), nodes)
+ nodes = map(lambda node: node.contactInfo(), nodes)
return {'nodes' : nodes, "id": self.node.id}
### provides a generic write method, you probably don't want to deploy something that allows
reactor.iterate()
reactor.iterate()
self.got = 0
- self.a.storeValueForKey(sha('foo').digest(), 'foobar', datetime.utcnow())
+ self.a.storeValueForKey(sha('foo').digest(), 'foobar')
reactor.iterate()
reactor.iterate()
reactor.iterate()
self.done = 0
def _scb(key, value, result):
self.done = 1
- self.l[randrange(0, self.num)].storeValueForKey(K, V, datetime.utcnow(), _scb)
+ self.l[randrange(0, self.num)].storeValueForKey(K, V, _scb)
while not self.done:
reactor.iterate()
class TestKTable(unittest.TestCase):
def setUp(self):
- self.a = Node(khash.newID(), 'localhost', 2002)
+ self.a = Node(khash.newID(), '127.0.0.1', 2002)
self.t = KTable(self.a, {'HASH_LENGTH': 160, 'K': 8, 'MAX_FAILURES': 3})
def testAddNode(self):
- self.b = Node(khash.newID(), 'localhost', 2003)
+ self.b = Node(khash.newID(), '127.0.0.1', 2003)
self.t.insertNode(self.b)
self.failUnlessEqual(len(self.t.buckets[0].l), 1)
self.failUnlessEqual(self.t.buckets[0].l[0], self.b)
from types import InstanceType
from twisted.trial import unittest
+from twisted.python import log
import khash
+from util import compact
# magic id to use before we know a peer's id
NULL_ID = 20 * '\0'
class Node:
"""encapsulate contact info"""
def __init__(self, id, host = None, port = None):
+ log.msg('New node: id=%r, host=%r, port=%r' % (id, host, port))
self.fails = 0
self.lastSeen = datetime(MINYEAR, 1, 1)
self.num = khash.intify(id)
self.host = host
self.port = int(port)
- self._senderDict = {'id': self.id, 'port' : self.port, 'host' : self.host}
+ self._contactInfo = None
def updateLastSeen(self):
self.lastSeen = datetime.now()
self.fails = self.fails + 1
return self.fails
- def senderDict(self):
- return self._senderDict
+ def contactInfo(self):
+ if self._contactInfo is None:
+ self._contactInfo = compact(self.id, self.host, self.port)
+ return self._contactInfo
def __repr__(self):
return `(self.id, self.host, self.port)`
class TestNode(unittest.TestCase):
def setUp(self):
- self.node = Node(khash.newID(), 'localhost', 2002)
+ self.node = Node(khash.newID(), '127.0.0.1', 2002)
def testUpdateLastSeen(self):
t = self.node.lastSeen
self.node.updateLastSeen()
## Copyright 2002-2003 Andrew Loewenstern, All Rights Reserved
# see LICENSE.txt for license information
+from twisted.trial import unittest
+
def bucket_stats(l):
"""given a list of khashmir instances, finds min, max, and average number of nodes in tables"""
max = avg = 0
avg = avg + c
avg = avg / len(l)
return {'min':min, 'max':max, 'avg':avg}
+
+def uncompact(s):
+ """Extract the contact info from a compact node representation.
+
+ @type s: C{string}
+ @param s: the compact representation
+ @rtype: C{dictionary}
+ @return: the node ID, IP address and port to contact the node on
+ @raise ValueError: if the compact representation doesn't exist
+ """
+ if (len(s) != 26):
+ raise ValueError
+ id = s[:20]
+ host = '.'.join([str(ord(i)) for i in s[20:24]])
+ port = (ord(s[24]) << 8) | ord(s[25])
+ return {'id': id, 'host': host, 'port': port}
+
+def compact(id, host, port):
+ """Create a compact representation of node contact info.
+
+ @type id: C{string}
+ @param id: the node ID
+ @type host: C{string}
+ @param host: the IP address of the node
+ @type port: C{int}
+ @param port: the port number to contact the node on
+ @rtype: C{string}
+ @return: the compact representation
+ @raise ValueError: if the compact representation doesn't exist
+ """
+
+ s = id + ''.join([chr(int(i)) for i in host.split('.')]) + \
+ chr((port & 0xFF00) >> 8) + chr(port & 0xFF)
+ if len(s) != 26:
+ raise ValueError
+ return s
+
+class TestUtil(unittest.TestCase):
+ """Tests for the utilities."""
+
+ timeout = 5
+ myid = '\xca\xec\xb8\x0c\x00\xe7\x07\xf8~])\x8f\x9d\xe5_B\xff\x1a\xc4!'
+ host = '165.234.1.34'
+ port = 61234
+
+ def test_compact(self):
+ d = uncompact(compact(self.myid, self.host, self.port))
+ self.failUnlessEqual(d['id'], self.myid)
+ self.failUnlessEqual(d['host'], self.host)
+ self.failUnlessEqual(d['port'], self.port)
+
\ No newline at end of file