rewrite waiting-for-sender pacifier messages
re-enable the test, and add an extra one The comments in cmd_send/cmd_receive now enumerate the four cases where we might notice that things are taking too long, the three cases where we say something about it, and the two cases where it might be appropriate to give up automatically (although we don't do that anywhere yet).
This commit is contained in:
parent
83e55f1f3e
commit
1a7b3baaf2
|
@ -13,7 +13,9 @@ from ..util import (dict_to_bytes, bytes_to_dict, bytes_to_hexstr,
|
|||
from .welcome import CLIWelcomeHandler
|
||||
|
||||
APPID = u"lothar.com/wormhole/text-or-file-xfer"
|
||||
VERIFY_TIMER = 1
|
||||
|
||||
KEY_TIMER = 1.0
|
||||
VERIFY_TIMER = 1.0
|
||||
|
||||
class RespondError(Exception):
|
||||
def __init__(self, response):
|
||||
|
@ -106,17 +108,45 @@ class TwistedReceiver:
|
|||
@inlineCallbacks
|
||||
def _go(self, w):
|
||||
yield self._handle_code(w)
|
||||
verifier = yield w.when_verified()
|
||||
def on_slow_connection():
|
||||
print(u"Key established, waiting for confirmation...",
|
||||
file=self.args.stderr)
|
||||
notify = self._reactor.callLater(VERIFY_TIMER, on_slow_connection)
|
||||
|
||||
def on_slow_key():
|
||||
print(u"Waiting for sender...", file=self.args.stderr)
|
||||
notify = self._reactor.callLater(KEY_TIMER, on_slow_key)
|
||||
try:
|
||||
yield w.when_version()
|
||||
# We wait here until we connect to the server and see the senders
|
||||
# PAKE message. If we used set_code() in the "human-selected
|
||||
# offline codes" mode, then the sender might not have even
|
||||
# started yet, so we might be sitting here for a while. Because
|
||||
# of that possibility, it's probably not appropriate to give up
|
||||
# automatically after some timeout. The user can express their
|
||||
# impatience by quitting the program with control-C.
|
||||
yield w.when_key()
|
||||
finally:
|
||||
if not notify.called:
|
||||
notify.cancel()
|
||||
self._show_verifier(verifier)
|
||||
|
||||
def on_slow_verification():
|
||||
print(u"Key established, waiting for confirmation...",
|
||||
file=self.args.stderr)
|
||||
notify = self._reactor.callLater(VERIFY_TIMER, on_slow_verification)
|
||||
try:
|
||||
# We wait here until we've seen their VERSION message (which they
|
||||
# send after seeing our PAKE message, and has the side-effect of
|
||||
# verifying that we both share the same key). There is a
|
||||
# round-trip between these two events, and we could experience a
|
||||
# significant delay here if:
|
||||
# * the relay server is being restarted
|
||||
# * the network is very slow
|
||||
# * the sender is very slow
|
||||
# * the sender has quit (in which case we may wait forever)
|
||||
|
||||
# It would be reasonable to give up after waiting here for too
|
||||
# long.
|
||||
verifier_bytes = yield w.when_verified()
|
||||
finally:
|
||||
if not notify.called:
|
||||
notify.cancel()
|
||||
self._show_verifier(verifier_bytes)
|
||||
|
||||
want_offer = True
|
||||
done = False
|
||||
|
@ -177,8 +207,8 @@ class TwistedReceiver:
|
|||
file=self.args.stderr)
|
||||
yield w.when_code()
|
||||
|
||||
def _show_verifier(self, verifier):
|
||||
verifier_hex = bytes_to_hexstr(verifier)
|
||||
def _show_verifier(self, verifier_bytes):
|
||||
verifier_hex = bytes_to_hexstr(verifier_bytes)
|
||||
if self.args.verify:
|
||||
self._msg(u"Verifier %s." % verifier_hex)
|
||||
|
||||
|
|
|
@ -118,30 +118,34 @@ class Sender:
|
|||
args.stderr.flush()
|
||||
print(u"", file=args.stderr)
|
||||
|
||||
# We don't print a "waiting" message for when_key() here, even though
|
||||
# we do that in cmd_receive.py, because it's not at all surprising to
|
||||
# we waiting here for a long time. We'll sit in when_key() until the
|
||||
# receiver has typed in the code and their PAKE message makes it to
|
||||
# us.
|
||||
yield w.when_key()
|
||||
|
||||
# TODO: don't stall on w.verify() unless they want it
|
||||
def on_slow_connection():
|
||||
print(u"Key established, waiting for confirmation...",
|
||||
file=args.stderr)
|
||||
#notify = self._reactor.callLater(VERIFY_TIMER, on_slow_connection)
|
||||
|
||||
# TODO: don't stall on w.verify() unless they want it
|
||||
#try:
|
||||
# verifier_bytes = yield w.when_verified() # might WrongPasswordError
|
||||
#finally:
|
||||
# if not notify.called:
|
||||
# notify.cancel()
|
||||
verifier_bytes = yield w.when_verified()
|
||||
notify = self._reactor.callLater(VERIFY_TIMER, on_slow_connection)
|
||||
try:
|
||||
# The usual sender-chooses-code sequence means the receiver's
|
||||
# PAKE should be followed immediately by their VERSION, so
|
||||
# w.when_verified() should fire right away. However if we're
|
||||
# using the offline-codes sequence, and the receiver typed in
|
||||
# their code first, and then they went offline, we might be
|
||||
# sitting here for a while, so printing the "waiting" message
|
||||
# seems like a good idea. It might even be appropriate to give up
|
||||
# after a while.
|
||||
verifier_bytes = yield w.when_verified() # might WrongPasswordError
|
||||
finally:
|
||||
if not notify.called:
|
||||
notify.cancel()
|
||||
|
||||
if args.verify:
|
||||
verifier = bytes_to_hexstr(verifier_bytes)
|
||||
while True:
|
||||
ok = six.moves.input("Verifier %s. ok? (yes/no): " % verifier)
|
||||
if ok.lower() == "yes":
|
||||
break
|
||||
if ok.lower() == "no":
|
||||
err = "sender rejected verification check, abandoned transfer"
|
||||
reject_data = dict_to_bytes({"error": err})
|
||||
w.send(reject_data)
|
||||
raise TransferError(err)
|
||||
self._check_verifier(w, verifier_bytes) # blocks, can TransferError
|
||||
|
||||
if self._fd_to_send:
|
||||
ts = TransitSender(args.transit_helper,
|
||||
|
@ -197,6 +201,18 @@ class Sender:
|
|||
if not recognized:
|
||||
log.msg("unrecognized message %r" % (them_d,))
|
||||
|
||||
def _check_verifier(self, w, verifier_bytes):
|
||||
verifier = bytes_to_hexstr(verifier_bytes)
|
||||
while True:
|
||||
ok = six.moves.input("Verifier %s. ok? (yes/no): " % verifier)
|
||||
if ok.lower() == "yes":
|
||||
break
|
||||
if ok.lower() == "no":
|
||||
err = "sender rejected verification check, abandoned transfer"
|
||||
reject_data = dict_to_bytes({"error": err})
|
||||
w.send(reject_data)
|
||||
raise TransferError(err)
|
||||
|
||||
def _handle_transit(self, receiver_transit):
|
||||
ts = self._transit_sender
|
||||
ts.add_connection_hints(receiver_transit.get("hints-v1", []))
|
||||
|
|
|
@ -281,7 +281,8 @@ class PregeneratedCode(ServerBase, ScriptsBase, unittest.TestCase):
|
|||
def _do_test(self, as_subprocess=False,
|
||||
mode="text", addslash=False, override_filename=False,
|
||||
fake_tor=False, overwrite=False, mock_accept=False):
|
||||
assert mode in ("text", "file", "empty-file", "directory", "slow-text")
|
||||
assert mode in ("text", "file", "empty-file", "directory",
|
||||
"slow-text", "slow-sender-text")
|
||||
if fake_tor:
|
||||
assert not as_subprocess
|
||||
send_cfg = config("send")
|
||||
|
@ -302,7 +303,7 @@ class PregeneratedCode(ServerBase, ScriptsBase, unittest.TestCase):
|
|||
receive_dir = self.mktemp()
|
||||
os.mkdir(receive_dir)
|
||||
|
||||
if mode in ("text", "slow-text"):
|
||||
if mode in ("text", "slow-text", "slow-sender-text"):
|
||||
send_cfg.text = message
|
||||
|
||||
elif mode in ("file", "empty-file"):
|
||||
|
@ -428,20 +429,22 @@ class PregeneratedCode(ServerBase, ScriptsBase, unittest.TestCase):
|
|||
) as mrx_tm:
|
||||
receive_d = cmd_receive.receive(recv_cfg)
|
||||
else:
|
||||
send_d = cmd_send.send(send_cfg)
|
||||
receive_d = cmd_receive.receive(recv_cfg)
|
||||
KEY_TIMER = 0 if mode == "slow-sender-text" else 1.0
|
||||
with mock.patch.object(cmd_receive, "KEY_TIMER", KEY_TIMER):
|
||||
send_d = cmd_send.send(send_cfg)
|
||||
receive_d = cmd_receive.receive(recv_cfg)
|
||||
|
||||
# The sender might fail, leaving the receiver hanging, or vice
|
||||
# versa. Make sure we don't wait on one side exclusively
|
||||
if mode == "slow-text":
|
||||
with mock.patch.object(cmd_send, "VERIFY_TIMER", 0), \
|
||||
mock.patch.object(cmd_receive, "VERIFY_TIMER", 0):
|
||||
yield gatherResults([send_d, receive_d], True)
|
||||
elif mock_accept:
|
||||
with mock.patch.object(cmd_receive.six.moves, 'input', return_value='y'):
|
||||
yield gatherResults([send_d, receive_d], True)
|
||||
else:
|
||||
yield gatherResults([send_d, receive_d], True)
|
||||
VERIFY_TIMER = 0 if mode == "slow-text" else 1.0
|
||||
with mock.patch.object(cmd_receive, "VERIFY_TIMER", VERIFY_TIMER):
|
||||
with mock.patch.object(cmd_send, "VERIFY_TIMER", VERIFY_TIMER):
|
||||
if mock_accept:
|
||||
with mock.patch.object(cmd_receive.six.moves,
|
||||
'input', return_value='y'):
|
||||
yield gatherResults([send_d, receive_d], True)
|
||||
else:
|
||||
yield gatherResults([send_d, receive_d], True)
|
||||
|
||||
if fake_tor:
|
||||
expected_endpoints = [("127.0.0.1", self.relayport)]
|
||||
|
@ -512,9 +515,14 @@ class PregeneratedCode(ServerBase, ScriptsBase, unittest.TestCase):
|
|||
.format(NL=NL), send_stderr)
|
||||
|
||||
# check receiver
|
||||
if mode == "text" or mode == "slow-text":
|
||||
if mode in ("text", "slow-text", "slow-sender-text"):
|
||||
self.assertEqual(receive_stdout, message+NL)
|
||||
self.assertEqual(receive_stderr, key_established)
|
||||
if mode == "text":
|
||||
self.assertEqual(receive_stderr, "")
|
||||
elif mode == "slow-text":
|
||||
self.assertEqual(receive_stderr, key_established)
|
||||
elif mode == "slow-sender-text":
|
||||
self.assertEqual(receive_stderr, "Waiting for sender...\n")
|
||||
elif mode == "file":
|
||||
self.failUnlessEqual(receive_stdout, "")
|
||||
self.failUnlessIn("Receiving file ({size:s}) into: {name}"
|
||||
|
@ -578,7 +586,8 @@ class PregeneratedCode(ServerBase, ScriptsBase, unittest.TestCase):
|
|||
|
||||
def test_slow_text(self):
|
||||
return self._do_test(mode="slow-text")
|
||||
test_slow_text.skip = "pending rethink"
|
||||
def test_slow_sender_text(self):
|
||||
return self._do_test(mode="slow-sender-text")
|
||||
|
||||
@inlineCallbacks
|
||||
def _do_test_fail(self, mode, failmode):
|
||||
|
|
Loading…
Reference in New Issue
Block a user