Comply with the newly defined protocol on the web page.
Various things need to done to comply with the newly defined protocol:
- - use the compact encoding of contact information
- add the token to find_node responses
- use the token in store_node requests
- standardize the error messages (especially for a bad token)
from twisted.web2.http import splitHostPort
from HTTPDownloader import HTTPClientManager
+from util import uncompact
class PeerManager:
def __init__(self):
@param peers: a list of the peers where the file can be found
"""
if peers:
- peer = choice(peers)
- log.msg('Downloading from peer %s' % peer)
- host, port = splitHostPort('http', peer)
+ compact_peer = choice(peers)
+ peer = uncompact(compact_peer['c'])
+ log.msg('Downloading from peer %r' % (peer, ))
+ host, port = peer
path = '/~/' + quote_plus(hash.expected())
else:
log.msg('Downloading (%s) from mirror %s' % (method, mirror))
from CacheManager import CacheManager
from Hash import HashObject
from db import DB
-from util import findMyIPAddr
+from util import findMyIPAddr, compact
download_dir = 'cache'
lookupDefer = self.dht.getValue(key)
lookupDefer.addCallback(self.lookupHash_done, hash, path, d)
- def lookupHash_done(self, locations, hash, path, d):
- if not locations:
+ def lookupHash_done(self, values, hash, path, d):
+ if not values:
log.msg('Peers for %s were not found' % path)
getDefer = self.peers.get(hash, path)
getDefer.addCallback(self.cache.save_file, hash, path)
getDefer.addErrback(self.cache.save_error, path)
getDefer.addCallbacks(d.callback, d.errback)
else:
- log.msg('Found peers for %s: %r' % (path, locations))
+ log.msg('Found peers for %s: %r' % (path, values))
# Download from the found peers
- getDefer = self.peers.get(hash, path, locations)
+ getDefer = self.peers.get(hash, path, values)
getDefer.addCallback(self.check_response, hash, path)
getDefer.addCallback(self.cache.save_file, hash, path)
getDefer.addErrback(self.cache.save_error, path)
self.mirrors.updatedFile(url, file_path)
if self.my_addr and hash and (hash.expected() is not None or forceDHT):
- site = self.my_addr + ':' + str(config.getint('DEFAULT', 'PORT'))
+ contact = compact(self.my_addr, config.getint('DEFAULT', 'PORT'))
+ value = {'c': contact}
key = hash.norm(bits = config.getint(config.get('DEFAULT', 'DHT'), 'HASH_LENGTH'))
- storeDefer = self.dht.storeValue(key, site)
+ storeDefer = self.dht.storeValue(key, value)
storeDefer.addCallback(self.store_done, hash)
return storeDefer
return None
import os, re
from twisted.python import log
+from twisted.trial import unittest
isLocal = re.compile('^(192\.168\.[0-9]{1,3}\.[0-9]{1,3})|'+
'(10\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})|'+
return current_ip
except Exception:
return []
+
+def uncompact(s):
+ """Extract the contatc info from a compact peer representation.
+
+ @type s: C{string}
+ @param s: the compact representation
+ @rtype: (C{string}, C{int})
+ @return: the IP address and port number to contact the peer on
+ @raise ValueError: if the compact representation doesn't exist
+ """
+ if (len(s) != 6):
+ raise ValueError
+ ip = '.'.join([str(ord(i)) for i in s[0:4]])
+ port = (ord(s[4]) << 8) | ord(s[5])
+ return (ip, port)
+
+def compact(ip, port):
+ """Create a compact representation of peer contact info.
+
+ @type ip: C{string}
+ @param ip: the IP address of the peer
+ @type port: C{int}
+ @param port: the port number to contact the peer on
+ @rtype: C{string}
+ @return: the compact representation
+ @raise ValueError: if the compact representation doesn't exist
+ """
+
+ s = ''.join([chr(int(i)) for i in ip.split('.')]) + \
+ chr((port & 0xFF00) >> 8) + chr(port & 0xFF)
+ if len(s) != 6:
+ raise ValueError
+ return s
+
+class TestUtil(unittest.TestCase):
+ """Tests for the utilities."""
+
+ timeout = 5
+ ip = '165.234.1.34'
+ port = 61234
+
+ def test_compact(self):
+ d = uncompact(compact(self.ip, self.port))
+ self.failUnlessEqual(d[0], self.ip)
+ self.failUnlessEqual(d[1], self.port)
from apt_dht.interfaces import IDHT
from khashmir import Khashmir
+from bencode import bencode, bdecode
khashmir_dir = 'apt-dht-Khashmir'
def _getValue(self, key, result):
if result:
- self.retrieved.setdefault(key, []).extend(result)
+ self.retrieved.setdefault(key, []).extend([bdecode(r) for r in result])
else:
final_result = []
if key in self.retrieved:
if not self.joined:
raise DHTError, "have not joined a network yet"
- if key in self.storing and value in self.storing[key]:
+ bvalue = bencode(value)
+
+ if key in self.storing and bvalue in self.storing[key]:
raise DHTError, "already storing that key with the same value"
d = defer.Deferred()
- self.khashmir.storeValueForKey(key, value, self._storeValue)
- self.storing.setdefault(key, {})[value] = d
+ self.khashmir.storeValueForKey(key, bvalue, self._storeValue)
+ self.storing.setdefault(key, {})[bvalue] = d
return d
- def _storeValue(self, key, value, result):
- if key in self.storing and value in self.storing[key]:
+ def _storeValue(self, key, bvalue, result):
+ if key in self.storing and bvalue in self.storing[key]:
if len(result) > 0:
- self.storing[key][value].callback(result)
+ self.storing[key][bvalue].callback(result)
else:
- self.storing[key][value].errback(DHTError('could not store value %s in key %s' % (value, key)))
- del self.storing[key][value]
+ self.storing[key][bvalue].errback(DHTError('could not store value %s in key %s' % (bvalue, key)))
+ del self.storing[key][bvalue]
if len(self.storing[key].keys()) == 0:
del self.storing[key]