From: Cameron Dale Date: Thu, 21 Feb 2008 22:27:21 +0000 (-0800) Subject: Use compact encoding of peer info for downloads. X-Git-Url: https://git.mxchange.org/?p=quix0rs-apt-p2p.git;a=commitdiff_plain;h=2995ddfcfe23b24e366156530b2207d434fb8344 Use compact encoding of peer info for downloads. --- diff --git a/TODO b/TODO index 3ebc8a8..a40cdaa 100644 --- a/TODO +++ b/TODO @@ -1,7 +1,6 @@ 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) diff --git a/apt_dht/PeerManager.py b/apt_dht/PeerManager.py index 16341c6..b561b8e 100644 --- a/apt_dht/PeerManager.py +++ b/apt_dht/PeerManager.py @@ -10,6 +10,7 @@ from twisted.web2 import stream as stream_mod from twisted.web2.http import splitHostPort from HTTPDownloader import HTTPClientManager +from util import uncompact class PeerManager: def __init__(self): @@ -22,9 +23,10 @@ class PeerManager: @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)) diff --git a/apt_dht/apt_dht.py b/apt_dht/apt_dht.py index f1732e5..25db818 100644 --- a/apt_dht/apt_dht.py +++ b/apt_dht/apt_dht.py @@ -15,7 +15,7 @@ from MirrorManager import MirrorManager from CacheManager import CacheManager from Hash import HashObject from db import DB -from util import findMyIPAddr +from util import findMyIPAddr, compact download_dir = 'cache' @@ -129,17 +129,17 @@ class AptDHT: 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) @@ -163,9 +163,10 @@ class AptDHT: 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 diff --git a/apt_dht/util.py b/apt_dht/util.py index df8d014..1b713bf 100644 --- a/apt_dht/util.py +++ b/apt_dht/util.py @@ -4,6 +4,7 @@ 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})|'+ @@ -109,3 +110,48 @@ def ipAddrFromChicken(): 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) diff --git a/apt_dht_Khashmir/DHT.py b/apt_dht_Khashmir/DHT.py index cdbd537..23b2755 100644 --- a/apt_dht_Khashmir/DHT.py +++ b/apt_dht_Khashmir/DHT.py @@ -10,6 +10,7 @@ from zope.interface import implements from apt_dht.interfaces import IDHT from khashmir import Khashmir +from bencode import bencode, bdecode khashmir_dir = 'apt-dht-Khashmir' @@ -139,7 +140,7 @@ class DHT: 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: @@ -157,21 +158,23 @@ class DHT: 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]