[tor-commits] [stem/master] Implementing a use_directory_mirrors() method

atagar at torproject.org atagar at torproject.org
Mon Jul 22 03:10:17 UTC 2013


commit 4005a88becb68858ec4742455ea4d80e1ff9f827
Author: Damian Johnson <atagar at torproject.org>
Date:   Fri Jul 19 10:12:39 2013 -0700

    Implementing a use_directory_mirrors() method
    
    Method so we can balance load against directory mirrors rather than hammering
    the authorities. We can either request this during construction (which fails
    silently) or call this method explicitly (to get the exception).
---
 stem/descriptor/remote.py       |   37 ++++++++++++++++++++++++++++++++++++-
 test/integ/descriptor/remote.py |   14 ++++++++++++++
 2 files changed, 50 insertions(+), 1 deletion(-)

diff --git a/stem/descriptor/remote.py b/stem/descriptor/remote.py
index 66e6d85..9df09f4 100644
--- a/stem/descriptor/remote.py
+++ b/stem/descriptor/remote.py
@@ -48,6 +48,7 @@ import urllib2
 
 import stem.descriptor
 
+from stem import Flag
 from stem.util import log
 
 # Tor has a limit on the number of descriptors we can fetch explicitly by their
@@ -250,6 +251,9 @@ class DescriptorDownloader(object):
   :var int retries: number of times to attempt the request if it fails
   :var float timeout: duration before we'll time out our request, no timeout is
     applied if **None**
+  :var bool use_mirrors: downloads the present consensus and uses the directory
+    mirrors to fetch future requests, this fails silently if the consensus
+    cannot be downloaded
   :var bool start_when_requested: issues requests when our methods are called
     if **True**, otherwise provides non-running
     :class:`~stem.descriptor.remote.Query` instances
@@ -257,13 +261,42 @@ class DescriptorDownloader(object):
     request to a directory authority if **True**
   """
 
-  def __init__(self, retries = 2, fall_back_to_authority = True, timeout = None, start_when_requested = True):
+  def __init__(self, retries = 2, use_mirrors = False, fall_back_to_authority = True, timeout = None, start_when_requested = True):
     self.retries = retries
     self.timeout = timeout
     self.start_when_requested = start_when_requested
     self.fall_back_to_authority = fall_back_to_authority
     self._endpoints = DIRECTORY_AUTHORITIES.values()
 
+    if use_mirrors:
+      try:
+        start_time = time.time()
+        self.use_directory_mirrors()
+        log.debug("Retrieve directory mirrors (took %0.2fs)" % (time.time() - start_time))
+      except Exception, exc:
+        log.debug("Unable to retrieve directory mirrors: %s" % exc)
+
+  def use_directory_mirrors(self):
+    """
+    Downloads the present consensus and configures ourselves to use directory
+    mirrors, in addition to authorities.
+
+    :raises: **Exception** if unable to determine the directory mirrors
+    """
+
+    new_endpoints = set(DIRECTORY_AUTHORITIES.values())
+
+    query = self.get_consensus()
+    query.run()  # running explicitly so we'll raise errors
+
+    for desc in query:
+      if Flag.V2DIR in desc.flags:
+        new_endpoints.add((desc.address, desc.dir_port))
+
+    # we need our endpoints to be a list rather than set for random.choice()
+
+    self._endpoints = list(new_endpoints)
+
   def get_server_descriptors(self, fingerprints = None):
     """
     Provides the server descriptors with the given fingerprints. If no
@@ -351,6 +384,8 @@ class DescriptorDownloader(object):
     Issues a request for the given resource.
     """
 
+    log.trace("Retrieving descriptors (resource: %s, type: %s)" % (resource, descriptor_type))
+
     return Query(
       resource,
       descriptor_type,
diff --git a/test/integ/descriptor/remote.py b/test/integ/descriptor/remote.py
index 9273b61..a705486 100644
--- a/test/integ/descriptor/remote.py
+++ b/test/integ/descriptor/remote.py
@@ -49,6 +49,20 @@ class TestDescriptorReader(unittest.TestCase):
       self.assertEqual(1, len(descriptors))
       self.assertEqual('moria1', descriptors[0].nickname)
 
+  def test_use_directory_mirrors(self):
+    """
+    Checks that we can fetch and use a list of directory mirrors.
+    """
+
+    if test.runner.require_online(self):
+      return
+    elif test.runner.only_run_once(self, "test_use_directory_mirrors"):
+      return
+
+    downloader = stem.descriptor.remote.DescriptorDownloader()
+    downloader.use_directory_mirrors()
+    self.assertTrue(len(downloader._endpoints) > 50)
+
   def test_get_server_descriptors(self):
     """
     Exercises the downloader's get_server_descriptors() method.





More information about the tor-commits mailing list