Skip to content

Commit

Permalink
Merge pull request #231 from meejah/when_closed_when_built
Browse files Browse the repository at this point in the history
When closed when built
  • Loading branch information
meejah committed May 5, 2017
2 parents bb530e2 + 8c57b14 commit 3e66d76
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 26 deletions.
28 changes: 27 additions & 1 deletion test/test_circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ def test_close_circuit(self):
# we already pretended that Tor answered "OK" to the
# CLOSECIRCUIT call (see close_circuit() in FakeTorController
# above) however the circuit isn't "really" closed yet...
self.assertTrue(not d0.called)
self.assertTrue(not d0.result.called)
# not unit-test-y? shouldn't probably delve into internals I
# suppose...
self.assertTrue(circuit._closing_deferred is not None)
Expand Down Expand Up @@ -463,6 +463,32 @@ def test_is_built(self):
self.assertTrue(built1.result == circuit)
self.assertTrue(built2.result == circuit)

def test_when_closed(self):
tor = FakeTorController()
a = FakeRouter('$E11D2B2269CC25E67CA6C9FB5843497539A74FD0', 'a')
b = FakeRouter('$50DD343021E509EB3A5A7FD0D8A4F8364AFBDCB5', 'b')
c = FakeRouter('$253DFF1838A2B7782BE7735F74E50090D46CA1BC', 'c')
tor.routers['$E11D2B2269CC25E67CA6C9FB5843497539A74FD0'] = a
tor.routers['$50DD343021E509EB3A5A7FD0D8A4F8364AFBDCB5'] = b
tor.routers['$253DFF1838A2B7782BE7735F74E50090D46CA1BC'] = c

circuit = Circuit(tor)
circuit.listen(tor)

circuit.update('123 EXTENDED $E11D2B2269CC25E67CA6C9FB5843497539A74FD0=eris,$50DD343021E509EB3A5A7FD0D8A4F8364AFBDCB5=venus,$253DFF1838A2B7782BE7735F74E50090D46CA1BC=chomsky PURPOSE=GENERAL'.split())
d0 = circuit.when_closed()

self.assertFalse(d0.called)

circuit.update('123 BUILT $E11D2B2269CC25E67CA6C9FB5843497539A74FD0=eris,$50DD343021E509EB3A5A7FD0D8A4F8364AFBDCB5=venus,$253DFF1838A2B7782BE7735F74E50090D46CA1BC=chomsky PURPOSE=GENERAL'.split())
circuit.update('123 CLOSED'.split())

d1 = circuit.when_closed()

self.assertTrue(d0 is not d1)
self.assertTrue(d0.called)
self.assertTrue(d1.called)

def test_is_built_errback(self):
tor = FakeTorController()
a = FakeRouter('$E11D2B2269CC25E67CA6C9FB5843497539A74FD0', 'a')
Expand Down
37 changes: 18 additions & 19 deletions txtorcon/circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from zope.interface import implementer

from .interface import IRouterContainer, IStreamAttacher
from txtorcon.util import find_keywords, maybe_ip_addr
from txtorcon.util import find_keywords, maybe_ip_addr, SingleObserver


# look like "2014-01-25T02:12:14.593772"
Expand Down Expand Up @@ -211,8 +211,9 @@ def __init__(self, routercontainer):
# caches parsed value for time_created()
self._time_created = None

# all notifications for when_built
self._when_built = []
# all notifications for when_built, when_closed
self._when_built = SingleObserver()
self._when_closed = SingleObserver()

# XXX backwards-compat for old .is_built for now
@property
Expand All @@ -232,12 +233,18 @@ def when_built(self):
Tor before it gets to BUILT) you will receive an errback
"""
# XXX note to self: we never do an errback; fix this behavior
d = defer.Deferred()
if self.state == 'BUILT':
d.callback(self)
else:
self._when_built.append(d)
return d
return defer.succeed(self)
return self._when_built.when_fired()

def when_closed(self):
"""
Returns a Deferred that callback()'s (with this Circuit instance)
when this circuit hits CLOSED or FAILED.
"""
if self.state in ['CLOSED', 'FAILED']:
return defer.succeed(self)
return self._when_closed.when_fired()

def web_agent(self, reactor, socks_endpoint, pool=None):
"""
Expand Down Expand Up @@ -355,7 +362,7 @@ def close_command_is_queued(*args):
return self._closing_deferred
d = self._torstate.close_circuit(self.id, **kw)
d.addCallback(close_command_is_queued)
return self._closing_deferred
return d

def age(self, now=None):
"""
Expand Down Expand Up @@ -413,7 +420,7 @@ def update(self, args):
if self.state == 'BUILT':
for x in self.listeners:
x.circuit_built(self)
self._notify_when_built()
self._when_built.fire(self)

elif self.state == 'CLOSED':
if len(self.streams) > 0:
Expand Down Expand Up @@ -443,15 +450,6 @@ def update(self, args):
for x in self.listeners:
x.circuit_failed(self, **flags)

# XXX should use the util helper
def _notify_when_built(self, err=None):
for d in self._when_built:
if err is None:
d.callback(self)
else:
d.errback(Failure(err))
self._when_built = []

def maybe_call_closing_deferred(self):
"""
Used internally to callback on the _closing_deferred if it
Expand All @@ -461,6 +459,7 @@ def maybe_call_closing_deferred(self):
if self._closing_deferred:
self._closing_deferred.callback(self)
self._closing_deferred = None
self._when_closed.fire(self)

def update_path(self, path):
"""
Expand Down
13 changes: 7 additions & 6 deletions txtorcon/torstate.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import warnings

from twisted.internet import defer
from twisted.python.failure import Failure
from twisted.internet.endpoints import TCP4ClientEndpoint
from twisted.internet.endpoints import UNIXClientEndpoint
from twisted.internet.interfaces import IReactorCore
Expand Down Expand Up @@ -920,23 +921,23 @@ def circuit_new(self, circuit):
def circuit_destroy(self, circuit):
"Used by circuit_closed and circuit_failed (below)"
txtorlog.msg("circuit_destroy:", circuit.id)
circuit._notify_when_built(
Exception("Destroying circuit; will never hit BUILT")
circuit._when_built.fire(
Failure(Exception("Destroying circuit; will never hit BUILT"))
)
del self.circuits[circuit.id]

def circuit_closed(self, circuit, **kw):
"ICircuitListener API"
txtorlog.msg("circuit_closed", circuit)
circuit._notify_when_built(
Exception("Circuit closed ('{}')".format(_extract_reason(kw)))
circuit._when_built.fire(
Failure(Exception("Circuit closed ('{}')".format(_extract_reason(kw))))
)
self.circuit_destroy(circuit)

def circuit_failed(self, circuit, **kw):
"ICircuitListener API"
txtorlog.msg("circuit_failed", circuit, str(kw))
circuit._notify_when_built(
Exception("Circuit failed ('{}')".format(_extract_reason(kw)))
circuit._when_built.fire(
Failure(Exception("Circuit failed ('{}')".format(_extract_reason(kw))))
)
self.circuit_destroy(circuit)

0 comments on commit 3e66d76

Please sign in to comment.