[tor-commits] [stem/master] Rename Cell.unpack to Cell.pop

atagar at torproject.org atagar at torproject.org
Wed Feb 7 19:44:51 UTC 2018


commit 28467fc87102069fabb1b5b496725f9e9855256e
Author: Damian Johnson <atagar at torproject.org>
Date:   Sun Jan 28 12:48:46 2018 -0800

    Rename Cell.unpack to Cell.pop
    
    Usually when we unpack cells we'll want to process everything from a response.
    As such having Cell.unpack provide a generator, and renaming our previous
    function that unpacked a single cell to Cell.pop.
---
 stem/client/cell.py      | 30 +++++++++++++++++++++++++-----
 test/unit/client/cell.py | 42 +++++++++++++++++++++---------------------
 2 files changed, 46 insertions(+), 26 deletions(-)

diff --git a/stem/client/cell.py b/stem/client/cell.py
index e2e589f5..59fbc14c 100644
--- a/stem/client/cell.py
+++ b/stem/client/cell.py
@@ -32,8 +32,9 @@ Messages communicated over a Tor relay's ORPort.
     |- AuthenticateCell - Client authentication.      (section 4.5)
     |- AuthorizeCell - Client authorization.          (not yet used)
     |
-    |- pack - Provides encoded bytes for this cell class.
-    +- unpack - Decodes bytes for this cell class.
+    |- pack - encodes cell into bytes
+    |- unpack - decodes series of cells
+    +- pop - decodes cell with remainder
 """
 
 import datetime
@@ -95,9 +96,31 @@ class Cell(object):
 
     raise ValueError("'%s' isn't a valid cell value" % value)
 
+  def pack(self, link_version):
+    raise NotImplementedError('Unpacking not yet implemented for %s cells' % type(self).NAME)
+
   @staticmethod
   def unpack(content, link_version):
     """
+    Unpacks all cells from a response.
+
+    :param bytes content: payload to decode
+    :param int link_version: link protocol version
+
+    :returns: :class:`~stem.client.cell.Cell` generator
+
+    :raises:
+      * ValueError if content is malformed
+      * NotImplementedError if unable to unpack any of the cell types
+    """
+
+    while content:
+      cell, content = Cell.pop(content, link_version)
+      yield cell
+
+  @staticmethod
+  def pop(content, link_version):
+    """
     Unpacks the first cell.
 
     :param bytes content: payload to decode
@@ -125,9 +148,6 @@ class Cell(object):
     payload, content = split(content, payload_len)
     return cls._unpack(payload, circ_id, link_version), content
 
-  def pack(self, link_version):
-    raise NotImplementedError('Unpacking not yet implemented for %s cells' % type(self).NAME)
-
   @classmethod
   def _pack(cls, link_version, payload, circ_id = 0):
     """
diff --git a/test/unit/client/cell.py b/test/unit/client/cell.py
index ba28238e..1fde76cf 100644
--- a/test/unit/client/cell.py
+++ b/test/unit/client/cell.py
@@ -99,7 +99,7 @@ class TestCell(unittest.TestCase):
     self.assertRaises(ValueError, Cell.by_value, None)
 
   def test_unpack_not_implemented(self):
-    self.assertRaisesRegexp(NotImplementedError, 'Unpacking not yet implemented for AUTHORIZE cells', Cell.unpack, '\x00\x00\x84\x00\x06\x00\x01\x00\x02\x00\x03', 2)
+    self.assertRaisesRegexp(NotImplementedError, 'Unpacking not yet implemented for AUTHORIZE cells', Cell.pop, '\x00\x00\x84\x00\x06\x00\x01\x00\x02\x00\x03', 2)
 
   def test_unpack_for_new_link(self):
     expected_certs = (
@@ -112,10 +112,10 @@ class TestCell(unittest.TestCase):
 
     content = test_data('new_link_cells')
 
-    version_cell, content = Cell.unpack(content, 2)
+    version_cell, content = Cell.pop(content, 2)
     self.assertEqual(VersionsCell([3, 4, 5]), version_cell)
 
-    certs_cell, content = Cell.unpack(content, 2)
+    certs_cell, content = Cell.pop(content, 2)
     self.assertEqual(CertsCell, type(certs_cell))
     self.assertEqual(len(expected_certs), len(certs_cell.certificates))
 
@@ -124,10 +124,10 @@ class TestCell(unittest.TestCase):
       self.assertEqual(cert_type_int, certs_cell.certificates[i].type_int)
       self.assertTrue(certs_cell.certificates[i].value.startswith(cert_prefix))
 
-    auth_challenge_cell, content = Cell.unpack(content, 2)
+    auth_challenge_cell, content = Cell.pop(content, 2)
     self.assertEqual(AuthChallengeCell([1, 3], '\x89Y\t\x99\xb2\x1e\xd9*V\xb6\x1bn\n\x05\xd8/\xe3QH\x85\x13Z\x17\xfc\x1c\x00{\xa9\xae\x83^K'), auth_challenge_cell)
 
-    netinfo_cell, content = Cell.unpack(content, 2)
+    netinfo_cell, content = Cell.pop(content, 2)
     self.assertEqual(NetinfoCell, type(netinfo_cell))
     self.assertEqual(datetime.datetime(2018, 1, 14, 1, 46, 56), netinfo_cell.timestamp)
     self.assertEqual(Address('127.0.0.1'), netinfo_cell.receiver_address)
@@ -138,14 +138,14 @@ class TestCell(unittest.TestCase):
   def test_padding_cell(self):
     for cell_bytes, payload in PADDING_CELLS.items():
       self.assertEqual(cell_bytes, PaddingCell(payload).pack(2))
-      self.assertEqual(payload, Cell.unpack(cell_bytes, 2)[0].payload)
+      self.assertEqual(payload, Cell.pop(cell_bytes, 2)[0].payload)
 
   def test_relay_cell(self):
     for cell_bytes, (command, command_int, circ_id, stream_id, data, digest) in RELAY_CELLS.items():
       self.assertEqual(cell_bytes, RelayCell(circ_id, command, data, digest, stream_id).pack(2))
       self.assertEqual(cell_bytes, RelayCell(circ_id, command_int, data, digest, stream_id).pack(2))
 
-      cell = Cell.unpack(cell_bytes, 2)[0]
+      cell = Cell.pop(cell_bytes, 2)[0]
       self.assertEqual(circ_id, cell.circ_id)
       self.assertEqual(command, cell.command)
       self.assertEqual(command_int, cell.command_int)
@@ -158,18 +158,18 @@ class TestCell(unittest.TestCase):
       self.assertEqual(cell_bytes, DestroyCell(circ_id, reason).pack(5))
       self.assertEqual(cell_bytes, DestroyCell(circ_id, reason_int).pack(5))
 
-      cell = Cell.unpack(cell_bytes, 5)[0]
+      cell = Cell.pop(cell_bytes, 5)[0]
       self.assertEqual(circ_id, cell.circ_id)
       self.assertEqual(reason, cell.reason)
       self.assertEqual(reason_int, cell.reason_int)
 
-    self.assertRaisesRegexp(ValueError, 'Circuit closure reason should be a single byte, but was 2', Cell.unpack, '\x80\x00\x00\x00\x04\x01\x01' + ZERO * 507, 5)
+    self.assertRaisesRegexp(ValueError, 'Circuit closure reason should be a single byte, but was 2', Cell.pop, '\x80\x00\x00\x00\x04\x01\x01' + ZERO * 507, 5)
 
   def test_create_fast_cell(self):
     for cell_bytes, (circ_id, key_material) in CREATE_FAST_CELLS.items():
       self.assertEqual(cell_bytes, CreateFastCell(circ_id, key_material).pack(5))
 
-      cell = Cell.unpack(cell_bytes, 5)[0]
+      cell = Cell.pop(cell_bytes, 5)[0]
       self.assertEqual(circ_id, cell.circ_id)
       self.assertEqual(key_material, cell.key_material)
 
@@ -179,7 +179,7 @@ class TestCell(unittest.TestCase):
     for cell_bytes, (circ_id, key_material, derivative_key) in CREATED_FAST_CELLS.items():
       self.assertEqual(cell_bytes, CreatedFastCell(circ_id, derivative_key, key_material).pack(5))
 
-      cell = Cell.unpack(cell_bytes, 5)[0]
+      cell = Cell.pop(cell_bytes, 5)[0]
       self.assertEqual(circ_id, cell.circ_id)
       self.assertEqual(key_material, cell.key_material)
       self.assertEqual(derivative_key, cell.derivative_key)
@@ -189,13 +189,13 @@ class TestCell(unittest.TestCase):
   def test_versions_cell(self):
     for cell_bytes, versions in VERSIONS_CELLS.items():
       self.assertEqual(cell_bytes, VersionsCell(versions).pack())
-      self.assertEqual(versions, Cell.unpack(cell_bytes, 2)[0].versions)
+      self.assertEqual(versions, Cell.pop(cell_bytes, 2)[0].versions)
 
   def test_netinfo_cell(self):
     for cell_bytes, (timestamp, receiver_address, sender_addresses) in NETINFO_CELLS.items():
       self.assertEqual(cell_bytes, NetinfoCell(receiver_address, sender_addresses, timestamp).pack(2))
 
-      cell = Cell.unpack(cell_bytes, 2)[0]
+      cell = Cell.pop(cell_bytes, 2)[0]
       self.assertEqual(timestamp, cell.timestamp)
       self.assertEqual(receiver_address, cell.receiver_address)
       self.assertEqual(sender_addresses, cell.sender_addresses)
@@ -203,31 +203,31 @@ class TestCell(unittest.TestCase):
   def test_vpadding_cell(self):
     for cell_bytes, payload in VPADDING_CELLS.items():
       self.assertEqual(cell_bytes, VPaddingCell(payload = payload).pack(2))
-      self.assertEqual(payload, Cell.unpack(cell_bytes, 2)[0].payload)
+      self.assertEqual(payload, Cell.pop(cell_bytes, 2)[0].payload)
 
     self.assertRaisesRegexp(ValueError, 'VPaddingCell constructor specified both a size of 5 bytes and payload of 1 bytes', VPaddingCell, 5, '\x02')
 
   def test_certs_cell(self):
     for cell_bytes, certs in CERTS_CELLS.items():
       self.assertEqual(cell_bytes, CertsCell(certs).pack(2))
-      self.assertEqual(certs, Cell.unpack(cell_bytes, 2)[0].certificates)
+      self.assertEqual(certs, Cell.pop(cell_bytes, 2)[0].certificates)
 
     # extra bytes after the last certificate should be ignored
 
-    self.assertEqual([Certificate(1, '\x08')], Cell.unpack('\x00\x00\x81\x00\x07\x01\x01\x00\x01\x08\x06\x04', 2)[0].certificates)
+    self.assertEqual([Certificate(1, '\x08')], Cell.pop('\x00\x00\x81\x00\x07\x01\x01\x00\x01\x08\x06\x04', 2)[0].certificates)
 
     # ... but truncated or missing certificates should error
 
-    self.assertRaisesRegexp(ValueError, 'CERTS cell should have a certificate with 3 bytes, but only had 1 remaining', Cell.unpack, '\x00\x00\x81\x00\x05\x01\x01\x00\x03\x08', 2)
-    self.assertRaisesRegexp(ValueError, 'CERTS cell indicates it should have 2 certificates, but only contained 1', Cell.unpack, '\x00\x00\x81\x00\x05\x02\x01\x00\x01\x08', 2)
+    self.assertRaisesRegexp(ValueError, 'CERTS cell should have a certificate with 3 bytes, but only had 1 remaining', Cell.pop, '\x00\x00\x81\x00\x05\x01\x01\x00\x03\x08', 2)
+    self.assertRaisesRegexp(ValueError, 'CERTS cell indicates it should have 2 certificates, but only contained 1', Cell.pop, '\x00\x00\x81\x00\x05\x02\x01\x00\x01\x08', 2)
 
   def test_auth_challenge_cell(self):
     for cell_bytes, (challenge, methods) in AUTH_CHALLENGE_CELLS.items():
       self.assertEqual(cell_bytes, AuthChallengeCell(methods, challenge).pack(2))
 
-      cell = Cell.unpack(cell_bytes, 2)[0]
+      cell = Cell.pop(cell_bytes, 2)[0]
       self.assertEqual(challenge, cell.challenge)
       self.assertEqual(methods, cell.methods)
 
-    self.assertRaisesRegexp(ValueError, 'AUTH_CHALLENGE cell should have a payload of 38 bytes, but only had 16', Cell.unpack, '\x00\x00\x82\x00&%s\x00\x02\x00\x01\x00\x03' % CHALLENGE[:10], 2)
-    self.assertRaisesRegexp(ValueError, 'AUTH_CHALLENGE should have 3 methods, but only had 4 bytes for it', Cell.unpack, '\x00\x00\x82\x00&%s\x00\x03\x00\x01\x00\x03' % CHALLENGE, 2)
+    self.assertRaisesRegexp(ValueError, 'AUTH_CHALLENGE cell should have a payload of 38 bytes, but only had 16', Cell.pop, '\x00\x00\x82\x00&%s\x00\x02\x00\x01\x00\x03' % CHALLENGE[:10], 2)
+    self.assertRaisesRegexp(ValueError, 'AUTH_CHALLENGE should have 3 methods, but only had 4 bytes for it', Cell.pop, '\x00\x00\x82\x00&%s\x00\x03\x00\x01\x00\x03' % CHALLENGE, 2)





More information about the tor-commits mailing list