[tor-commits] [stem/master] Support resetting circuit timeouts

atagar at torproject.org atagar at torproject.org
Sun Aug 30 01:39:38 UTC 2020


commit 120715e4af8cbaca2b0d1f785aba15a94e4264df
Author: Damian Johnson <atagar at torproject.org>
Date:   Sat Aug 29 18:24:16 2020 -0700

    Support resetting circuit timeouts
    
    According to Mike and Karsten, OnionPerf and Shadow require the ability to
    reset circuit timeouts when they change tor's guards. Adding this as an
    argument to drop_guards().
    
      https://github.com/torproject/stem/issues/73
---
 docs/change_log.rst              |  1 +
 stem/control.py                  | 13 ++++++++++++-
 stem/version.py                  |  2 ++
 test/integ/control/controller.py | 36 ++++++++++++++++++++++++++++++++++++
 4 files changed, 51 insertions(+), 1 deletion(-)

diff --git a/docs/change_log.rst b/docs/change_log.rst
index f30c3a48..f5a96d7b 100644
--- a/docs/change_log.rst
+++ b/docs/change_log.rst
@@ -53,6 +53,7 @@ The following are only available within Stem's `git repository
   * Our API now provides `type hints <https://blog.atagar.com/may2020/>`_.
   * Migrated to `asyncio <https://docs.python.org/3/library/asyncio.html>`_. Stem can now be used by `both synchronous and asynchronous applications <https://blog.atagar.com/july2020/>`_.
   * Installation has migrated from distutils to setuptools.
+  * Added the 'reset_timeouts' argument to :func:`~stem.control.Controller.drop_guards` (:ticket:`73`)
 
  * **Controller**
 
diff --git a/stem/control.py b/stem/control.py
index 79d5881d..5ee9782e 100644
--- a/stem/control.py
+++ b/stem/control.py
@@ -3894,17 +3894,28 @@ class Controller(BaseController):
     response = await self.msg('MAPADDRESS %s' % mapaddress_arg)
     return stem.response._convert_to_mapaddress(response)
 
-  async def drop_guards(self) -> None:
+  async def drop_guards(self, reset_timeouts: bool = False) -> None:
     """
     Drops our present guard nodes and picks a new set.
 
     .. versionadded:: 1.2.0
 
+    .. versionchanged:: 2.0.0
+       Added the reset_timeouts argument.
+
+    :param reset_timeouts: reset circuit timeout counters
+
     :raises: :class:`stem.ControllerError` if Tor couldn't fulfill the request
     """
 
+    if reset_timeouts and (await self.get_version() < stem.version.Requirement.DROPTIMEOUTS):
+      raise ValueError('DROPTIMEOUTS requires tor %s or higher' % stem.version.Requirement.DROPTIMEOUTS)
+
     await self.msg('DROPGUARDS')
 
+    if reset_timeouts:
+      await self.msg('DROPTIMEOUTS')
+
   async def _post_authentication(self) -> None:
     await super(Controller, self)._post_authentication()
 
diff --git a/stem/version.py b/stem/version.py
index d01e04d9..9ed5fa37 100644
--- a/stem/version.py
+++ b/stem/version.py
@@ -30,6 +30,7 @@ easily parsed and compared, for instance...
   Requirement           Description
   ===================== ===========
   **DORMANT_MODE**      **DORMANT** and **ACTIVE** :data:`~stem.Signal`
+  **DROPTIMEOUTS**      **DROPTIMEOUTS** controller command
   **HSFETCH_V3**        HSFETCH for version 3 hidden services
   ===================== ===========
 """
@@ -218,5 +219,6 @@ class Version(object):
 
 Requirement = stem.util.enum.Enum(
   ('DORMANT_MODE', Version('0.4.0.1-alpha')),
+  ('DROPTIMEOUTS', Version('0.4.5.0-alpha')),
   ('HSFETCH_V3', Version('0.4.1.1-alpha')),
 )
diff --git a/test/integ/control/controller.py b/test/integ/control/controller.py
index 3a96c688..6f5da0c4 100644
--- a/test/integ/control/controller.py
+++ b/test/integ/control/controller.py
@@ -1285,6 +1285,42 @@ class TestController(unittest.TestCase):
       await controller.map_address(dict([(addr, None) for addr in mapped_addresses]))
       self.assertEquals({}, await address_mappings('control'))
 
+  @test.require.controller
+  @test.require.online
+  @async_test
+  async def test_drop_guards(self):
+    async with await test.runner.get_runner().get_tor_controller() as controller:
+      previous_guards = await controller.get_info('entry-guards')
+      started_at = time.time()
+
+      await controller.drop_guards()
+
+      while time.time() < (started_at + 5):
+        if previous_guards != await controller.get_info('entry-guards'):
+          return  # success
+
+        await asyncio.sleep(0.01)
+
+      self.fail('DROPGUARDS failed to change our guards within five seconds')
+
+  @test.require.controller
+  @test.require.version(stem.version.Requirement.DROPTIMEOUTS)
+  @async_test
+  async def test_drop_guards_with_reset(self):
+    async with await test.runner.get_runner().get_tor_controller() as controller:
+      events = asyncio.Queue()
+
+      await controller.add_event_listener(lambda event: events.put_nowait(event), stem.control.EventType.BUILDTIMEOUT_SET)
+      await controller.drop_guards(reset_timeouts = True)
+
+      try:
+        event = await asyncio.wait_for(events.get(), timeout = 5)
+      except asyncio.TimeoutError:
+        self.fail('DROPTIMEOUTS failed to emit a BUILDTIMEOUT_SET event within five seconds')
+
+      self.assertEqual('RESET', event.set_type)
+      self.assertEqual(0, event.total_times)
+
   @test.require.controller
   @async_test
   async def test_mapaddress_mixed_response(self):



More information about the tor-commits mailing list