[tor-commits] [stem/master] Cache 'GETINFO fingerprint' when not a relay

atagar at torproject.org atagar at torproject.org
Sun Oct 22 01:16:00 UTC 2017


commit b730a0b57d13a7fe12a7b956ac5fc06fabcddf73
Author: Damian Johnson <atagar at torproject.org>
Date:   Sat Oct 21 06:43:41 2017 -0700

    Cache 'GETINFO fingerprint' when not a relay
    
    Probably the most requested GETINFO parameter is our fingerprint. We cache
    fingerprint values, but not exceptions causing any request when not a relay to
    go to tor.
    
    This is unnecessary. We can detect if we become a relay so caching the 'not a
    relay' response to avoid having callers (like Nyx) accidently hammer tor.
---
 stem/control.py                 |  9 +++++++++
 test/unit/control/controller.py | 27 +++++++++++++++++++++++++++
 2 files changed, 36 insertions(+)

diff --git a/stem/control.py b/stem/control.py
index b6d49d22..7b622aff 100644
--- a/stem/control.py
+++ b/stem/control.py
@@ -1048,6 +1048,7 @@ class Controller(BaseController):
     self._event_listeners_lock = threading.RLock()
     self._enabled_features = []
     self._is_geoip_unavailable = None
+    self._last_fingerprint_exc = None
 
     super(Controller, self).__init__(control_socket, is_authenticated)
 
@@ -1144,6 +1145,8 @@ class Controller(BaseController):
     for param in params:
       if param.startswith('ip-to-country/') and param != 'ip-to-country/0.0.0.0' and self.is_geoip_unavailable():
         raise stem.ProtocolError('Tor geoip database is unavailable')
+      elif param == 'fingerprint' and self._last_fingerprint_exc and self.get_conf('ORPort', None) is None:
+        raise self._last_fingerprint_exc  # we already know we're not a relay
 
     # check for cached results
 
@@ -1189,6 +1192,9 @@ class Controller(BaseController):
 
         self._set_cache(to_cache, 'getinfo')
 
+      if 'fingerprint' in params:
+        self._last_fingerprint_exc = None
+
       log.debug('GETINFO %s (runtime: %0.4f)' % (' '.join(params), time.time() - start_time))
 
       if is_multiple:
@@ -1196,6 +1202,9 @@ class Controller(BaseController):
       else:
         return list(reply.values())[0]
     except stem.ControllerError as exc:
+      if 'fingerprint' in params:
+        self._last_fingerprint_exc = exc
+
       log.debug('GETINFO %s (failed: %s)' % (' '.join(params), exc))
       raise
 
diff --git a/test/unit/control/controller.py b/test/unit/control/controller.py
index 8b5d6c8f..c7e412c5 100644
--- a/test/unit/control/controller.py
+++ b/test/unit/control/controller.py
@@ -45,6 +45,33 @@ class TestControl(unittest.TestCase):
     for event in stem.control.EventType:
       self.assertTrue(stem.control.event_description(event) is not None)
 
+  @patch('stem.control.Controller.msg')
+  def test_get_get_info(self, msg_mock):
+    msg_mock.return_value = ControlMessage.from_str('250-hello=hi right back!\r\n250 OK\r\n', 'GETINFO')
+    self.assertEqual('hi right back!', self.controller.get_info('hello'))
+
+  @patch('stem.control.Controller.msg')
+  @patch('stem.control.Controller.get_conf')
+  def test_get_get_info_without_fingerprint(self, get_conf_mock, msg_mock):
+    msg_mock.return_value = ControlMessage.from_str('551 Not running in server mode\r\n')
+    get_conf_mock.return_value = None
+
+    self.assertEqual(None, self.controller._last_fingerprint_exc)
+    self.assertRaisesRegexp(stem.ProtocolError, 'Not running in server mode', self.controller.get_info, 'fingerprint')
+    self.assertEqual("GETINFO response didn't have an OK status:\nNot running in server mode", str(self.controller._last_fingerprint_exc))
+    self.assertEqual(1, msg_mock.call_count)
+
+    # now that we have a cached failure we should provide that back
+
+    self.assertRaisesRegexp(stem.ProtocolError, 'Not running in server mode', self.controller.get_info, 'fingerprint')
+    self.assertEqual(1, msg_mock.call_count)
+
+    # ... but if we become a relay we'll call it again
+
+    get_conf_mock.return_value = '443'
+    self.assertRaisesRegexp(stem.ProtocolError, 'Not running in server mode', self.controller.get_info, 'fingerprint')
+    self.assertEqual(2, msg_mock.call_count)
+
   @patch('stem.control.Controller.get_info')
   def test_get_version(self, get_info_mock):
     """





More information about the tor-commits mailing list