Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
337 additions
and
91 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
|
||
from mock import Mock | ||
|
||
from twisted.trial import unittest | ||
|
||
from txtorcon._microdesc_parser import MicrodescriptorParser | ||
|
||
|
||
class ParserTests(unittest.TestCase): | ||
|
||
def test_two_no_w(self): | ||
relays = [] | ||
|
||
def create_relay(**kw): | ||
relays.append(kw) | ||
m = MicrodescriptorParser(create_relay) | ||
|
||
for line in [ | ||
'r fake YkkmgCNRV1/35OPWDvo7+1bmfoo tanLV/4ZfzpYQW0xtGFqAa46foo 2011-12-12 16:29:16 11.11.11.11 443 80', | ||
's Exit Fast Guard HSDir Named Running Stable V2Dir Valid FutureProof', | ||
'r ekaf foooooooooooooooooooooooooo barbarbarbarbarbarbarbarbar 2011-11-11 16:30:00 22.22.22.22 443 80', | ||
's Exit Fast Guard HSDir Named Running Stable V2Dir Valid FutureProof', | ||
]: | ||
m.feed_line(line) | ||
m.done() | ||
|
||
self.assertEqual(2, len(relays)) | ||
self.assertEqual('fake', relays[0]['nickname']) | ||
self.assertEqual('ekaf', relays[1]['nickname']) | ||
self.assertEqual('11.11.11.11', relays[0]['ip']) | ||
self.assertEqual('22.22.22.22', relays[1]['ip']) | ||
|
||
self.assertTrue('bandwidth' not in relays[0]) | ||
self.assertTrue('bandwidth' not in relays[1]) | ||
self.assertTrue('flags' in relays[0]) | ||
self.assertTrue('flags' in relays[1]) | ||
self.assertTrue('FutureProof' in relays[1]['flags']) | ||
|
||
def test_two(self): | ||
relays = [] | ||
|
||
def create_relay(**kw): | ||
relays.append(kw) | ||
m = MicrodescriptorParser(create_relay) | ||
|
||
for line in [ | ||
'r fake YkkmgCNRV1/35OPWDvo7+1bmfoo tanLV/4ZfzpYQW0xtGFqAa46foo 2011-12-12 16:29:16 11.11.11.11 443 80', | ||
's Exit Fast Guard HSDir Named Running Stable V2Dir Valid FutureProof', | ||
'r ekaf foooooooooooooooooooooooooo barbarbarbarbarbarbarbarbar 2011-11-11 16:30:00 22.22.22.22 443 80', | ||
's Exit Fast Guard HSDir Named Running Stable V2Dir Valid FutureProof', | ||
'w Bandwidth=518000', | ||
'p accept 43,53,79-81', | ||
]: | ||
m.feed_line(line) | ||
m.done() | ||
|
||
self.assertEqual(2, len(relays)) | ||
self.assertEqual('fake', relays[0]['nickname']) | ||
self.assertEqual('ekaf', relays[1]['nickname']) | ||
self.assertEqual('11.11.11.11', relays[0]['ip']) | ||
self.assertEqual('22.22.22.22', relays[1]['ip']) | ||
|
||
self.assertTrue('bandwidth' not in relays[0]) | ||
self.assertTrue('bandwidth' in relays[1]) | ||
self.assertTrue('flags' in relays[0]) | ||
self.assertTrue('flags' in relays[1]) | ||
self.assertTrue('FutureProof' in relays[1]['flags']) | ||
|
||
def test_bad_line(self): | ||
relays = [] | ||
|
||
def create_relay(**kw): | ||
relays.append(kw) | ||
m = MicrodescriptorParser(create_relay) | ||
|
||
with self.assertRaises(Exception) as ctx: | ||
m.feed_line('x blam') | ||
self.assertTrue('Unknown microdescriptor' in str(ctx.exception)) | ||
self.assertEqual(0, len(relays)) | ||
|
||
def test_single_ipv6(self): | ||
relays = [] | ||
|
||
def create_relay(**kw): | ||
relays.append(kw) | ||
m = MicrodescriptorParser(create_relay) | ||
|
||
for line in [ | ||
'r fake YkkmgCNRV1/35OPWDvo7+1bmfoo tanLV/4ZfzpYQW0xtGFqAa46foo 2011-12-12 16:29:16 11.11.11.11 443 80', | ||
'a [2001:0:0:0::0]:4321' | ||
]: | ||
m.feed_line(line) | ||
m.done() | ||
|
||
self.assertEqual(1, len(relays)) | ||
self.assertEqual(['[2001:0:0:0::0]:4321'], list(relays[0]['ip_v6'])) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
|
||
from .util import find_keywords | ||
import automat | ||
|
||
class MicrodescriptorParser(object): | ||
""" | ||
Parsers microdescriptors line by line. New relays are emitted via | ||
the 'create_relay' callback. | ||
""" | ||
_machine = automat.MethodicalMachine() | ||
|
||
def __init__(self, create_relay): | ||
self._create_relay = create_relay | ||
self._relay_attrs = None | ||
|
||
@_machine.input() | ||
def line(self, line): | ||
""" | ||
A line has been received. | ||
""" | ||
|
||
@_machine.input() | ||
def done(self, *args): | ||
""" | ||
All lines have been fed. | ||
""" | ||
|
||
@_machine.input() | ||
def _r_line(self, *args): | ||
pass | ||
|
||
@_machine.input() | ||
def _s_line(self, *args): | ||
pass | ||
|
||
@_machine.input() | ||
def _w_line(self, *args): | ||
pass | ||
|
||
@_machine.input() | ||
def _p_line(self, *args): | ||
pass | ||
|
||
@_machine.input() | ||
def _a_line(self, *args): | ||
pass | ||
|
||
@_machine.output() | ||
def create_relay(self, *args): | ||
r = self._create_relay(**self._relay_attrs) | ||
self._relay_attrs = None | ||
return r | ||
|
||
@_machine.output() | ||
def start_relay(self, *args): | ||
self._relay_attrs = dict() | ||
|
||
@_machine.output() | ||
def _parse_r(self, *args): | ||
assert len(args) >= 8 | ||
self._relay_attrs.update(dict( | ||
nickname=args[0], | ||
idhash=args[1], | ||
orhash=args[2], | ||
modified=args[3] + ' ' + args[4], | ||
ip=args[5], | ||
orport=args[6], | ||
dirport=args[7], | ||
)) | ||
|
||
@_machine.output() | ||
def _parse_s(self, *args): | ||
self._relay_attrs['flags'] = args | ||
|
||
@_machine.output() | ||
def _parse_w(self, *args): | ||
# should only contain "bandwidth" ... | ||
kw = find_keywords(args) | ||
assert 'Bandwidth' in kw | ||
self._relay_attrs['bandwidth'] = kw['Bandwidth'] | ||
|
||
@_machine.output() | ||
def _parse_a(self, *args): | ||
try: | ||
self._relay_attrs['ip_v6'].extend(args) | ||
except KeyError: | ||
self._relay_attrs['ip_v6'] = list(args) | ||
|
||
@_machine.output() | ||
def _error(self, *args): | ||
raise RuntimeError("Illegal state in microdescriptor parser") | ||
|
||
@_machine.state(initial=True) | ||
def waiting_r(self): | ||
""" | ||
waiting for an 'r' line | ||
""" | ||
|
||
@_machine.state() | ||
def waiting_s(self): | ||
""" | ||
waiting for an 's' line | ||
""" | ||
|
||
@_machine.state() | ||
def waiting_w(self): | ||
""" | ||
waiting for an 's' line | ||
""" | ||
|
||
@_machine.state() | ||
def error(self): | ||
""" | ||
something bad happened | ||
""" | ||
|
||
def feed_line(self, data): | ||
args = data.split() | ||
try: | ||
{ | ||
'r': self._r_line, | ||
's': self._s_line, | ||
'w': self._w_line, | ||
'p': self._p_line, | ||
'a': self._a_line, | ||
'OK': lambda: None, # ignore | ||
'ns/all=': lambda: None, # ignore | ||
'.': lambda: None, # ignore | ||
}[args[0]](*args[1:]) | ||
except KeyError: | ||
raise Exception( | ||
'Unknown microdescriptor line: "{}"'.format(args[0]) | ||
) | ||
|
||
waiting_r.upon( | ||
_r_line, | ||
enter=waiting_s, | ||
outputs=[start_relay, _parse_r], | ||
) | ||
waiting_r.upon( | ||
_p_line, | ||
enter=waiting_r, | ||
outputs=[], | ||
) | ||
waiting_r.upon( | ||
done, | ||
enter=waiting_r, | ||
outputs=[], | ||
) | ||
|
||
waiting_s.upon( | ||
_r_line, | ||
enter=error, | ||
outputs=[_error], | ||
) | ||
# waiting_s.upon( | ||
# _r_line, | ||
# enter=waiting_s, | ||
# outputs=[create_relay, start_relay, _parse_r], | ||
# ) | ||
waiting_s.upon( | ||
_s_line, | ||
enter=waiting_w, | ||
outputs=[_parse_s], | ||
) | ||
waiting_s.upon( | ||
_a_line, | ||
enter=waiting_s, | ||
outputs=[_parse_a], | ||
) | ||
waiting_s.upon( | ||
done, | ||
enter=waiting_r, | ||
outputs=[create_relay], | ||
) | ||
|
||
waiting_w.upon( | ||
_w_line, | ||
enter=waiting_r, | ||
outputs=[_parse_w, create_relay], | ||
) | ||
waiting_w.upon( | ||
_r_line, | ||
enter=waiting_s, | ||
outputs=[create_relay, start_relay, _parse_r], | ||
) | ||
waiting_w.upon( | ||
_a_line, | ||
enter=waiting_w, | ||
outputs=[_parse_a], | ||
) | ||
waiting_w.upon( | ||
done, | ||
enter=waiting_r, | ||
outputs=[create_relay], | ||
) |
Oops, something went wrong.