[tor-commits] [stem/master] Having create_hidden_service() provide a namedtuple

atagar at torproject.org atagar at torproject.org
Sat Dec 20 21:41:07 UTC 2014


commit 5ae9899ebe5251a46bae86ea3650ec337787dc85
Author: Damian Johnson <atagar at torproject.org>
Date:   Sun Dec 14 10:24:28 2014 -0800

    Having create_hidden_service() provide a namedtuple
    
    On reflection it's handy for this method to return more than just a
    maybe-read-maybe-not hostname. Instead providing back a namedtuple with the
    hidden service attributes. Extra advantage is that this can be used as a
    boolean to determine 'did we create or update a hidden service'.
---
 stem/control.py                  |   65 ++++++++++++++++++++++++--------------
 test/integ/control/controller.py |   12 ++++---
 2 files changed, 48 insertions(+), 29 deletions(-)

diff --git a/stem/control.py b/stem/control.py
index 77a5e3b..4d8b170 100644
--- a/stem/control.py
+++ b/stem/control.py
@@ -357,6 +357,12 @@ AccountingStats = collections.namedtuple('AccountingStats', [
   'write_limit',
 ])
 
+CreateHiddenServiceOutput = collections.namedtuple('CreateHiddenServiceOutput', [
+  'path',
+  'hostname',
+  'config',
+])
+
 
 def with_default(yields = False):
   """
@@ -2235,11 +2241,18 @@ class Controller(BaseController):
   def create_hidden_service(self, path, port, target_address = None, target_port = None):
     """
     Create a new hidden service. If the directory is already present, a
-    new port is added.
+    new port is added. This provides a **namedtuple** of the following...
+
+      * path (str) - hidden service directory
 
-    This method returns our *.onion address by reading the hidden service
-    directory. However, this directory is only readable by the tor user, so if
-    unavailable this method returns None.
+      * hostname (str) - onion address of the service, this is only retrieved
+        if we can read the hidden service directory
+
+      * config (dict) - tor's new hidden service configuration
+
+    Our *.onion address is fetched by reading the hidden service directory.
+    However, this directory is only readable by the tor user, so if unavailable
+    the **hostname** will be **None**.
 
     .. versionadded:: 1.3.0
 
@@ -2249,8 +2262,7 @@ class Controller(BaseController):
     :param int target_port: port of the service, by default this is the same as
       **port**
 
-    :returns: **str** of the onion address for the hidden service if it can be
-      retrieved, **None** otherwise
+    :returns: **CreateHiddenServiceOutput** if we create or update a hidden service, **None** otherwise
 
     :raises: :class:`stem.ControllerError` if the call fails
     """
@@ -2268,41 +2280,46 @@ class Controller(BaseController):
 
     conf = self.get_hidden_service_conf()
 
-    if path in conf:
-      ports = conf[path]['HiddenServicePort']
-
-      if (port, target_address, target_port) in ports:
-        return
-    else:
-      conf[path] = {'HiddenServicePort': []}
+    if path in conf and (port, target_address, target_port) in conf[path]['HiddenServicePort']:
+      return None
 
-    conf[path]['HiddenServicePort'].append((port, target_address, target_port))
+    conf.setdefault(path, OrderedDict()).setdefault('HiddenServicePort', []).append((port, target_address, target_port))
     self.set_hidden_service_conf(conf)
 
+    hostname = None
+
     if self.is_localhost():
-      if not os.path.isabs(path):
+      hostname_path = os.path.join(path, 'hostname')
+
+      if not os.path.isabs(hostname_path):
         cwd = stem.util.system.cwd(self.get_pid(None))
 
         if cwd:
-          path = stem.util.system.expand_path(path, cwd)
+          hostname_path = stem.util.system.expand_path(hostname_path, cwd)
 
-      if os.path.isabs(path):
+      if os.path.isabs(hostname_path):
         start_time = time.time()
-        hostname_path = os.path.join(path, 'hostname')
 
         while not os.path.exists(hostname_path):
           wait_time = time.time() - start_time
 
           if wait_time >= 3:
-            return
+            break
           else:
             time.sleep(0.05)
 
-        try:
-          with open(hostname_path) as hostname_file:
-            return hostname_file.read().strip()
-        except:
-          pass
+        if os.path.exists(hostname_path):
+          try:
+            with open(hostname_path) as hostname_file:
+              hostname = hostname_file.read().strip()
+          except:
+            pass
+
+    return CreateHiddenServiceOutput(
+      path = path,
+      hostname = hostname,
+      config = conf,
+    )
 
   def remove_hidden_service(self, path, port = None):
     """
diff --git a/test/integ/control/controller.py b/test/integ/control/controller.py
index 8b9759c..e482281 100644
--- a/test/integ/control/controller.py
+++ b/test/integ/control/controller.py
@@ -512,8 +512,8 @@ class TestController(unittest.TestCase):
         # add a new service, with/without explicit target
 
         hs_path = os.path.join(os.getcwd(), 'test_hidden_serviceX')
-        hs_address1 = controller.create_hidden_service(hs_path, 8888)
-        hs_address2 = controller.create_hidden_service(hs_path, 8989, target_port = 8021)
+        hs_address1 = controller.create_hidden_service(hs_path, 8888).hostname
+        hs_address2 = controller.create_hidden_service(hs_path, 8989, target_port = 8021).hostname
 
         self.assertEqual(hs_address1, hs_address2)
         self.assertTrue(hs_address1.endswith('.onion'))
@@ -536,9 +536,11 @@ class TestController(unittest.TestCase):
 
         # clean up the hidden service directories created as part of this test
 
-        shutil.rmtree('test_hidden_service1')
-        shutil.rmtree('test_hidden_service2')
-        shutil.rmtree('test_hidden_serviceX')
+        for path in ('test_hidden_service1', 'test_hidden_service2', 'test_hidden_serviceX'):
+          try:
+            shutil.rmtree(path)
+          except:
+            pass
 
   def test_set_conf(self):
     """





More information about the tor-commits mailing list