commit 9b5c6bf74e3d6bc7a718d84c9d24e07bac460f0d
Author: Damian Johnson <atagar(a)torproject.org>
Date: Mon Jan 8 11:42:46 2018 -0800
Split stem.client.cell into its own module
---
stem/client/__init__.py | 141 +----------------------------
stem/client/cell.py | 151 ++++++++++++++++++++++++++++++++
test/settings.cfg | 2 +-
test/unit/client/__init__.py | 7 ++
test/unit/{client.py => client/cell.py} | 13 +--
5 files changed, 169 insertions(+), 145 deletions(-)
diff --git a/stem/client/__init__.py b/stem/client/__init__.py
index a0288f52..21786835 100644
--- a/stem/client/__init__.py
+++ b/stem/client/__init__.py
@@ -31,128 +31,14 @@ a wrapper for :class:`~stem.socket.RelaySocket`, much the same way as
Relay - Connection with a relay's ORPort.
"""
-import collections
-import struct
-
from stem.util import enum
ZERO = '\x00'
-class Cell(collections.namedtuple('Cell', ['name', 'value', 'fixed_size', 'for_circuit'])):
- """
- Metadata for ORPort cells.
-
- :var str name: command of the cell
- :var int value: integer value of the command on the wire
- :var bool fixed_size: **True** if cells have a fixed length,
- **False** if variable
- :var bool for_circuit: **True** if command is for a circuit,
- **False** otherwise
- """
-
- @staticmethod
- def by_name(name):
- """
- Provides cell attributes by its name.
-
- :parm str name: cell command to fetch
-
- :raise: **ValueError** if cell type is invalid
- """
-
- for cell_type in CELL_TYPES:
- if name == cell_type.name:
- return cell_type
-
- raise ValueError("'%s' isn't a valid cell type" % name)
-
- @staticmethod
- def by_value(value):
- """
- Provides cell attributes by its value.
-
- :parm int value: cell value to fetch
-
- :raise: **ValueError** if cell type is invalid
- """
-
- for cell_type in CELL_TYPES:
- if value == cell_type.value:
- return cell_type
-
- raise ValueError("'%s' isn't a valid cell value" % value)
-
- @staticmethod
- def pack(name, link_version, payload, circ_id = None):
- """
- Provides bytes that can be used on the wire for these cell attributes.
-
- :param str name: cell command
- :param int link_version: link protocol version
- :param bytes payload: cell payload
- :param int circ_id: circuit id, if for a circuit
-
- :raise: **ValueError** if...
- * cell type or circuit id is invalid
- * payload is too large
- """
-
- attr = Cell.by_name(name)
- circ_id_len = Pack.LONG if link_version > 3 else Pack.SHORT
-
- if attr.for_circuit and circ_id is None:
- if name.startswith('CREATE'):
- # Since we're initiating the circuit we pick any value from a range
- # that's determined by our link version.
-
- circ_id = 0x80000000 if link_version > 3 else 0x01
- else:
- raise ValueError('%s cells require a circ_id' % name)
- elif not attr.for_circuit:
- if circ_id:
- raise ValueError("%s cells don't concern circuits, circ_id is unused" % name)
-
- circ_id = 0 # field is still mandatory for all cells
-
- packed_circ_id = struct.pack(circ_id_len, circ_id)
- packed_command = struct.pack(Pack.CHAR, attr.value)
- packed_size = b'' if attr.fixed_size else struct.pack(Pack.SHORT, len(payload))
- cell = b''.join((packed_circ_id, packed_command, packed_size, payload))
-
- # pad fixed sized cells to the required length
-
- if attr.fixed_size:
- fixed_cell_len = 514 if link_version > 3 else 512
-
- if len(cell) > fixed_cell_len:
- raise ValueError('Payload of %s is too large (%i bytes), must be less than %i' % (name, len(cell), fixed_cell_len))
-
- cell += ZERO * (fixed_cell_len - len(cell))
-
- return cell
-
-
-class VersionCell(Cell):
- """
- Link version negotiation cell.
- """
-
- @staticmethod
- def pack(versions):
- """
- Provides the payload for a series of link versions.
-
- :param list versions: link versions to serialize
-
- :returns: **bytes** with a payload for these versions
- """
-
- # Used for link version negotiation so we don't have that yet. This is fine
- # since VERSION cells avoid most version dependent attributes.
-
- payload = b''.join([struct.pack(Pack.SHORT, v) for v in versions])
- return Cell.pack('VERSIONS', 3, payload)
+__all__ = [
+ 'cell',
+]
class Relay(object):
@@ -168,24 +54,3 @@ Pack = enum.Enum(
('LONG', '!L'), # 4 bytes
('LONG_LONG', '!Q'), # 8 bytes
)
-
-CELL_TYPES = (
- Cell('PADDING', 0, True, False), # Padding (section 7.2)
- Cell('CREATE', 1, True, True), # Create a circuit (section 5.1)
- Cell('CREATED', 2, True, True), # Acknowledge create (section 5.1)
- Cell('RELAY', 3, True, True), # End-to-end data (section 5.5 and 6)
- Cell('DESTROY', 4, True, True), # Stop using a circuit (section 5.4)
- Cell('CREATE_FAST', 5, True, True), # Create a circuit, no PK (section 5.1)
- Cell('CREATED_FAST', 6, True, True), # Circuit created, no PK (section 5.1)
- Cell('VERSIONS', 7, False, False), # Negotiate proto version (section 4)
- Cell('NETINFO', 8, True, False), # Time and address info (section 4.5)
- Cell('RELAY_EARLY', 9, True, True), # End-to-end data; limited (section 5.6)
- Cell('CREATE2', 10, True, True), # Extended CREATE cell (section 5.1)
- Cell('CREATED2', 11, True, True), # Extended CREATED cell (section 5.1)
- Cell('PADDING_NEGOTIATE', 12, True, False), # Padding negotiation (section 7.2)
- Cell('VPADDING', 128, False, False), # Variable-length padding (section 7.2)
- Cell('CERTS', 129, False, False), # Certificates (section 4.2)
- Cell('AUTH_CHALLENGE', 130, False, False), # Challenge value (section 4.3)
- Cell('AUTHENTICATE', 131, False, False), # Client authentication (section 4.5)
- Cell('AUTHORIZE', 132, False, False), # Client authorization (not yet used)
-)
diff --git a/stem/client/cell.py b/stem/client/cell.py
new file mode 100644
index 00000000..5cd23c0e
--- /dev/null
+++ b/stem/client/cell.py
@@ -0,0 +1,151 @@
+# Copyright 2018, Damian Johnson and The Tor Project
+# See LICENSE for licensing information
+
+"""
+Messages communicated over a Tor relay's ORPort.
+
+.. versionadded:: 1.7.0
+"""
+
+import collections
+import struct
+
+from stem.client import ZERO, Pack
+
+
+class Cell(collections.namedtuple('Cell', ['name', 'value', 'fixed_size', 'for_circuit'])):
+ """
+ Metadata for ORPort cells.
+
+ :var str name: command of the cell
+ :var int value: integer value of the command on the wire
+ :var bool fixed_size: **True** if cells have a fixed length,
+ **False** if variable
+ :var bool for_circuit: **True** if command is for a circuit,
+ **False** otherwise
+ """
+
+ @staticmethod
+ def by_name(name):
+ """
+ Provides cell attributes by its name.
+
+ :parm str name: cell command to fetch
+
+ :raise: **ValueError** if cell type is invalid
+ """
+
+ for cell_type in CELL_TYPES:
+ if name == cell_type.name:
+ return cell_type
+
+ raise ValueError("'%s' isn't a valid cell type" % name)
+
+ @staticmethod
+ def by_value(value):
+ """
+ Provides cell attributes by its value.
+
+ :parm int value: cell value to fetch
+
+ :raise: **ValueError** if cell type is invalid
+ """
+
+ for cell_type in CELL_TYPES:
+ if value == cell_type.value:
+ return cell_type
+
+ raise ValueError("'%s' isn't a valid cell value" % value)
+
+ @staticmethod
+ def pack(name, link_version, payload, circ_id = None):
+ """
+ Provides bytes that can be used on the wire for these cell attributes.
+
+ :param str name: cell command
+ :param int link_version: link protocol version
+ :param bytes payload: cell payload
+ :param int circ_id: circuit id, if for a circuit
+
+ :raise: **ValueError** if...
+ * cell type or circuit id is invalid
+ * payload is too large
+ """
+
+ attr = Cell.by_name(name)
+ circ_id_len = Pack.LONG if link_version > 3 else Pack.SHORT
+
+ if attr.for_circuit and circ_id is None:
+ if name.startswith('CREATE'):
+ # Since we're initiating the circuit we pick any value from a range
+ # that's determined by our link version.
+
+ circ_id = 0x80000000 if link_version > 3 else 0x01
+ else:
+ raise ValueError('%s cells require a circ_id' % name)
+ elif not attr.for_circuit:
+ if circ_id:
+ raise ValueError("%s cells don't concern circuits, circ_id is unused" % name)
+
+ circ_id = 0 # field is still mandatory for all cells
+
+ packed_circ_id = struct.pack(circ_id_len, circ_id)
+ packed_command = struct.pack(Pack.CHAR, attr.value)
+ packed_size = b'' if attr.fixed_size else struct.pack(Pack.SHORT, len(payload))
+ cell = b''.join((packed_circ_id, packed_command, packed_size, payload))
+
+ # pad fixed sized cells to the required length
+
+ if attr.fixed_size:
+ fixed_cell_len = 514 if link_version > 3 else 512
+
+ if len(cell) > fixed_cell_len:
+ raise ValueError('Payload of %s is too large (%i bytes), must be less than %i' % (name, len(cell), fixed_cell_len))
+
+ cell += ZERO * (fixed_cell_len - len(cell))
+
+ return cell
+
+
+class VersionCell(Cell):
+ """
+ Link version negotiation cell.
+ """
+
+ @staticmethod
+ def pack(versions):
+ """
+ Provides the payload for a series of link versions.
+
+ :param list versions: link versions to serialize
+
+ :returns: **bytes** with a payload for these versions
+ """
+
+ # Used for link version negotiation so we don't have that yet. This is fine
+ # since VERSION cells avoid most version dependent attributes.
+
+ payload = b''.join([struct.pack(Pack.SHORT, v) for v in versions])
+ return Cell.pack('VERSIONS', 3, payload)
+
+
+CELL_TYPES = (
+ Cell('PADDING', 0, True, False), # Padding (section 7.2)
+ Cell('CREATE', 1, True, True), # Create a circuit (section 5.1)
+ Cell('CREATED', 2, True, True), # Acknowledge create (section 5.1)
+ Cell('RELAY', 3, True, True), # End-to-end data (section 5.5 and 6)
+ Cell('DESTROY', 4, True, True), # Stop using a circuit (section 5.4)
+ Cell('CREATE_FAST', 5, True, True), # Create a circuit, no PK (section 5.1)
+ Cell('CREATED_FAST', 6, True, True), # Circuit created, no PK (section 5.1)
+ Cell('VERSIONS', 7, False, False), # Negotiate proto version (section 4)
+ Cell('NETINFO', 8, True, False), # Time and address info (section 4.5)
+ Cell('RELAY_EARLY', 9, True, True), # End-to-end data; limited (section 5.6)
+ Cell('CREATE2', 10, True, True), # Extended CREATE cell (section 5.1)
+ Cell('CREATED2', 11, True, True), # Extended CREATED cell (section 5.1)
+ Cell('PADDING_NEGOTIATE', 12, True, False), # Padding negotiation (section 7.2)
+ Cell('VPADDING', 128, False, False), # Variable-length padding (section 7.2)
+ Cell('CERTS', 129, False, False), # Certificates (section 4.2)
+ Cell('AUTH_CHALLENGE', 130, False, False), # Challenge value (section 4.3)
+ Cell('AUTHENTICATE', 131, False, False), # Client authentication (section 4.5)
+ Cell('AUTHORIZE', 132, False, False), # Client authorization (not yet used)
+)
diff --git a/test/settings.cfg b/test/settings.cfg
index 501dff9c..b4b125a8 100644
--- a/test/settings.cfg
+++ b/test/settings.cfg
@@ -229,7 +229,7 @@ test.unit_tests
|test.unit.response.authchallenge.TestAuthChallengeResponse
|test.unit.response.protocolinfo.TestProtocolInfoResponse
|test.unit.response.mapaddress.TestMapAddressResponse
-|test.unit.client.TestClient
+|test.unit.client.cell.TestCell
|test.unit.connection.authentication.TestAuthenticate
|test.unit.connection.connect.TestConnect
|test.unit.control.controller.TestControl
diff --git a/test/unit/client/__init__.py b/test/unit/client/__init__.py
new file mode 100644
index 00000000..e35416a8
--- /dev/null
+++ b/test/unit/client/__init__.py
@@ -0,0 +1,7 @@
+"""
+Unit tests for stem.client.* contents.
+"""
+
+__all__ = [
+ 'cell',
+]
diff --git a/test/unit/client.py b/test/unit/client/cell.py
similarity index 87%
rename from test/unit/client.py
rename to test/unit/client/cell.py
index 6faa3e8e..8093b8d9 100644
--- a/test/unit/client.py
+++ b/test/unit/client/cell.py
@@ -1,15 +1,16 @@
"""
-Unit tests for the stem.client.
+Unit tests for the stem.client.cell.
"""
import struct
import unittest
-from stem.client import Pack, Cell
+from stem.client import Pack
+from stem.client.cell import Cell
-class TestClient(unittest.TestCase):
- def test_cell_by_name(self):
+class TestCell(unittest.TestCase):
+ def test_by_name(self):
cell = Cell.by_name('NETINFO')
self.assertEqual('NETINFO', cell.name)
self.assertEqual(8, cell.value)
@@ -20,7 +21,7 @@ class TestClient(unittest.TestCase):
self.assertRaises(ValueError, Cell.by_name, 85)
self.assertRaises(ValueError, Cell.by_name, None)
- def test_cell_by_value(self):
+ def test_by_value(self):
cell = Cell.by_value(8)
self.assertEqual('NETINFO', cell.name)
self.assertEqual(8, cell.value)
@@ -31,7 +32,7 @@ class TestClient(unittest.TestCase):
self.assertRaises(ValueError, Cell.by_value, 85)
self.assertRaises(ValueError, Cell.by_value, None)
- def test_cell_pack(self):
+ def test_pack(self):
version_payload = struct.pack(Pack.SHORT, 2)
# basic link v2 and v4 VERSIONS cell