Increase the concurrency of DHT requests to 8.
[quix0rs-apt-p2p.git] / apt_p2p / apt_p2p_conf.py
index aaf20133aec6794c35d43f045047c4ec9cc7dccc..98beae7fa237014f39d2372019c6481cf82aaa93 100644 (file)
@@ -14,6 +14,7 @@ import os, sys
 from ConfigParser import SafeConfigParser
 
 from twisted.python import log, versions
+from twisted.trial import unittest
 
 class ConfigError(Exception):
     """Errors that occur in the loading of configuration variables."""
@@ -22,7 +23,7 @@ class ConfigError(Exception):
     def __str__(self):
         return repr(self.message)
 
-version = versions.Version('apt-p2p', 0, 0, 0)
+version = versions.Version('apt-p2p', 0, 1, 2)
 
 # Set the home parameter
 home = os.path.expandvars('${HOME}')
@@ -39,6 +40,16 @@ DEFAULTS = {
     # Port to listen on for all requests (TCP and UDP)
     'PORT': '9977',
     
+    # The rate to limit sending data to peers to, in KBytes/sec.
+    # Set this to 0 to not limit the upload bandwidth.
+    'UPLOAD_LIMIT': '0',
+
+    # The minimum number of peers before the mirror is not used.
+    # If there are fewer peers than this for a file, the mirror will also be
+    # used to speed up the download. Set to 0 to never use the mirror if
+    # there are peers.
+    'MIN_DOWNLOAD_PEERS': '3',
+
     # Directory to store the downloaded files in
     'CACHE_DIR': home + '/.apt-p2p/cache',
     
@@ -47,12 +58,12 @@ DEFAULTS = {
     #          for everybody to download
     'OTHER_DIRS': """""",
     
-    # User name to try and run as
-    'USERNAME': '',
-    
-    # Whether it's OK to use an IP addres from a known local/private range
+    # Whether it's OK to use an IP address from a known local/private range
     'LOCAL_OK': 'no',
 
+    # Whether a remote peer can access the statistics page
+    'REMOTE_STATS': 'yes',
+
     # Unload the packages cache after an interval of inactivity this long.
     # The packages cache uses a lot of memory, and only takes a few seconds
     # to reload when a new request arrives.
@@ -60,10 +71,13 @@ DEFAULTS = {
 
     # Refresh the DHT keys after this much time has passed.
     # This should be a time slightly less than the DHT's KEY_EXPIRE value.
-    'KEY_REFRESH': '57m',
+    'KEY_REFRESH': '2.5h',
 
+    # The user name to try and run as (leave blank to run as current user)
+    'USERNAME': 'apt-p2p',
+    
     # Which DHT implementation to use.
-    # It must be possile to do "from <DHT>.DHT import DHT" to get a class that
+    # It must be possible to do "from <DHT>.DHT import DHT" to get a class that
     # implements the IDHT interface.
     'DHT': 'apt_p2p_Khashmir',
 
@@ -73,24 +87,17 @@ DEFAULTS = {
 
 DHT_DEFAULTS = {
     # bootstrap nodes to contact to join the DHT
-    'BOOTSTRAP': """www.camrdale.org:9977
-        steveholt.hopto.org:9976""",
+    'BOOTSTRAP': """www.camrdale.org:9977""",
     
     # whether this node is a bootstrap node
     'BOOTSTRAP_NODE': "no",
     
-    # Kademlia "K" constant, this should be an even number
-    'K': '8',
-    
-    # SHA1 is 160 bits long
-    'HASH_LENGTH': '160',
-    
     # checkpoint every this many seconds
     'CHECKPOINT_INTERVAL': '5m', # five minutes
     
     ### SEARCHING/STORING
     # concurrent xmlrpc calls per find node/value request!
-    'CONCURRENT_REQS': '4',
+    'CONCURRENT_REQS': '8',
     
     # how many hosts to post to
     'STORE_REDUNDANCY': '3',
@@ -114,10 +121,21 @@ DHT_DEFAULTS = {
     'BUCKET_STALENESS': '1h', # one hour
     
     # expire entries older than this
-    'KEY_EXPIRE': '1h', # 60 minutes
+    'KEY_EXPIRE': '3h', # 3 hours
+    
+    # Timeout KRPC requests to nodes after this time.
+    'KRPC_TIMEOUT': '9s',
     
+    # KRPC requests are resent using exponential backoff starting with this delay.
+    # The request will first be resent after the delay set here.
+    # The request will be resent again after twice the delay set here. etc.
+    # e.g. if TIMEOUT is 9 sec., and INITIAL_DELAY is 2 sec., then requests will
+    # be resent at times 0, 2 (2 sec. later), and 6 (4 sec. later), and then will
+    # timeout at 9.
+    'KRPC_INITIAL_DELAY': '2s',
+
     # whether to spew info about the requests/responses in the protocol
-    'SPEW': 'yes',
+    'SPEW': 'no',
 }
 
 class AptP2PConfigParser(SafeConfigParser):
@@ -144,7 +162,7 @@ class AptP2PConfigParser(SafeConfigParser):
         if suffix in self.time_multipliers.keys():
             mult = self.time_multipliers[suffix]
             value = value[:-1]
-        return int(value)*mult
+        return int(float(value)*mult)
     
     def getstring(self, section, option):
         """Read the config parameter as a string."""
@@ -163,3 +181,54 @@ config = AptP2PConfigParser(DEFAULTS)
 config.add_section(config.get('DEFAULT', 'DHT'))
 for k in DHT_DEFAULTS:
     config.set(config.get('DEFAULT', 'DHT'), k, DHT_DEFAULTS[k])
+
+class TestConfigParser(unittest.TestCase):
+    """Unit tests for the config parser."""
+
+    def test_uppercase(self):
+        config.set('DEFAULT', 'case_tester', 'foo')
+        self.failUnless(config.get('DEFAULT', 'CASE_TESTER') == 'foo')
+        self.failUnless(config.get('DEFAULT', 'case_tester') == 'foo')
+        config.set('DEFAULT', 'TEST_CASE', 'bar')
+        self.failUnless(config.get('DEFAULT', 'TEST_CASE') == 'bar')
+        self.failUnless(config.get('DEFAULT', 'test_case') == 'bar')
+        config.set('DEFAULT', 'FINAL_test_CASE', 'foobar')
+        self.failUnless(config.get('DEFAULT', 'FINAL_TEST_CASE') == 'foobar')
+        self.failUnless(config.get('DEFAULT', 'final_test_case') == 'foobar')
+        self.failUnless(config.get('DEFAULT', 'FINAL_test_CASE') == 'foobar')
+        self.failUnless(config.get('DEFAULT', 'final_TEST_case') == 'foobar')
+    
+    def test_time(self):
+        config.set('DEFAULT', 'time_tester_1', '2d')
+        self.failUnless(config.gettime('DEFAULT', 'time_tester_1') == 2*86400)
+        config.set('DEFAULT', 'time_tester_2', '3h')
+        self.failUnless(config.gettime('DEFAULT', 'time_tester_2') == 3*3600)
+        config.set('DEFAULT', 'time_tester_3', '4m')
+        self.failUnless(config.gettime('DEFAULT', 'time_tester_3') == 4*60)
+        config.set('DEFAULT', 'time_tester_4', '37s')
+        self.failUnless(config.gettime('DEFAULT', 'time_tester_4') == 37)
+        
+    def test_floating_time(self):
+        config.set('DEFAULT', 'time_float_tester_1', '2.5d')
+        self.failUnless(config.gettime('DEFAULT', 'time_float_tester_1') == int(2.5*86400))
+        config.set('DEFAULT', 'time_float_tester_2', '0.5h')
+        self.failUnless(config.gettime('DEFAULT', 'time_float_tester_2') == int(0.5*3600))
+        config.set('DEFAULT', 'time_float_tester_3', '4.3333m')
+        self.failUnless(config.gettime('DEFAULT', 'time_float_tester_3') == int(4.3333*60))
+        
+    def test_string(self):
+        config.set('DEFAULT', 'string_test', 'foobar')
+        self.failUnless(type(config.getstring('DEFAULT', 'string_test')) == str)
+        self.failUnless(config.getstring('DEFAULT', 'string_test') == 'foobar')
+
+    def test_stringlist(self):
+        config.set('DEFAULT', 'stringlist_test', """foo
+        bar   
+        foobar  """)
+        l = config.getstringlist('DEFAULT', 'stringlist_test')
+        self.failUnless(type(l) == list)
+        self.failUnless(len(l) == 3)
+        self.failUnless(l[0] == 'foo')
+        self.failUnless(l[1] == 'bar')
+        self.failUnless(l[2] == 'foobar')
+        
\ No newline at end of file