[tor-commits] [stem/master] Parsing network-status-version attribute

atagar at torproject.org atagar at torproject.org
Sat Oct 13 18:35:45 UTC 2012


commit 5e12da0a50ee60577f3a571eea0ef3350e146d10
Author: Damian Johnson <atagar at torproject.org>
Date:   Sat Sep 8 12:15:34 2012 -0700

    Parsing network-status-version attribute
    
    I don't like our assumption that this is a v3 network status document, but I'll
    need to think more about how to handle other versions later. For now keeping
    the validation assertion that parsed documents are v3.
---
 stem/descriptor/networkstatus.py               |   56 +++++++++++++++---------
 test/integ/descriptor/networkstatus.py         |    4 +-
 test/unit/descriptor/networkstatus/document.py |   16 ++++++-
 3 files changed, 50 insertions(+), 26 deletions(-)

diff --git a/stem/descriptor/networkstatus.py b/stem/descriptor/networkstatus.py
index 2140b4d..d554d42 100644
--- a/stem/descriptor/networkstatus.py
+++ b/stem/descriptor/networkstatus.py
@@ -198,7 +198,7 @@ class NetworkStatusDocument(stem.descriptor.Descriptor):
   
   :var tuple routers: RouterStatusEntry contained in the document
   
-  :var str network_status_version: **\*** document version
+  :var str version: **\*** document version
   :var str vote_status: **\*** status of the vote (is either "vote" or "consensus")
   :var int consensus_method: **~** consensus method used to generate a consensus
   :var list consensus_methods: **^** A list of supported consensus generation methods (integers)
@@ -236,7 +236,7 @@ class NetworkStatusDocument(stem.descriptor.Descriptor):
     self.directory_authorities = []
     self.directory_signatures = []
     
-    self.network_status_version = None
+    self.version = None
     self.vote_status = None
     self.consensus_methods = []
     self.published = None
@@ -255,8 +255,8 @@ class NetworkStatusDocument(stem.descriptor.Descriptor):
     document_file = StringIO(raw_content)
     header, footer, routers_end = _get_document_content(document_file, validate)
     
-    self._parse_old(header + footer, validate)
     self._parse(header, footer, validate)
+    self._parse_old(header + footer, validate)
     
     if document_file.tell() < routers_end:
       self.routers = tuple(_get_routers(document_file, validate, self, routers_end, self._get_router_type()))
@@ -266,9 +266,6 @@ class NetworkStatusDocument(stem.descriptor.Descriptor):
   def _get_router_type(self):
     return RouterStatusEntry
   
-  def _validate_network_status_version(self):
-    return self.network_status_version == "3"
-  
   def get_unrecognized_lines(self):
     """
     Returns any unrecognized trailing lines.
@@ -292,23 +289,27 @@ class NetworkStatusDocument(stem.descriptor.Descriptor):
     header_entries = stem.descriptor._get_descriptor_components(header, validate)[0]
     footer_entries = stem.descriptor._get_descriptor_components(footer, validate)[0]
     
-    if validate:
-      if not 'vote-status' in header_entries:
-        raise ValueError("Network status documents must have a 'vote-status' line to say if they're a vote or consensus")
-      
+    all_entries = dict()
+    all_entries.update(header_entries)
+    all_entries.update(footer_entries)
+    
+    if 'vote-status' in header_entries:
       is_consensus = header_entries['vote-status'][0][0] == "consensus"
       is_vote = not is_consensus
+    else:
+      if validate:
+        raise ValueError("Network status documents must have a 'vote-status' line to say if they're a vote or consensus")
+      
+      is_consensus, is_vote = True, False
+    
+    if validate:
       self._check_for_missing_and_disallowed_fields(is_consensus, header_entries, footer_entries)
       self._check_for_misordered_fields(is_consensus, header_entries, footer_entries)
     
     known_fields = [attr[0] for attr in HEADER_STATUS_DOCUMENT_FIELDS + FOOTER_STATUS_DOCUMENT_FIELDS]
     content = header + '\n' + footer
     
-    entries = dict()
-    entries.update(header_entries)
-    entries.update(footer_entries)
-    
-    for keyword, values in entries.items():
+    for keyword, values in all_entries.items():
       value, block_contents = values[0]
       line = "%s %s" % (keyword, value)
       
@@ -318,15 +319,28 @@ class NetworkStatusDocument(stem.descriptor.Descriptor):
       if validate and len(values) > 1 and keyword in known_fields:
         if not (keyword == 'directory-signature' and is_consensus):
           raise ValueError("Network status documents can only have a single '%s' line, got %i:\n%s" % (keyword, len(values), content))
+      
+      if keyword == 'network-status-version':
+        # "network-status-version" version
+        
+        self.version = value
+        
+        # TODO: Obviously not right when we extend this to parse v2 documents,
+        # but we'll cross that bridge when we come to it.
+        
+        if validate and self.version != "3":
+          raise ValueError("Expected a version 3 network status documents, got version '%s' instead" % self.version)
   
   def _parse_old(self, raw_content, validate):
     # preamble
     content = StringIO(raw_content)
     read_keyword_line = lambda keyword, optional = False: setattr(self, keyword.replace("-", "_"), _read_keyword_line(keyword, content, validate, optional))
     
-    map(read_keyword_line, ["network-status-version", "vote-status"])
-    if validate and not self._validate_network_status_version():
-      raise ValueError("Invalid network-status-version: %s" % self.network_status_version)
+    # ignore things the parse() method handles
+    _read_keyword_line("network-status-version", content, False, True)
+    
+    
+    map(read_keyword_line, ["vote-status"])
     
     vote = False
     if self.vote_status == "vote": vote = True
@@ -479,7 +493,7 @@ class NetworkStatusDocument(stem.descriptor.Descriptor):
       if actual != expected:
         actual_label = ', '.join(actual)
         expected_label = ', '.join(expected)
-        raise ValueError("The fields in the document's %s are misordered. It should be '%s' but was '%s'" % (lable, actual_label, expected_label))
+        raise ValueError("The fields in the document's %s are misordered. It should be '%s' but was '%s'" % (label, actual_label, expected_label))
 
 class DirectoryAuthority(stem.descriptor.Descriptor):
   """
@@ -855,7 +869,7 @@ class MicrodescriptorConsensus(NetworkStatusDocument):
   """
   A v3 microdescriptor consensus.
   
-  :var str network_status_version: **\*** a document format version. For v3 microdescriptor consensuses this is "3 microdesc"
+  :var str version: **\*** a document format version. For v3 microdescriptor consensuses this is "3 microdesc"
   :var str vote_status: **\*** status of the vote (is "consensus")
   :var int consensus_method: **~** consensus method used to generate a consensus
   :var datetime valid_after: **\*** time when the consensus becomes valid
@@ -879,7 +893,7 @@ class MicrodescriptorConsensus(NetworkStatusDocument):
     return RouterMicrodescriptor
   
   def _validate_network_status_version(self):
-    return self.network_status_version == "3 microdesc"
+    return self.version == "3 microdesc"
 
 class RouterMicrodescriptor(RouterStatusEntry):
   """
diff --git a/test/integ/descriptor/networkstatus.py b/test/integ/descriptor/networkstatus.py
index 4ca8e5c..1777828 100644
--- a/test/integ/descriptor/networkstatus.py
+++ b/test/integ/descriptor/networkstatus.py
@@ -85,7 +85,7 @@ class TestNetworkStatus(unittest.TestCase):
     router1 = desc.routers[0]
     descriptor_file.close()
     
-    self.assertEquals("3", desc.network_status_version)
+    self.assertEquals("3", desc.version)
     self.assertEquals("consensus", desc.vote_status)
     self.assertEquals([], desc.consensus_methods)
     self.assertEquals(None, desc.published)
@@ -177,7 +177,7 @@ I/TJmV928na7RLZe2mGHCAW3VQOvV+QkCfj05VZ8CsY=
     router1 = desc.routers[0]
     descriptor_file.close()
     
-    self.assertEquals("3", desc.network_status_version)
+    self.assertEquals("3", desc.version)
     self.assertEquals("vote", desc.vote_status)
     self.assertEquals(range(1, 13), desc.consensus_methods)
     self.assertEquals(_strptime("2012-07-11 23:50:01"), desc.published)
diff --git a/test/unit/descriptor/networkstatus/document.py b/test/unit/descriptor/networkstatus/document.py
index ef77391..a099fa9 100644
--- a/test/unit/descriptor/networkstatus/document.py
+++ b/test/unit/descriptor/networkstatus/document.py
@@ -100,7 +100,7 @@ class TestNetworkStatusDocument(unittest.TestCase):
     sig = DirectorySignature("directory-signature " + NETWORK_STATUS_DOCUMENT_ATTR["directory-signature"])
     
     self.assertEqual((), document.routers)
-    self.assertEqual("3", document.network_status_version)
+    self.assertEqual("3", document.version)
     self.assertEqual("consensus", document.vote_status)
     self.assertEqual(9, document.consensus_method)
     self.assertEqual([], document.consensus_methods)
@@ -133,7 +133,7 @@ class TestNetworkStatusDocument(unittest.TestCase):
     sig = DirectorySignature("directory-signature " + NETWORK_STATUS_DOCUMENT_ATTR["directory-signature"])
     
     self.assertEqual((), document.routers)
-    self.assertEqual("3", document.network_status_version)
+    self.assertEqual("3", document.version)
     self.assertEqual("vote", document.vote_status)
     self.assertEqual(None, document.consensus_method)
     self.assertEqual([9], document.consensus_methods)
@@ -213,5 +213,15 @@ class TestNetworkStatusDocument(unittest.TestCase):
         content = "\n".join(test_lines)
         self.assertRaises(ValueError, NetworkStatusDocument, content)
         NetworkStatusDocument(content, False) # constructs without validation
-
+  
+  def test_invalid_version(self):
+    """
+    Try parsing a different document version with the v3 parser.
+    """
+    
+    content = get_network_status_document({"network-status-version": "4"})
+    self.assertRaises(ValueError, NetworkStatusDocument, content)
+    
+    document = NetworkStatusDocument(content, False)
+    self.assertEquals("4", document.version)
 





More information about the tor-commits mailing list