transcribe: stop automatically doing close() on error
And provide a close() that can live at the end of a Deferred chain, so callers can do d.addBoth(w.close). I like auto-close-on-error in general, but I'm removing it so I can clean up the error-handling pathways. It will probably come back later. The constraint is that it must be possible to wait on the return Deferred that close() gives you (to synchronize tests, or keep the CLI program running long enough to deallocate the channel) even if something else (and error handler) called close() earlier. This will require either a OneShotObserverList, or keeping a "deallocated" Deferred around in case more callers want to wait on it later.
This commit is contained in:
parent
8d0bcf9f82
commit
a4c1ba9e4e
|
@ -27,30 +27,6 @@ def make_confmsg(confkey, nonce):
|
||||||
def to_bytes(u):
|
def to_bytes(u):
|
||||||
return unicodedata.normalize("NFC", u).encode("utf-8")
|
return unicodedata.normalize("NFC", u).encode("utf-8")
|
||||||
|
|
||||||
def close_on_error(meth): # method decorator
|
|
||||||
# Clients report certain errors as "moods", so the server can make a
|
|
||||||
# rough count failed connections (due to mismatched passwords, attacks,
|
|
||||||
# or timeouts). We don't report precondition failures, as those are the
|
|
||||||
# responsibility/fault of the local application code. We count
|
|
||||||
# non-precondition errors in case they represent server-side problems.
|
|
||||||
def _wrapper(self, *args, **kwargs):
|
|
||||||
d = defer.maybeDeferred(meth, self, *args, **kwargs)
|
|
||||||
def _onerror(f):
|
|
||||||
if f.check(Timeout):
|
|
||||||
d2 = self.close(u"lonely")
|
|
||||||
elif f.check(WrongPasswordError):
|
|
||||||
d2 = self.close(u"scary")
|
|
||||||
elif f.check(TypeError, UsageError):
|
|
||||||
# preconditions don't warrant _close_with_error()
|
|
||||||
d2 = defer.succeed(None)
|
|
||||||
else:
|
|
||||||
d2 = self.close(u"errory")
|
|
||||||
d2.addBoth(lambda _: f)
|
|
||||||
return d2
|
|
||||||
d.addErrback(_onerror)
|
|
||||||
return d
|
|
||||||
return _wrapper
|
|
||||||
|
|
||||||
class WSClient(websocket.WebSocketClientProtocol):
|
class WSClient(websocket.WebSocketClientProtocol):
|
||||||
def onOpen(self):
|
def onOpen(self):
|
||||||
self.wormhole_open = True
|
self.wormhole_open = True
|
||||||
|
@ -369,7 +345,6 @@ class Wormhole:
|
||||||
self._msg1 = unhexlify(d["msg1"].encode("ascii"))
|
self._msg1 = unhexlify(d["msg1"].encode("ascii"))
|
||||||
return self
|
return self
|
||||||
|
|
||||||
@close_on_error
|
|
||||||
@inlineCallbacks
|
@inlineCallbacks
|
||||||
def get_verifier(self):
|
def get_verifier(self):
|
||||||
if self._closed: raise UsageError
|
if self._closed: raise UsageError
|
||||||
|
@ -461,7 +436,6 @@ class Wormhole:
|
||||||
data = box.decrypt(encrypted)
|
data = box.decrypt(encrypted)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
@close_on_error
|
|
||||||
@inlineCallbacks
|
@inlineCallbacks
|
||||||
def send_data(self, outbound_data, phase=u"data", wait=False):
|
def send_data(self, outbound_data, phase=u"data", wait=False):
|
||||||
if not isinstance(outbound_data, type(b"")):
|
if not isinstance(outbound_data, type(b"")):
|
||||||
|
@ -484,7 +458,6 @@ class Wormhole:
|
||||||
yield self._msg_send(phase, outbound_encrypted, wait)
|
yield self._msg_send(phase, outbound_encrypted, wait)
|
||||||
self._timing.finish_event(_sent)
|
self._timing.finish_event(_sent)
|
||||||
|
|
||||||
@close_on_error
|
|
||||||
@inlineCallbacks
|
@inlineCallbacks
|
||||||
def get_data(self, phase=u"data"):
|
def get_data(self, phase=u"data"):
|
||||||
if not isinstance(phase, type(u"")): raise TypeError(type(phase))
|
if not isinstance(phase, type(u"")): raise TypeError(type(phase))
|
||||||
|
@ -509,26 +482,42 @@ class Wormhole:
|
||||||
# TODO: schedule reconnect, unless we're done
|
# TODO: schedule reconnect, unless we're done
|
||||||
|
|
||||||
@inlineCallbacks
|
@inlineCallbacks
|
||||||
def close(self, res=None, mood=u"happy"):
|
def close(self, f=None, mood=None):
|
||||||
if not isinstance(mood, (type(None), type(u""))):
|
"""Do d.addBoth(w.close) at the end of your chain."""
|
||||||
raise TypeError(type(mood))
|
|
||||||
if self._closed:
|
if self._closed:
|
||||||
returnValue(None)
|
returnValue(None)
|
||||||
self._closed = True
|
self._closed = True
|
||||||
if not self._ws:
|
if not self._ws:
|
||||||
returnValue(None)
|
returnValue(None)
|
||||||
|
|
||||||
|
if mood is None:
|
||||||
|
mood = u"happy"
|
||||||
|
if f:
|
||||||
|
if f.check(Timeout):
|
||||||
|
mood = u"lonely"
|
||||||
|
elif f.check(WrongPasswordError):
|
||||||
|
mood = u"scary"
|
||||||
|
elif f.check(TypeError, UsageError):
|
||||||
|
# preconditions don't warrant reporting mood
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
mood = u"errory" # other errors do
|
||||||
|
if not isinstance(mood, (type(None), type(u""))):
|
||||||
|
raise TypeError(type(mood))
|
||||||
|
|
||||||
self._timing.finish_event(self._timing_started, mood=mood)
|
self._timing.finish_event(self._timing_started, mood=mood)
|
||||||
yield self._deallocate(mood)
|
yield self._deallocate(mood)
|
||||||
# TODO: mark WebSocket as don't-reconnect
|
# TODO: mark WebSocket as don't-reconnect
|
||||||
self._ws.transport.loseConnection() # probably flushes
|
self._ws.transport.loseConnection() # probably flushes
|
||||||
del self._ws
|
del self._ws
|
||||||
|
returnValue(f)
|
||||||
|
|
||||||
@inlineCallbacks
|
@inlineCallbacks
|
||||||
def _deallocate(self, mood=None):
|
def _deallocate(self, mood):
|
||||||
_sent = self._timing.add_event("close")
|
_sent = self._timing.add_event("close")
|
||||||
yield self._ws_send(u"deallocate", mood=mood)
|
yield self._ws_send(u"deallocate", mood=mood)
|
||||||
while self._deallocated_status is None:
|
while self._deallocated_status is None:
|
||||||
yield self._sleep()
|
yield self._sleep(wake_on_error=False)
|
||||||
self._timing.finish_event(_sent)
|
self._timing.finish_event(_sent)
|
||||||
# TODO: set a timeout, don't wait forever for an ack
|
# TODO: set a timeout, don't wait forever for an ack
|
||||||
# TODO: if the connection is lost, let it go
|
# TODO: if the connection is lost, let it go
|
||||||
|
|
Loading…
Reference in New Issue
Block a user