[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [stem/master] RelayDescriptor creation
commit dafb4a6a96c0c1eb138a7c7d9499c5577864dc67
Author: Damian Johnson <atagar@xxxxxxxxxxxxxx>
Date: Sun Apr 30 16:37:16 2017 -0700
RelayDescriptor creation
Implementing a function for creating our first descriptor type (relay server
descriptors). This replacing our mocking util's get_relay_server_descriptor()
with RelayDescriptor.create() and RelayDescriptor.content().
This is a little different from the API I was previously planning on but think
I like this better.
---
stem/descriptor/__init__.py | 154 ++++++++++++++++++++----------
stem/descriptor/server_descriptor.py | 30 +++++-
test/mocking.py | 41 --------
test/unit/descriptor/export.py | 14 +--
test/unit/descriptor/server_descriptor.py | 95 ++++++++----------
test/unit/tutorial.py | 8 +-
test/unit/tutorial_examples.py | 10 +-
7 files changed, 188 insertions(+), 164 deletions(-)
diff --git a/stem/descriptor/__init__.py b/stem/descriptor/__init__.py
index d50b661..32b7d7c 100644
--- a/stem/descriptor/__init__.py
+++ b/stem/descriptor/__init__.py
@@ -18,22 +18,6 @@ Package for parsing and processing descriptor data.
|- get_unrecognized_lines - unparsed descriptor content
+- __str__ - string that the descriptor was made from
-.. data:: DescriptorType (enum)
-
- Common descriptor types.
-
- .. versionadded:: 1.6.0
-
- =================== ===========
- DescriptorType Description
- =================== ===========
- **SERVER** :class:`~stem.descriptor.server_descriptor.RelayDescriptor`
- **EXTRA** :class:`~stem.descriptor.extrainfo_descriptor.RelayExtraInfoDescriptor`
- **MICRO** :class:`~stem.descriptor.microdescriptor.Microdescriptor`
- **CONSENSUS** :class:`~stem.descriptor.networkstatus.NetworkStatusDocumentV3`
- **HIDDEN_SERVICE** :class:`~stem.descriptor.hidden_service_descriptor.HiddenServiceDescriptor`
- =================== ===========
-
.. data:: DocumentHandler (enum)
Ways in which we can parse a
@@ -97,20 +81,18 @@ PGP_BLOCK_START = re.compile('^-----BEGIN ([%s%s]+)-----$' % (KEYWORD_CHAR, WHIT
PGP_BLOCK_END = '-----END %s-----'
EMPTY_COLLECTION = ([], {}, set())
+CRYPTO_BLOB = """
+MIGJAoGBAJv5IIWQ+WDWYUdyA/0L8qbIkEVH/cwryZWoIaPAzINfrw1WfNZGtBmg
+skFtXhOHHqTRN4GPPrZsAIUOQGzQtGb66IQgT4tO/pj+P6QmSCCdTfhvGfgTCsC+
+WPi4Fl2qryzTb3QO5r5x7T8OsG2IBUET1bLQzmtbC560SYR49IvVAgMBAAE=
+"""
+
DocumentHandler = stem.util.enum.UppercaseEnum(
'ENTRIES',
'DOCUMENT',
'BARE_DOCUMENT',
)
-DescriptorType = stem.util.enum.Enum(
- ('SERVER', 'server-descriptor 1.0'),
- ('EXTRAINFO', 'extra-info 1.0'),
- ('MICRO', 'microdescriptor 1.0'),
- ('CONSENSUS', 'network-status-consensus-3 1.0'),
- ('HIDDEN_SERVICE', 'hidden-service-descriptor 1.0'),
-)
-
def parse_file(descriptor_file, descriptor_type = None, validate = False, document_handler = DocumentHandler.ENTRIES, normalize_newlines = None, **kwargs):
"""
@@ -353,39 +335,64 @@ def _parse_metrics_file(descriptor_type, major_version, minor_version, descripto
raise TypeError("Unrecognized metrics descriptor format. type: '%s', version: '%i.%i'" % (descriptor_type, major_version, minor_version))
-def create(desc_type, attr = None, exclude = (), validate = False, sign = False):
+def _descriptor_content(attr = None, exclude = (), header_template = (), footer_template = ()):
"""
- Creates a descriptor with the given attributes.
+ Constructs a minimal descriptor with the given attributes. The content we
+ provide back is of the form...
- .. versionadded:: 1.6.0
+ * header_template (with matching attr filled in)
+ * unused attr entries
+ * footer_template (with matching attr filled in)
- :param DescriptorType desc_type: type of descriptor to be created
- :param dict attr: keyword/value mappings to be included in the descriptor
- :param list exclude: mandatory keywords to exclude from the descriptor, this
- results in an invalid descriptor
- :param bool validate: checks the validity of the descriptor's content if
- **True**, skips these checks otherwise
- :param bool sign_content: includes cryptographic digest if True
+ So for instance...
+
+ ::
- :returns: :class:`~stem.descriptor.Descriptor` subclass
+ _descriptor_content(
+ attr = {'nickname': 'caerSidi', 'contact': 'atagar'},
+ header_template = (
+ ('nickname', 'foobar'),
+ ('fingerprint', '12345'),
+ ),
+ )
- :raises:
- * **ValueError** if the contents is malformed and validate is True
- * **ImportError** if cryptography is unavailable and sign is True
+ ... would result in...
+
+ ::
+
+ nickname caerSidi
+ fingerprint 12345
+ contact atagar
+
+ :param dict attr: keyword/value mappings to be included in the descriptor
+ :param list exclude: mandatory keywords to exclude from the descriptor
+ :param tuple header_template: key/value pairs for mandatory fields before unrecognized content
+ :param tuple footer_template: key/value pairs for mandatory fields after unrecognized content
+
+ :returns: str with the requested descriptor content
"""
- if desc_type == DescriptorType.SERVER:
- pass
- elif desc_type == DescriptorType.EXTRAINFO:
- pass
- elif desc_type == DescriptorType.MICRO:
- pass
- elif desc_type == DescriptorType.CONSENSUS:
- pass
- elif desc_type == DescriptorType.HIDDEN_SERVICE:
- pass
- else:
- raise TypeError("'%s' isn't a valid descriptor type we can create" % desc_type)
+ header_content, footer_content = [], []
+ attr = {} if attr is None else dict(attr) # shallow copy since we're destructive
+
+ for content, template in ((header_content, header_template),
+ (footer_content, footer_template)):
+ for keyword, value in template:
+ if keyword in exclude:
+ continue
+
+ value = attr.pop(keyword, value)
+
+ if not value:
+ content.append(keyword)
+ elif value.startswith('\n'):
+ # some values like crypto follow the line instead
+ content.append('%s%s' % (keyword, value))
+ else:
+ content.append('%s %s' % (keyword, value))
+
+ remainder = [('%s %s' % (k, v) if v else k) for k, v in attr.items()]
+ return stem.util.str_tools._to_bytes('\n'.join(header_content + remainder + footer_content))
def _value(line, entries):
@@ -522,6 +529,53 @@ class Descriptor(object):
self._entries = {}
self._unrecognized_lines = []
+ @classmethod
+ def content(cls, attr = None, exclude = (), sign = False):
+ """
+ Creates descriptor content with the given attributes. Mandatory fields are
+ filled with dummy information unless data is supplied.
+
+ .. versionadded:: 1.6.0
+
+ :param dict attr: keyword/value mappings to be included in the descriptor
+ :param list exclude: mandatory keywords to exclude from the descriptor, this
+ results in an invalid descriptor
+ :param bool sign_content: includes cryptographic digest if True
+
+ :returns: **str** with the content of a descriptor
+
+ :raises:
+ * **ImportError** if cryptography is unavailable and sign is True
+ * **NotImplementedError** if not implemented for this descriptor type
+ """
+
+ raise NotImplementedError("The create and content methods haven't been implemented for %s" % cls.__name__)
+
+ @classmethod
+ def create(cls, attr = None, exclude = (), validate = True, sign = False):
+ """
+ Creates a descriptor with the given attributes. Mandatory fields are filled
+ with dummy information unless data is supplied.
+
+ .. versionadded:: 1.6.0
+
+ :param dict attr: keyword/value mappings to be included in the descriptor
+ :param list exclude: mandatory keywords to exclude from the descriptor, this
+ results in an invalid descriptor
+ :param bool validate: checks the validity of the descriptor's content if
+ **True**, skips these checks otherwise
+ :param bool sign_content: includes cryptographic digest if True
+
+ :returns: :class:`~stem.descriptor.Descriptor` subclass
+
+ :raises:
+ * **ValueError** if the contents is malformed and validate is True
+ * **ImportError** if cryptography is unavailable and sign is True
+ * **NotImplementedError** if not implemented for this descriptor type
+ """
+
+ return cls(cls.content(attr, exclude, sign), validate = validate)
+
def get_path(self):
"""
Provides the absolute path that we loaded this descriptor from.
diff --git a/stem/descriptor/server_descriptor.py b/stem/descriptor/server_descriptor.py
index ddc1149..9f7c846 100644
--- a/stem/descriptor/server_descriptor.py
+++ b/stem/descriptor/server_descriptor.py
@@ -49,8 +49,10 @@ import stem.version
from stem.util import str_type
from stem.descriptor import (
+ CRYPTO_BLOB,
PGP_BLOCK_END,
Descriptor,
+ _descriptor_content,
_get_descriptor_components,
_read_until_keywords,
_bytes_for_block,
@@ -110,6 +112,19 @@ SINGLE_FIELDS = (
DEFAULT_IPV6_EXIT_POLICY = stem.exit_policy.MicroExitPolicy('reject 1-65535')
REJECT_ALL_POLICY = stem.exit_policy.ExitPolicy('reject *:*')
+RELAY_SERVER_HEADER = (
+ ('router', 'caerSidi 71.35.133.197 9001 0 0'),
+ ('published', '2012-03-01 17:15:27'),
+ ('bandwidth', '153600 256000 104590'),
+ ('reject', '*:*'),
+ ('onion-key', '\n-----BEGIN RSA PUBLIC KEY-----%s-----END RSA PUBLIC KEY-----' % CRYPTO_BLOB),
+ ('signing-key', '\n-----BEGIN RSA PUBLIC KEY-----%s-----END RSA PUBLIC KEY-----' % CRYPTO_BLOB),
+)
+
+RELAY_SERVER_FOOTER = (
+ ('router-signature', '\n-----BEGIN SIGNATURE-----%s-----END SIGNATURE-----' % CRYPTO_BLOB),
+)
+
def _parse_file(descriptor_file, is_bridge = False, validate = False, **kwargs):
"""
@@ -738,6 +753,9 @@ class RelayDescriptor(ServerDescriptor):
Our **ed25519_certificate** is deprecated in favor of our new
**certificate** attribute. The base64 encoded certificate is available via
the certificate's **encoded** attribute.
+
+ .. versionchanged:: 1.6.0
+ Added the **skip_crypto_validation** constructor argument.
"""
ATTRIBUTES = dict(ServerDescriptor.ATTRIBUTES, **{
@@ -765,7 +783,7 @@ class RelayDescriptor(ServerDescriptor):
'router-signature': _parse_router_signature_line,
})
- def __init__(self, raw_contents, validate = False, annotations = None):
+ def __init__(self, raw_contents, validate = False, annotations = None, skip_crypto_validation = False):
super(RelayDescriptor, self).__init__(raw_contents, validate, annotations)
if validate:
@@ -775,7 +793,7 @@ class RelayDescriptor(ServerDescriptor):
if key_hash != self.fingerprint.lower():
raise ValueError('Fingerprint does not match the hash of our signing key (fingerprint: %s, signing key hash: %s)' % (self.fingerprint.lower(), key_hash))
- if stem.prereq.is_crypto_available():
+ if not skip_crypto_validation and stem.prereq.is_crypto_available():
signed_digest = self._digest_for_signature(self.signing_key, self.signature)
if signed_digest != self.digest():
@@ -790,6 +808,14 @@ class RelayDescriptor(ServerDescriptor):
if stem.prereq._is_pynacl_available() and self.certificate:
self.certificate.validate(self)
+ @classmethod
+ def content(cls, attr = None, exclude = (), sign = False):
+ return _descriptor_content(attr, exclude, RELAY_SERVER_HEADER, RELAY_SERVER_FOOTER)
+
+ @classmethod
+ def create(cls, attr = None, exclude = (), validate = True, sign = False):
+ return cls(cls.content(attr, exclude, sign), validate = validate, skip_crypto_validation = not sign)
+
@lru_cache()
def digest(self):
"""
diff --git a/test/mocking.py b/test/mocking.py
index c293cf0..4843a97 100644
--- a/test/mocking.py
+++ b/test/mocking.py
@@ -14,7 +14,6 @@ Helper functions for creating mock objects.
get_protocolinfo_response - stem.response.protocolinfo.ProtocolInfoResponse
stem.descriptor.server_descriptor
- get_relay_server_descriptor - RelayDescriptor
get_bridge_server_descriptor - BridgeDescriptor
stem.descriptor.microdescriptor
@@ -80,19 +79,6 @@ DOC_SIG = stem.descriptor.networkstatus.DocumentSignature(
'BF112F1C6D5543CFD0A32215ACABD4197B5279AD',
'-----BEGIN SIGNATURE-----%s-----END SIGNATURE-----' % CRYPTO_BLOB)
-RELAY_SERVER_HEADER = (
- ('router', 'caerSidi 71.35.133.197 9001 0 0'),
- ('published', '2012-03-01 17:15:27'),
- ('bandwidth', '153600 256000 104590'),
- ('reject', '*:*'),
- ('onion-key', '\n-----BEGIN RSA PUBLIC KEY-----%s-----END RSA PUBLIC KEY-----' % CRYPTO_BLOB),
- ('signing-key', '\n-----BEGIN RSA PUBLIC KEY-----%s-----END RSA PUBLIC KEY-----' % CRYPTO_BLOB),
-)
-
-RELAY_SERVER_FOOTER = (
- ('router-signature', '\n-----BEGIN SIGNATURE-----%s-----END SIGNATURE-----' % CRYPTO_BLOB),
-)
-
BRIDGE_SERVER_HEADER = (
('router', 'Unnamed 10.45.227.253 9001 0 0'),
('router-digest', '006FD96BA35E7785A6A3B8B75FE2E2435A13BDB4'),
@@ -366,33 +352,6 @@ def _get_descriptor_content(attr = None, exclude = (), header_template = (), foo
return stem.util.str_tools._to_bytes('\n'.join(header_content + remainder + footer_content))
-def get_relay_server_descriptor(attr = None, exclude = (), content = False, sign_content = False):
- """
- Provides the descriptor content for...
- stem.descriptor.server_descriptor.RelayDescriptor
-
- :param dict attr: keyword/value mappings to be included in the descriptor
- :param list exclude: mandatory keywords to exclude from the descriptor
- :param bool content: provides the str content of the descriptor rather than the class if True
- :param bool sign_content: sets a proper digest value if True
-
- :returns: RelayDescriptor for the requested descriptor content
- """
-
- desc_content = _get_descriptor_content(attr, exclude, RELAY_SERVER_HEADER, RELAY_SERVER_FOOTER)
-
- if content:
- return desc_content
- else:
- if sign_content:
- desc_content = sign_descriptor_content(desc_content)
-
- with patch('stem.prereq.is_crypto_available', Mock(return_value = False)):
- desc = stem.descriptor.server_descriptor.RelayDescriptor(desc_content, validate = True)
-
- return desc
-
-
def get_bridge_server_descriptor(attr = None, exclude = (), content = False):
"""
Provides the descriptor content for...
diff --git a/test/unit/descriptor/export.py b/test/unit/descriptor/export.py
index a262343..009c43d 100644
--- a/test/unit/descriptor/export.py
+++ b/test/unit/descriptor/export.py
@@ -11,10 +11,10 @@ except ImportError:
import stem.prereq
+from stem.descriptor.server_descriptor import RelayDescriptor
from stem.descriptor.export import export_csv, export_csv_file
from test.mocking import (
- get_relay_server_descriptor,
get_bridge_server_descriptor,
)
@@ -29,7 +29,7 @@ class TestExport(unittest.TestCase):
self.skipTest('(header added in python 2.7)')
return
- desc = get_relay_server_descriptor()
+ desc = RelayDescriptor.create()
desc_csv = export_csv(desc, included_fields = ('nickname', 'address', 'published'), header = False)
expected = 'caerSidi,71.35.133.197,2012-03-01 17:15:27\n'
@@ -50,7 +50,7 @@ class TestExport(unittest.TestCase):
for nickname in nicknames:
router_line = '%s 71.35.133.197 9001 0 0' % nickname
- descriptors.append(get_relay_server_descriptor({'router': router_line}))
+ descriptors.append(RelayDescriptor.create({'router': router_line}))
expected = '\n'.join(nicknames) + '\n'
self.assertEqual(expected, export_csv(descriptors, included_fields = ('nickname',), header = False))
@@ -61,7 +61,7 @@ class TestExport(unittest.TestCase):
the same output as export_csv().
"""
- desc = get_relay_server_descriptor()
+ desc = RelayDescriptor.create()
desc_csv = export_csv(desc)
csv_buffer = StringIO()
@@ -78,7 +78,7 @@ class TestExport(unittest.TestCase):
self.skipTest('(header added in python 2.7)')
return
- desc = get_relay_server_descriptor()
+ desc = RelayDescriptor.create()
desc_csv = export_csv(desc)
self.assertTrue(',signature' in desc_csv)
@@ -96,7 +96,7 @@ class TestExport(unittest.TestCase):
Attempts to make a csv with attributes that don't exist.
"""
- desc = get_relay_server_descriptor()
+ desc = RelayDescriptor.create()
self.assertRaises(ValueError, export_csv, desc, ('nickname', 'blarg!'))
def test_multiple_descriptor_types(self):
@@ -104,6 +104,6 @@ class TestExport(unittest.TestCase):
Attempts to make a csv with multiple descriptor types.
"""
- server_desc = get_relay_server_descriptor()
+ server_desc = RelayDescriptor.create()
bridge_desc = get_bridge_server_descriptor()
self.assertRaises(ValueError, export_csv, (server_desc, bridge_desc))
diff --git a/test/unit/descriptor/server_descriptor.py b/test/unit/descriptor/server_descriptor.py
index 747600c..1b754bf 100644
--- a/test/unit/descriptor/server_descriptor.py
+++ b/test/unit/descriptor/server_descriptor.py
@@ -21,7 +21,6 @@ from stem.descriptor.certificate import CertType, ExtensionType
from stem.descriptor.server_descriptor import RelayDescriptor, BridgeDescriptor
from test.mocking import (
- get_relay_server_descriptor,
get_bridge_server_descriptor,
CRYPTO_BLOB,
)
@@ -433,8 +432,7 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
attributes.
"""
- desc = get_relay_server_descriptor()
-
+ desc = RelayDescriptor.create()
self.assertEqual('caerSidi', desc.nickname)
self.assertEqual('71.35.133.197', desc.address)
self.assertEqual(None, desc.fingerprint)
@@ -445,7 +443,7 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
Includes an 'opt <keyword> <value>' entry.
"""
- desc = get_relay_server_descriptor({'opt': 'contact www.atagar.com/contact/'})
+ desc = RelayDescriptor.create({'opt': 'contact www.atagar.com/contact/'})
self.assertEqual(b'www.atagar.com/contact/', desc.contact)
def test_unrecognized_line(self):
@@ -453,7 +451,7 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
Includes unrecognized content in the descriptor.
"""
- desc = get_relay_server_descriptor({'pepperjack': 'is oh so tasty!'})
+ desc = RelayDescriptor.create({'pepperjack': 'is oh so tasty!'})
self.assertEqual(['pepperjack is oh so tasty!'], desc.get_unrecognized_lines())
def test_proceeding_line(self):
@@ -461,79 +459,72 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
Includes a line prior to the 'router' entry.
"""
- desc_text = b'hibernate 1\n' + get_relay_server_descriptor(content = True)
- self._expect_invalid_attr(desc_text)
+ desc_text = b'hibernate 1\n' + RelayDescriptor.content()
+ self._expect_invalid_attr_for_text(desc_text)
def test_trailing_line(self):
"""
Includes a line after the 'router-signature' entry.
"""
- desc_text = get_relay_server_descriptor(content = True) + b'\nhibernate 1'
- self._expect_invalid_attr(desc_text)
+ desc_text = RelayDescriptor.content() + b'\nhibernate 1'
+ self._expect_invalid_attr_for_text(desc_text)
def test_nickname_missing(self):
"""
Constructs with a malformed router entry.
"""
- desc_text = get_relay_server_descriptor({'router': ' 71.35.133.197 9001 0 0'}, content = True)
- self._expect_invalid_attr(desc_text, 'nickname')
+ self._expect_invalid_attr({'router': ' 71.35.133.197 9001 0 0'}, 'nickname')
def test_nickname_too_long(self):
"""
Constructs with a nickname that is an invalid length.
"""
- desc_text = get_relay_server_descriptor({'router': 'saberrider2008ReallyLongNickname 71.35.133.197 9001 0 0'}, content = True)
- self._expect_invalid_attr(desc_text, 'nickname')
+ self._expect_invalid_attr({'router': 'saberrider2008ReallyLongNickname 71.35.133.197 9001 0 0'}, 'nickname')
def test_nickname_invalid_char(self):
"""
Constructs with an invalid relay nickname.
"""
- desc_text = get_relay_server_descriptor({'router': '$aberrider2008 71.35.133.197 9001 0 0'}, content = True)
- self._expect_invalid_attr(desc_text, 'nickname')
+ self._expect_invalid_attr({'router': '$aberrider2008 71.35.133.197 9001 0 0'}, 'nickname')
def test_address_malformed(self):
"""
Constructs with an invalid ip address.
"""
- desc_text = get_relay_server_descriptor({'router': 'caerSidi 371.35.133.197 9001 0 0'}, content = True)
- self._expect_invalid_attr(desc_text, 'address')
+ self._expect_invalid_attr({'router': 'caerSidi 371.35.133.197 9001 0 0'}, 'address')
def test_port_too_high(self):
"""
Constructs with an ORPort that is too large.
"""
- desc_text = get_relay_server_descriptor({'router': 'caerSidi 71.35.133.197 900001 0 0'}, content = True)
- self._expect_invalid_attr(desc_text, 'or_port')
+ self._expect_invalid_attr({'router': 'caerSidi 71.35.133.197 900001 0 0'}, 'or_port')
def test_port_malformed(self):
"""
Constructs with an ORPort that isn't numeric.
"""
- desc_text = get_relay_server_descriptor({'router': 'caerSidi 71.35.133.197 900a1 0 0'}, content = True)
- self._expect_invalid_attr(desc_text, 'or_port')
+ self._expect_invalid_attr({'router': 'caerSidi 71.35.133.197 900a1 0 0'}, 'or_port')
def test_port_newline(self):
"""
Constructs with a newline replacing the ORPort.
"""
- desc_text = get_relay_server_descriptor({'router': 'caerSidi 71.35.133.197 \n 0 0'}, content = True)
- self._expect_invalid_attr(desc_text, 'or_port')
+ self._expect_invalid_attr({'router': 'caerSidi 71.35.133.197 \n 0 0'}, 'or_port')
def test_platform_empty(self):
"""
Constructs with an empty platform entry.
"""
- desc_text = get_relay_server_descriptor({'platform': ''}, content = True)
+ desc_text = RelayDescriptor.content({'platform': ''})
desc = RelayDescriptor(desc_text, validate = False)
self.assertEqual(b'', desc.platform)
@@ -547,7 +538,7 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
Parse a platform line belonging to a node-Tor relay.
"""
- desc = get_relay_server_descriptor({'platform': 'node-Tor 0.1.0 on Linux x86_64'})
+ desc = RelayDescriptor.create({'platform': 'node-Tor 0.1.0 on Linux x86_64'})
self.assertEqual(b'node-Tor 0.1.0 on Linux x86_64', desc.platform)
self.assertEqual(stem.version.Version('0.1.0'), desc.tor_version)
self.assertEqual('Linux x86_64', desc.operating_system)
@@ -557,8 +548,7 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
Constructs with a protocols line without circuit versions.
"""
- desc_text = get_relay_server_descriptor({'opt': 'protocols Link 1 2'}, content = True)
- self._expect_invalid_attr(desc_text, 'circuit_protocols')
+ self._expect_invalid_attr({'opt': 'protocols Link 1 2'}, 'circuit_protocols')
@patch('stem.prereq.is_crypto_available', Mock(return_value = False))
def test_published_leap_year(self):
@@ -567,20 +557,17 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
invalid.
"""
- desc_text = get_relay_server_descriptor({'published': '2011-02-29 04:03:19'}, content = True)
- self._expect_invalid_attr(desc_text, 'published')
+ self._expect_invalid_attr({'published': '2011-02-29 04:03:19'}, 'published')
- desc_text = get_relay_server_descriptor({'published': '2012-02-29 04:03:19'}, content = True)
- expected_published = datetime.datetime(2012, 2, 29, 4, 3, 19)
- self.assertEqual(expected_published, RelayDescriptor(desc_text).published)
+ desc = RelayDescriptor.create({'published': '2012-02-29 04:03:19'})
+ self.assertEqual(datetime.datetime(2012, 2, 29, 4, 3, 19), desc.published)
def test_published_no_time(self):
"""
Constructs with a published entry without a time component.
"""
- desc_text = get_relay_server_descriptor({'published': '2012-01-01'}, content = True)
- self._expect_invalid_attr(desc_text, 'published')
+ self._expect_invalid_attr({'published': '2012-01-01'}, 'published')
def test_read_and_write_history(self):
"""
@@ -591,7 +578,7 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
for field in ('read-history', 'write-history'):
value = '2005-12-16 18:00:48 (900 s) 81,8848,8927,8927,83,8848'
- desc = get_relay_server_descriptor({'opt %s' % field: value})
+ desc = RelayDescriptor.create({'opt %s' % field: value})
if field == 'read-history':
attr = (desc.read_history_end, desc.read_history_interval, desc.read_history_values)
@@ -610,8 +597,7 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
Parses a read-history with an empty value.
"""
- value = '2005-12-17 01:23:11 (900 s) '
- desc = get_relay_server_descriptor({'opt read-history': value})
+ desc = RelayDescriptor.create({'opt read-history': '2005-12-17 01:23:11 (900 s) '})
self.assertEqual(datetime.datetime(2005, 12, 17, 1, 23, 11), desc.read_history_end)
self.assertEqual(900, desc.read_history_interval)
self.assertEqual([], desc.read_history_values)
@@ -623,7 +609,7 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
"""
desc_text = b'@pepperjack very tasty\n@mushrooms not so much\n'
- desc_text += get_relay_server_descriptor(content = True)
+ desc_text += RelayDescriptor.content()
desc_text += b'\ntrailing text that should be invalid, ho hum'
# running _parse_file should provide an iterator with a single descriptor
@@ -631,7 +617,7 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
self.assertRaises(ValueError, list, desc_iter)
desc_text = b'@pepperjack very tasty\n@mushrooms not so much\n'
- desc_text += get_relay_server_descriptor(content = True)
+ desc_text += RelayDescriptor.content()
desc_iter = stem.descriptor.server_descriptor._parse_file(io.BytesIO(desc_text))
desc_entries = list(desc_iter)
@@ -649,9 +635,9 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
Constructs with a field appearing twice.
"""
- desc_text = get_relay_server_descriptor({'<replace>': ''}, content = True)
+ desc_text = RelayDescriptor.content({'<replace>': ''})
desc_text = desc_text.replace(b'<replace>', b'contact foo\ncontact bar')
- self._expect_invalid_attr(desc_text, 'contact', b'foo')
+ self._expect_invalid_attr_for_text(desc_text, 'contact', b'foo')
def test_missing_required_attr(self):
"""
@@ -659,7 +645,7 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
"""
for attr in stem.descriptor.server_descriptor.REQUIRED_FIELDS:
- desc_text = get_relay_server_descriptor(exclude = [attr], content = True)
+ desc_text = RelayDescriptor.content(exclude = [attr])
self.assertRaises(ValueError, RelayDescriptor, desc_text, True)
# check that we can still construct it without validation
@@ -680,24 +666,22 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
"""
fingerprint = '4F0C 867D F0EF 6816 0568 C826 838F 482C EA7C FE45'
- desc_text = get_relay_server_descriptor({'opt fingerprint': fingerprint}, content = True)
- self._expect_invalid_attr(desc_text, 'fingerprint', fingerprint.replace(' ', ''))
+ self._expect_invalid_attr({'opt fingerprint': fingerprint}, 'fingerprint', fingerprint.replace(' ', ''))
def test_ipv6_policy(self):
"""
Checks a 'ipv6-policy' line.
"""
- expected = stem.exit_policy.MicroExitPolicy('accept 22-23,53,80,110')
- desc = get_relay_server_descriptor({'ipv6-policy': 'accept 22-23,53,80,110'})
- self.assertEqual(expected, desc.exit_policy_v6)
+ desc = RelayDescriptor.create({'ipv6-policy': 'accept 22-23,53,80,110'})
+ self.assertEqual(stem.exit_policy.MicroExitPolicy('accept 22-23,53,80,110'), desc.exit_policy_v6)
def test_extrainfo_sha256_digest(self):
"""
Extrainfo descriptor line with both a hex and base64 encoded sha256 digest.
"""
- desc = get_relay_server_descriptor({'extra-info-digest': '03272BF7C68484AFBDA508DAE3734D809E4A5BC4 DWMz1AEdqPlcroubwx3lPEoGbT+oX7S2BH653sPIqfI'})
+ desc = RelayDescriptor.create({'extra-info-digest': '03272BF7C68484AFBDA508DAE3734D809E4A5BC4 DWMz1AEdqPlcroubwx3lPEoGbT+oX7S2BH653sPIqfI'})
self.assertEqual('03272BF7C68484AFBDA508DAE3734D809E4A5BC4', desc.extra_info_digest)
self.assertEqual('DWMz1AEdqPlcroubwx3lPEoGbT+oX7S2BH653sPIqfI', desc.extra_info_sha256_digest)
@@ -706,7 +690,7 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
Checks a 'proto' line.
"""
- desc = get_relay_server_descriptor({'proto': 'Cons=1 Desc=1 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 Link=1-4 LinkAuth=1 Microdesc=1 Relay=1-2'})
+ desc = RelayDescriptor.create({'proto': 'Cons=1 Desc=1 DirCache=1 HSDir=1 HSIntro=3 HSRend=1 Link=1-4 LinkAuth=1 Microdesc=1 Relay=1-2'})
self.assertEqual({'Cons': [1], 'Desc': [1], 'DirCache': [1], 'HSDir': [1], 'HSIntro': [3], 'HSRend': [1], 'Link': [1, 2, 3, 4], 'LinkAuth': [1], 'Microdesc': [1], 'Relay': [1, 2]}, desc.protocols)
def test_protocols_with_no_mapping(self):
@@ -715,7 +699,7 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
"""
exc_msg = "Protocol entires are expected to be a series of 'key=value' pairs but was: proto Desc Link=1-4"
- self.assertRaisesRegexp(ValueError, exc_msg, get_relay_server_descriptor, {'proto': 'Desc Link=1-4'})
+ self.assertRaisesRegexp(ValueError, exc_msg, RelayDescriptor.create, {'proto': 'Desc Link=1-4'})
def test_parse_with_non_int_version(self):
"""
@@ -723,14 +707,14 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
"""
exc_msg = 'Protocol values should be a number or number range, but was: proto Desc=hi Link=1-4'
- self.assertRaisesRegexp(ValueError, exc_msg, get_relay_server_descriptor, {'proto': 'Desc=hi Link=1-4'})
+ self.assertRaisesRegexp(ValueError, exc_msg, RelayDescriptor.create, {'proto': 'Desc=hi Link=1-4'})
def test_ntor_onion_key(self):
"""
Checks a 'ntor-onion-key' line.
"""
- desc = get_relay_server_descriptor({'ntor-onion-key': 'Od2Sj3UXFyDjwESLXk6fhatqW9z/oBL/vAKJ+tbDqUU='})
+ desc = RelayDescriptor.create({'ntor-onion-key': 'Od2Sj3UXFyDjwESLXk6fhatqW9z/oBL/vAKJ+tbDqUU='})
self.assertEqual('Od2Sj3UXFyDjwESLXk6fhatqW9z/oBL/vAKJ+tbDqUU=', desc.ntor_onion_key)
def test_minimal_bridge_descriptor(self):
@@ -775,7 +759,7 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
its unsanatized content.
"""
- desc_text = get_relay_server_descriptor({'router-digest': '006FD96BA35E7785A6A3B8B75FE2E2435A13BDB4'}, content = True)
+ desc_text = RelayDescriptor.content({'router-digest': '006FD96BA35E7785A6A3B8B75FE2E2435A13BDB4'})
desc = BridgeDescriptor(desc_text)
self.assertFalse(desc.is_scrubbed())
@@ -848,7 +832,10 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
desc = BridgeDescriptor(desc_text)
self.assertEqual(expected_or_addresses, desc.or_addresses)
- def _expect_invalid_attr(self, desc_text, attr = None, expected_value = None):
+ def _expect_invalid_attr(self, desc_attrs, attr = None, expected_value = None):
+ self._expect_invalid_attr_for_text(RelayDescriptor.content(desc_attrs), attr, expected_value)
+
+ def _expect_invalid_attr_for_text(self, desc_text, attr = None, expected_value = None):
"""
Asserts that construction will fail due to desc_text having a malformed
attribute. If an attr is provided then we check that it matches an expected
diff --git a/test/unit/tutorial.py b/test/unit/tutorial.py
index ac0bdd4..26ea2b1 100644
--- a/test/unit/tutorial.py
+++ b/test/unit/tutorial.py
@@ -165,7 +165,7 @@ class TestTutorial(unittest.TestCase):
@patch('stem.descriptor.reader.DescriptorReader', spec = DescriptorReader)
def test_mirror_mirror_on_the_wall_4(self, reader_mock, stdout_mock):
reader = reader_mock().__enter__()
- reader.__iter__.return_value = iter([mocking.get_relay_server_descriptor()])
+ reader.__iter__.return_value = iter([RelayDescriptor.create()])
exec_documentation_example('past_descriptors.py')
self.assertEqual('found relay caerSidi (None)\n', stdout_mock.getvalue())
@@ -206,16 +206,14 @@ class TestTutorial(unittest.TestCase):
if count > 15:
return
- exit_descriptor = mocking.get_relay_server_descriptor({
- 'router': 'speedyexit 149.255.97.109 9001 0 0'
- }, content = True).replace(b'reject *:*', b'accept *:*')
+ exit_descriptor = RelayDescriptor.content({'router': 'speedyexit 149.255.97.109 9001 0 0'}).replace(b'reject *:*', b'accept *:*')
exit_descriptor = mocking.sign_descriptor_content(exit_descriptor)
exit_descriptor = RelayDescriptor(exit_descriptor)
downloader_mock().get_server_descriptors().run.return_value = [
exit_descriptor,
- mocking.get_relay_server_descriptor(), # non-exit
+ RelayDescriptor.create(), # non-exit
exit_descriptor,
exit_descriptor,
]
diff --git a/test/unit/tutorial_examples.py b/test/unit/tutorial_examples.py
index de5079e..267fd3d 100644
--- a/test/unit/tutorial_examples.py
+++ b/test/unit/tutorial_examples.py
@@ -18,12 +18,12 @@ import stem.prereq
from stem.control import Controller
from stem.util import str_type
from stem.descriptor.remote import DIRECTORY_AUTHORITIES
+from stem.descriptor.server_descriptor import RelayDescriptor
from test import mocking
from test.unit import exec_documentation_example
from test.mocking import (
- get_relay_server_descriptor,
get_router_status_entry_v3,
ROUTER_STATUS_ENTRY_V3_HEADER,
get_network_status_document_v3,
@@ -228,10 +228,10 @@ class TestTutorialExamples(unittest.TestCase):
@patch('stem.descriptor.remote.DescriptorDownloader')
def test_outdated_relays(self, downloader_mock, stdout_mock):
downloader_mock().get_server_descriptors.return_value = [
- get_relay_server_descriptor({'platform': 'node-Tor 0.2.3.0 on Linux x86_64'}),
- get_relay_server_descriptor({'platform': 'node-Tor 0.1.0 on Linux x86_64'}),
- get_relay_server_descriptor({'opt': 'contact Random Person admin@xxxxxxxxx', 'platform': 'node-Tor 0.2.3.0 on Linux x86_64'}),
- get_relay_server_descriptor({'opt': 'contact Sambuddha Basu', 'platform': 'node-Tor 0.1.0 on Linux x86_64'}),
+ RelayDescriptor.create({'platform': 'node-Tor 0.2.3.0 on Linux x86_64'}),
+ RelayDescriptor.create({'platform': 'node-Tor 0.1.0 on Linux x86_64'}),
+ RelayDescriptor.create({'opt': 'contact Random Person admin@xxxxxxxxx', 'platform': 'node-Tor 0.2.3.0 on Linux x86_64'}),
+ RelayDescriptor.create({'opt': 'contact Sambuddha Basu', 'platform': 'node-Tor 0.1.0 on Linux x86_64'}),
]
exec_documentation_example('outdated_relays.py')
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits