switch to new API

This renames all the existing API methods, to use a consistent
"d=get_XYZ()" (for Deferred mode) or "dg.wormhole_got_XYZ()" (for Delegated
mode). It updates cmd_send/cmd_receive/cmd_ssh to use the new API.

Since we now have get_welcome(), apps handle the Welcome message with a
Deferred callback instead of registering a "welcome handler". This lets us
make sure we've finished printing any server message-of-the-day or "you
should update your client" message to stdout before using stdio to ask for
the wormhole code. (Previously, the code-input prompt was overwritten by the
server message, and it was ugly). refs #145. This approach adds an extra
roundtrip to the receiver, but we can fix that (see #145 for details).

Because of that change, the server-is-being-slow message is printed at a
slightly different time, so those tests needed some extra work to exercise it
properly.
This commit is contained in:
Brian Warner 2017-05-12 13:12:36 -07:00
parent 2312f2ccce
commit 7955a36bfd
9 changed files with 264 additions and 269 deletions

View File

@ -33,7 +33,6 @@ class Boss(object):
_url = attrib(validator=instance_of(type(u""))) _url = attrib(validator=instance_of(type(u"")))
_appid = attrib(validator=instance_of(type(u""))) _appid = attrib(validator=instance_of(type(u"")))
_versions = attrib(validator=instance_of(dict)) _versions = attrib(validator=instance_of(dict))
_welcome_handler = attrib() # TODO: validator: callable
_reactor = attrib() _reactor = attrib()
_journal = attrib(validator=provides(_interfaces.IJournal)) _journal = attrib(validator=provides(_interfaces.IJournal))
_tor_manager = attrib() # TODO: ITorManager or None _tor_manager = attrib() # TODO: ITorManager or None
@ -185,11 +184,11 @@ class Boss(object):
raise WelcomeError(welcome["error"]) raise WelcomeError(welcome["error"])
# TODO: it'd be nice to not call the handler when we're in # TODO: it'd be nice to not call the handler when we're in
# S3_closing or S4_closed states. I tried to implement this with # S3_closing or S4_closed states. I tried to implement this with
# rx_Welcome as an @input, but in the error case I'd be # rx_welcome as an @input, but in the error case I'd be
# delivering a new input (rx_error or something) while in the # delivering a new input (rx_error or something) while in the
# middle of processing the rx_welcome input, and I wasn't sure # middle of processing the rx_welcome input, and I wasn't sure
# Automat would handle that correctly. # Automat would handle that correctly.
self._welcome_handler(welcome) # can raise WelcomeError too self._W.got_welcome(welcome) # TODO: let this raise WelcomeError?
except WelcomeError as welcome_error: except WelcomeError as welcome_error:
self.rx_unwelcome(welcome_error) self.rx_unwelcome(welcome_error)
@m.input() @m.input()
@ -244,7 +243,7 @@ class Boss(object):
self._their_versions = bytes_to_dict(plaintext) self._their_versions = bytes_to_dict(plaintext)
# but this part is app-to-app # but this part is app-to-app
app_versions = self._their_versions.get("app_versions", {}) app_versions = self._their_versions.get("app_versions", {})
self._W.got_version(app_versions) self._W.got_versions(app_versions)
@m.output() @m.output()
def S_send(self, plaintext): def S_send(self, plaintext):

View File

@ -10,7 +10,7 @@ from ..transit import TransitReceiver
from ..errors import TransferError, WormholeClosedError, NoTorError from ..errors import TransferError, WormholeClosedError, NoTorError
from ..util import (dict_to_bytes, bytes_to_dict, bytes_to_hexstr, from ..util import (dict_to_bytes, bytes_to_dict, bytes_to_hexstr,
estimate_free_space) estimate_free_space)
from .welcome import CLIWelcomeHandler from .welcome import handle_welcome
APPID = u"lothar.com/wormhole/text-or-file-xfer" APPID = u"lothar.com/wormhole/text-or-file-xfer"
@ -68,13 +68,10 @@ class Receiver:
# with the user handing off the wormhole code # with the user handing off the wormhole code
yield self._tor_manager.start() yield self._tor_manager.start()
wh = CLIWelcomeHandler(self.args.relay_url, __version__,
self.args.stderr)
w = create(self.args.appid or APPID, self.args.relay_url, w = create(self.args.appid or APPID, self.args.relay_url,
self._reactor, self._reactor,
tor_manager=self._tor_manager, tor_manager=self._tor_manager,
timing=self.args.timing, timing=self.args.timing)
welcome_handler=wh.handle_welcome)
self._w = w # so tests can wait on events too self._w = w # so tests can wait on events too
# I wanted to do this instead: # I wanted to do this instead:
@ -112,6 +109,10 @@ class Receiver:
@inlineCallbacks @inlineCallbacks
def _go(self, w): def _go(self, w):
welcome = yield w.get_welcome()
handle_welcome(welcome, self.args.relay_url, __version__,
self.args.stderr)
yield self._handle_code(w) yield self._handle_code(w)
def on_slow_key(): def on_slow_key():
@ -125,7 +126,7 @@ class Receiver:
# of that possibility, it's probably not appropriate to give up # of that possibility, it's probably not appropriate to give up
# automatically after some timeout. The user can express their # automatically after some timeout. The user can express their
# impatience by quitting the program with control-C. # impatience by quitting the program with control-C.
yield w.when_key() yield w.get_unverified_key()
finally: finally:
if not notify.called: if not notify.called:
notify.cancel() notify.cancel()
@ -147,7 +148,7 @@ class Receiver:
# It would be reasonable to give up after waiting here for too # It would be reasonable to give up after waiting here for too
# long. # long.
verifier_bytes = yield w.when_verified() verifier_bytes = yield w.get_verifier()
finally: finally:
if not notify.called: if not notify.called:
notify.cancel() notify.cancel()
@ -183,12 +184,12 @@ class Receiver:
def _send_data(self, data, w): def _send_data(self, data, w):
data_bytes = dict_to_bytes(data) data_bytes = dict_to_bytes(data)
w.send(data_bytes) w.send_message(data_bytes)
@inlineCallbacks @inlineCallbacks
def _get_data(self, w): def _get_data(self, w):
# this may raise WrongPasswordError # this may raise WrongPasswordError
them_bytes = yield w.when_received() them_bytes = yield w.get_message()
them_d = bytes_to_dict(them_bytes) them_d = bytes_to_dict(them_bytes)
if "error" in them_d: if "error" in them_d:
raise TransferError(them_d["error"]) raise TransferError(them_d["error"])
@ -210,7 +211,7 @@ class Receiver:
if not used_completion: if not used_completion:
print(" (note: you can use <Tab> to complete words)", print(" (note: you can use <Tab> to complete words)",
file=self.args.stderr) file=self.args.stderr)
yield w.when_code() yield w.get_code()
def _show_verifier(self, verifier_bytes): def _show_verifier(self, verifier_bytes):
verifier_hex = bytes_to_hexstr(verifier_bytes) verifier_hex = bytes_to_hexstr(verifier_bytes)

View File

@ -10,7 +10,7 @@ from ..errors import TransferError, WormholeClosedError, NoTorError
from wormhole import create, __version__ from wormhole import create, __version__
from ..transit import TransitSender from ..transit import TransitSender
from ..util import dict_to_bytes, bytes_to_dict, bytes_to_hexstr from ..util import dict_to_bytes, bytes_to_dict, bytes_to_hexstr
from .welcome import CLIWelcomeHandler from .welcome import handle_welcome
APPID = u"lothar.com/wormhole/text-or-file-xfer" APPID = u"lothar.com/wormhole/text-or-file-xfer"
VERIFY_TIMER = 1 VERIFY_TIMER = 1
@ -53,13 +53,10 @@ class Sender:
# with the user handing off the wormhole code # with the user handing off the wormhole code
yield self._tor_manager.start() yield self._tor_manager.start()
wh = CLIWelcomeHandler(self._args.relay_url, __version__,
self._args.stderr)
w = create(self._args.appid or APPID, self._args.relay_url, w = create(self._args.appid or APPID, self._args.relay_url,
self._reactor, self._reactor,
tor_manager=self._tor_manager, tor_manager=self._tor_manager,
timing=self._timing, timing=self._timing)
welcome_handler=wh.handle_welcome)
d = self._go(w) d = self._go(w)
# if we succeed, we should close and return the w.close results # if we succeed, we should close and return the w.close results
@ -85,10 +82,14 @@ class Sender:
def _send_data(self, data, w): def _send_data(self, data, w):
data_bytes = dict_to_bytes(data) data_bytes = dict_to_bytes(data)
w.send(data_bytes) w.send_message(data_bytes)
@inlineCallbacks @inlineCallbacks
def _go(self, w): def _go(self, w):
welcome = yield w.get_welcome()
handle_welcome(welcome, self._args.relay_url, __version__,
self._args.stderr)
# TODO: run the blocking zip-the-directory IO in a thread, let the # TODO: run the blocking zip-the-directory IO in a thread, let the
# wormhole exchange happen in parallel # wormhole exchange happen in parallel
offer, self._fd_to_send = self._build_offer() offer, self._fd_to_send = self._build_offer()
@ -110,21 +111,21 @@ class Sender:
else: else:
w.allocate_code(args.code_length) w.allocate_code(args.code_length)
code = yield w.when_code() code = yield w.get_code()
if not args.zeromode: if not args.zeromode:
print(u"Wormhole code is: %s" % code, file=args.stderr) print(u"Wormhole code is: %s" % code, file=args.stderr)
# flush stderr so the code is displayed immediately # flush stderr so the code is displayed immediately
args.stderr.flush() args.stderr.flush()
print(u"", file=args.stderr) print(u"", file=args.stderr)
# We don't print a "waiting" message for when_key() here, even though # We don't print a "waiting" message for get_unverified_key() here,
# we do that in cmd_receive.py, because it's not at all surprising to # even though we do that in cmd_receive.py, because it's not at all
# we waiting here for a long time. We'll sit in when_key() until the # surprising to we waiting here for a long time. We'll sit in
# receiver has typed in the code and their PAKE message makes it to # get_unverified_key() until the receiver has typed in the code and
# us. # their PAKE message makes it to us.
yield w.when_key() yield w.get_unverified_key()
# TODO: don't stall on w.verify() unless they want it # TODO: don't stall on w.get_verifier() unless they want it
def on_slow_connection(): def on_slow_connection():
print(u"Key established, waiting for confirmation...", print(u"Key established, waiting for confirmation...",
file=args.stderr) file=args.stderr)
@ -132,13 +133,13 @@ class Sender:
try: try:
# The usual sender-chooses-code sequence means the receiver's # The usual sender-chooses-code sequence means the receiver's
# PAKE should be followed immediately by their VERSION, so # PAKE should be followed immediately by their VERSION, so
# w.when_verified() should fire right away. However if we're # w.get_verifier() should fire right away. However if we're
# using the offline-codes sequence, and the receiver typed in # using the offline-codes sequence, and the receiver typed in
# their code first, and then they went offline, we might be # their code first, and then they went offline, we might be
# sitting here for a while, so printing the "waiting" message # sitting here for a while, so printing the "waiting" message
# seems like a good idea. It might even be appropriate to give up # seems like a good idea. It might even be appropriate to give up
# after a while. # after a while.
verifier_bytes = yield w.when_verified() # might WrongPasswordError verifier_bytes = yield w.get_verifier() # might WrongPasswordError
finally: finally:
if not notify.called: if not notify.called:
notify.cancel() notify.cancel()
@ -162,7 +163,7 @@ class Sender:
} }
self._send_data({u"transit": sender_transit}, w) self._send_data({u"transit": sender_transit}, w)
# TODO: move this down below w.get() # TODO: move this down below w.get_message()
transit_key = w.derive_key(APPID+"/transit-key", transit_key = w.derive_key(APPID+"/transit-key",
ts.TRANSIT_KEY_LENGTH) ts.TRANSIT_KEY_LENGTH)
ts.set_transit_key(transit_key) ts.set_transit_key(transit_key)
@ -174,13 +175,13 @@ class Sender:
while True: while True:
try: try:
them_d_bytes = yield w.when_received() them_d_bytes = yield w.get_message()
except WormholeClosedError: except WormholeClosedError:
if done: if done:
returnValue(None) returnValue(None)
raise TransferError("unexpected close") raise TransferError("unexpected close")
# TODO: when_received() fired, so now it's safe to use # TODO: get_message() fired, so get_verifier must have fired, so
# w.derive_key() # now it's safe to use w.derive_key()
them_d = bytes_to_dict(them_d_bytes) them_d = bytes_to_dict(them_d_bytes)
#print("GOT", them_d) #print("GOT", them_d)
recognized = False recognized = False
@ -209,7 +210,7 @@ class Sender:
if ok.lower() == "no": if ok.lower() == "no":
err = "sender rejected verification check, abandoned transfer" err = "sender rejected verification check, abandoned transfer"
reject_data = dict_to_bytes({"error": err}) reject_data = dict_to_bytes({"error": err})
w.send(reject_data) w.send_message(reject_data)
raise TransferError(err) raise TransferError(err)
def _handle_transit(self, receiver_transit): def _handle_transit(self, receiver_transit):

View File

@ -1,24 +1,18 @@
from __future__ import print_function, absolute_import, unicode_literals from __future__ import print_function, absolute_import, unicode_literals
import sys
from ..wormhole import _WelcomeHandler
class CLIWelcomeHandler(_WelcomeHandler): def handle_welcome(welcome, relay_url, my_version, stderr):
def __init__(self, url, cli_version, stderr=sys.stderr): if "motd" in welcome:
_WelcomeHandler.__init__(self, url, stderr) motd_lines = welcome["motd"].splitlines()
self._current_version = cli_version motd_formatted = "\n ".join(motd_lines)
self._version_warning_displayed = False print("Server (at %s) says:\n %s" % (relay_url, motd_formatted),
file=stderr)
def handle_welcome(self, welcome):
# Only warn if we're running a release version (e.g. 0.0.6, not
# 0.0.6+DISTANCE.gHASH). Only warn once.
if ("current_cli_version" in welcome
and "+" not in self._current_version
and not self._version_warning_displayed
and welcome["current_cli_version"] != self._current_version):
print("Warning: errors may occur unless both sides are running the same version", file=self.stderr)
print("Server claims %s is current, but ours is %s"
% (welcome["current_cli_version"], self._current_version),
file=self.stderr)
self._version_warning_displayed = True
_WelcomeHandler.handle_welcome(self, welcome)
# Only warn if we're running a release version (e.g. 0.0.6, not
# 0.0.6+DISTANCE.gHASH). Only warn once.
if ("current_cli_version" in welcome
and "+" not in my_version
and welcome["current_cli_version"] != my_version):
print("Warning: errors may occur unless both sides are running the same version", file=stderr)
print("Server claims %s is current, but ours is %s"
% (welcome["current_cli_version"], my_version),
file=stderr)

View File

@ -433,9 +433,16 @@ class PregeneratedCode(ServerBase, ScriptsBase, unittest.TestCase):
receive_d = cmd_receive.receive(recv_cfg) receive_d = cmd_receive.receive(recv_cfg)
else: else:
KEY_TIMER = 0 if mode == "slow-sender-text" else 1.0 KEY_TIMER = 0 if mode == "slow-sender-text" else 1.0
rxw = []
with mock.patch.object(cmd_receive, "KEY_TIMER", KEY_TIMER): with mock.patch.object(cmd_receive, "KEY_TIMER", KEY_TIMER):
send_d = cmd_send.send(send_cfg) send_d = cmd_send.send(send_cfg)
receive_d = cmd_receive.receive(recv_cfg) receive_d = cmd_receive.receive(recv_cfg,
_debug_stash_wormhole=rxw)
# we need to keep KEY_TIMER patched until the receiver
# gets far enough to start the timer, which happens after
# the code is set
if mode == "slow-sender-text":
yield rxw[0].get_unverified_key()
# The sender might fail, leaving the receiver hanging, or vice # The sender might fail, leaving the receiver hanging, or vice
# versa. Make sure we don't wait on one side exclusively # versa. Make sure we don't wait on one side exclusively
@ -832,26 +839,26 @@ class NotWelcome(ServerBase, unittest.TestCase):
class Cleanup(ServerBase, unittest.TestCase): class Cleanup(ServerBase, unittest.TestCase):
def setUp(self): def make_config(self):
d = super(Cleanup, self).setUp() cfg = config("send")
self.cfg = cfg = config("send")
# common options for all tests in this suite # common options for all tests in this suite
cfg.hide_progress = True cfg.hide_progress = True
cfg.relay_url = self.relayurl cfg.relay_url = self.relayurl
cfg.transit_helper = "" cfg.transit_helper = ""
cfg.stdout = io.StringIO() cfg.stdout = io.StringIO()
cfg.stderr = io.StringIO() cfg.stderr = io.StringIO()
return d return cfg
@inlineCallbacks @inlineCallbacks
@mock.patch('sys.stdout') @mock.patch('sys.stdout')
def test_text(self, stdout): def test_text(self, stdout):
# the rendezvous channel should be deleted after success # the rendezvous channel should be deleted after success
self.cfg.text = "hello" cfg = self.make_config()
self.cfg.code = "1-abc" cfg.text = "hello"
cfg.code = "1-abc"
send_d = cmd_send.send(self.cfg) send_d = cmd_send.send(cfg)
receive_d = cmd_receive.receive(self.cfg) receive_d = cmd_receive.receive(cfg)
yield send_d yield send_d
yield receive_d yield receive_d
@ -863,12 +870,14 @@ class Cleanup(ServerBase, unittest.TestCase):
def test_text_wrong_password(self): def test_text_wrong_password(self):
# if the password was wrong, the rendezvous channel should still be # if the password was wrong, the rendezvous channel should still be
# deleted # deleted
self.cfg.text = "secret message" send_cfg = self.make_config()
self.cfg.code = "1-abc" send_cfg.text = "secret message"
send_d = cmd_send.send(self.cfg) send_cfg.code = "1-abc"
send_d = cmd_send.send(send_cfg)
self.cfg.code = "1-WRONG" rx_cfg = self.make_config()
receive_d = cmd_receive.receive(self.cfg) rx_cfg.code = "1-WRONG"
receive_d = cmd_receive.receive(rx_cfg)
# both sides should be capable of detecting the mismatch # both sides should be capable of detecting the mismatch
yield self.assertFailure(send_d, WrongPasswordError) yield self.assertFailure(send_d, WrongPasswordError)
@ -951,12 +960,9 @@ class AppID(ServerBase, unittest.TestCase):
self.assertEqual(used[0]["app_id"], u"appid2") self.assertEqual(used[0]["app_id"], u"appid2")
class Welcome(unittest.TestCase): class Welcome(unittest.TestCase):
def do(self, welcome_message, my_version="2.0", twice=False): def do(self, welcome_message, my_version="2.0"):
stderr = io.StringIO() stderr = io.StringIO()
h = welcome.CLIWelcomeHandler("url", my_version, stderr) welcome.handle_welcome(welcome_message, "url", my_version, stderr)
h.handle_welcome(welcome_message)
if twice:
h.handle_welcome(welcome_message)
return stderr.getvalue() return stderr.getvalue()
def test_empty(self): def test_empty(self):
@ -973,15 +979,6 @@ class Welcome(unittest.TestCase):
"Server claims 3.0 is current, but ours is 2.0\n") "Server claims 3.0 is current, but ours is 2.0\n")
self.assertEqual(stderr, expected) self.assertEqual(stderr, expected)
def test_version_old_twice(self):
stderr = self.do({"current_cli_version": "3.0"}, twice=True)
# the handler should only emit the version warning once, even if we
# get multiple Welcome messages (which could happen if we lose the
# connection and then reconnect)
expected = ("Warning: errors may occur unless both sides are running the same version\n" +
"Server claims 3.0 is current, but ours is 2.0\n")
self.assertEqual(stderr, expected)
def test_version_unreleased(self): def test_version_unreleased(self):
stderr = self.do({"current_cli_version": "3.0"}, stderr = self.do({"current_cli_version": "3.0"},
my_version="2.5+middle.something") my_version="2.5+middle.something")

View File

@ -1152,15 +1152,15 @@ class Boss(unittest.TestCase):
def build(self): def build(self):
events = [] events = []
wormhole = Dummy("w", events, None, wormhole = Dummy("w", events, None,
"got_code", "got_key", "got_verifier", "got_version", "got_welcome",
"got_code", "got_key", "got_verifier", "got_versions",
"received", "closed") "received", "closed")
self._welcome_handler = mock.Mock()
versions = {"app": "version1"} versions = {"app": "version1"}
reactor = None reactor = None
journal = ImmediateJournal() journal = ImmediateJournal()
tor_manager = None tor_manager = None
b = MockBoss(wormhole, "side", "url", "appid", versions, b = MockBoss(wormhole, "side", "url", "appid", versions,
self._welcome_handler, reactor, journal, tor_manager, reactor, journal, tor_manager,
timing.DebugTiming()) timing.DebugTiming())
b._T = Dummy("t", events, ITerminator, "close") b._T = Dummy("t", events, ITerminator, "close")
b._S = Dummy("s", events, ISend, "send") b._S = Dummy("s", events, ISend, "send")
@ -1179,8 +1179,11 @@ class Boss(unittest.TestCase):
self.assertEqual(events, [("w.got_code", "1-code")]) self.assertEqual(events, [("w.got_code", "1-code")])
events[:] = [] events[:] = []
b.rx_welcome("welcome") welcome = {"howdy": "how are ya"}
self.assertEqual(self._welcome_handler.mock_calls, [mock.call("welcome")]) b.rx_welcome(welcome)
self.assertEqual(events, [("w.got_welcome", welcome),
])
events[:] = []
# pretend a peer message was correctly decrypted # pretend a peer message was correctly decrypted
b.got_key(b"key") b.got_key(b"key")
@ -1190,7 +1193,7 @@ class Boss(unittest.TestCase):
b.got_message("0", b"msg1") b.got_message("0", b"msg1")
self.assertEqual(events, [("w.got_key", b"key"), self.assertEqual(events, [("w.got_key", b"key"),
("w.got_verifier", b"verifier"), ("w.got_verifier", b"verifier"),
("w.got_version", {}), ("w.got_versions", {}),
("w.received", b"msg1"), ("w.received", b"msg1"),
]) ])
events[:] = [] events[:] = []
@ -1206,6 +1209,12 @@ class Boss(unittest.TestCase):
b.closed() b.closed()
self.assertEqual(events, [("w.closed", "happy")]) self.assertEqual(events, [("w.closed", "happy")])
def test_unwelcome(self):
b, events = self.build()
unwelcome = {"error": "go away"}
b.rx_welcome(unwelcome)
self.assertEqual(events, [("t.close", "unwelcome")])
def test_lonely(self): def test_lonely(self):
b, events = self.build() b, events = self.build()
b.set_code("1-code") b.set_code("1-code")

View File

@ -12,23 +12,6 @@ from ..errors import (WrongPasswordError,
APPID = "appid" APPID = "appid"
class Welcome(unittest.TestCase):
def test_tolerate_no_current_version(self):
w = wormhole._WelcomeHandler("relay_url")
w.handle_welcome({})
def test_print_motd(self):
stderr = io.StringIO()
w = wormhole._WelcomeHandler("relay_url", stderr=stderr)
w.handle_welcome({"motd": "message of\nthe day"})
self.assertEqual(stderr.getvalue(),
"Server (at relay_url) says:\n message of\n the day\n")
# motd can be displayed multiple times
w.handle_welcome({"motd": "second message"})
self.assertEqual(stderr.getvalue(),
("Server (at relay_url) says:\n message of\n the day\n"
"Server (at relay_url) says:\n second message\n"))
# event orderings to exercise: # event orderings to exercise:
# #
# * normal sender: set_code, send_phase1, connected, claimed, learn_msg2, # * normal sender: set_code, send_phase1, connected, claimed, learn_msg2,
@ -42,21 +25,24 @@ class Welcome(unittest.TestCase):
class Delegate: class Delegate:
def __init__(self): def __init__(self):
self.welcome = None
self.code = None self.code = None
self.key = None self.key = None
self.verifier = None self.verifier = None
self.version = None self.versions = None
self.messages = [] self.messages = []
self.closed = None self.closed = None
def wormhole_code(self, code): def wormhole_got_welcome(self, welcome):
self.welcome = welcome
def wormhole_got_code(self, code):
self.code = code self.code = code
def wormhole_key(self, key): def wormhole_got_unverified_key(self, key):
self.key = key self.key = key
def wormhole_verified(self, verifier): def wormhole_got_verifier(self, verifier):
self.verifier = verifier self.verifier = verifier
def wormhole_version(self, version): def wormhole_got_versions(self, versions):
self.version = version self.versions = versions
def wormhole_received(self, data): def wormhole_got_message(self, data):
self.messages.append(data) self.messages.append(data)
def wormhole_closed(self, result): def wormhole_closed(self, result):
self.closed = result self.closed = result
@ -76,12 +62,12 @@ class Delegated(ServerBase, unittest.TestCase):
w2.set_code(dg.code) w2.set_code(dg.code)
yield poll_until(lambda: dg.key is not None) yield poll_until(lambda: dg.key is not None)
yield poll_until(lambda: dg.verifier is not None) yield poll_until(lambda: dg.verifier is not None)
yield poll_until(lambda: dg.version is not None) yield poll_until(lambda: dg.versions is not None)
w1.send(b"ping") w1.send_message(b"ping")
got = yield w2.when_received() got = yield w2.get_message()
self.assertEqual(got, b"ping") self.assertEqual(got, b"ping")
w2.send(b"pong") w2.send_message(b"pong")
yield poll_until(lambda: dg.messages) yield poll_until(lambda: dg.messages)
self.assertEqual(dg.messages[0], b"pong") self.assertEqual(dg.messages[0], b"pong")
@ -124,7 +110,7 @@ class Wormholes(ServerBase, unittest.TestCase):
def test_allocate_default(self): def test_allocate_default(self):
w1 = wormhole.create(APPID, self.relayurl, reactor) w1 = wormhole.create(APPID, self.relayurl, reactor)
w1.allocate_code() w1.allocate_code()
code = yield w1.when_code() code = yield w1.get_code()
mo = re.search(r"^\d+-\w+-\w+$", code) mo = re.search(r"^\d+-\w+-\w+$", code)
self.assert_(mo, code) self.assert_(mo, code)
# w.close() fails because we closed before connecting # w.close() fails because we closed before connecting
@ -134,7 +120,7 @@ class Wormholes(ServerBase, unittest.TestCase):
def test_allocate_more_words(self): def test_allocate_more_words(self):
w1 = wormhole.create(APPID, self.relayurl, reactor) w1 = wormhole.create(APPID, self.relayurl, reactor)
w1.allocate_code(3) w1.allocate_code(3)
code = yield w1.when_code() code = yield w1.get_code()
mo = re.search(r"^\d+-\w+-\w+-\w+$", code) mo = re.search(r"^\d+-\w+-\w+-\w+$", code)
self.assert_(mo, code) self.assert_(mo, code)
yield self.assertFailure(w1.close(), LonelyError) yield self.assertFailure(w1.close(), LonelyError)
@ -149,11 +135,11 @@ class Wormholes(ServerBase, unittest.TestCase):
w2 = wormhole.create(APPID, self.relayurl, reactor) w2 = wormhole.create(APPID, self.relayurl, reactor)
#w2.debug_set_trace(" W2") #w2.debug_set_trace(" W2")
w1.allocate_code() w1.allocate_code()
code = yield w1.when_code() code = yield w1.get_code()
w2.set_code(code) w2.set_code(code)
yield w1.when_key() yield w1.get_unverified_key()
yield w2.when_key() yield w2.get_unverified_key()
key1 = w1.derive_key("purpose", 16) key1 = w1.derive_key("purpose", 16)
self.assertEqual(len(key1), 16) self.assertEqual(len(key1), 16)
@ -163,29 +149,29 @@ class Wormholes(ServerBase, unittest.TestCase):
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
w1.derive_key(12345, 16) w1.derive_key(12345, 16)
verifier1 = yield w1.when_verified() verifier1 = yield w1.get_verifier()
verifier2 = yield w2.when_verified() verifier2 = yield w2.get_verifier()
self.assertEqual(verifier1, verifier2) self.assertEqual(verifier1, verifier2)
self.successResultOf(w1.when_key()) self.successResultOf(w1.get_unverified_key())
self.successResultOf(w2.when_key()) self.successResultOf(w2.get_unverified_key())
version1 = yield w1.when_version() versions1 = yield w1.get_versions()
version2 = yield w2.when_version() versions2 = yield w2.get_versions()
# app-versions are exercised properly in test_versions, this just # app-versions are exercised properly in test_versions, this just
# tests the defaults # tests the defaults
self.assertEqual(version1, {}) self.assertEqual(versions1, {})
self.assertEqual(version2, {}) self.assertEqual(versions2, {})
w1.send(b"data1") w1.send_message(b"data1")
w2.send(b"data2") w2.send_message(b"data2")
dataX = yield w1.when_received() dataX = yield w1.get_message()
dataY = yield w2.when_received() dataY = yield w2.get_message()
self.assertEqual(dataX, b"data2") self.assertEqual(dataX, b"data2")
self.assertEqual(dataY, b"data1") self.assertEqual(dataY, b"data1")
version1_again = yield w1.when_version() versions1_again = yield w1.get_versions()
self.assertEqual(version1, version1_again) self.assertEqual(versions1, versions1_again)
c1 = yield w1.close() c1 = yield w1.close()
self.assertEqual(c1, "happy") self.assertEqual(c1, "happy")
@ -193,19 +179,19 @@ class Wormholes(ServerBase, unittest.TestCase):
self.assertEqual(c2, "happy") self.assertEqual(c2, "happy")
@inlineCallbacks @inlineCallbacks
def test_when_code_early(self): def test_get_code_early(self):
w1 = wormhole.create(APPID, self.relayurl, reactor) w1 = wormhole.create(APPID, self.relayurl, reactor)
d = w1.when_code() d = w1.get_code()
w1.set_code("1-abc") w1.set_code("1-abc")
code = self.successResultOf(d) code = self.successResultOf(d)
self.assertEqual(code, "1-abc") self.assertEqual(code, "1-abc")
yield self.assertFailure(w1.close(), LonelyError) yield self.assertFailure(w1.close(), LonelyError)
@inlineCallbacks @inlineCallbacks
def test_when_code_late(self): def test_get_code_late(self):
w1 = wormhole.create(APPID, self.relayurl, reactor) w1 = wormhole.create(APPID, self.relayurl, reactor)
w1.set_code("1-abc") w1.set_code("1-abc")
d = w1.when_code() d = w1.get_code()
code = self.successResultOf(d) code = self.successResultOf(d)
self.assertEqual(code, "1-abc") self.assertEqual(code, "1-abc")
yield self.assertFailure(w1.close(), LonelyError) yield self.assertFailure(w1.close(), LonelyError)
@ -218,12 +204,12 @@ class Wormholes(ServerBase, unittest.TestCase):
w1 = wormhole.create(APPID, self.relayurl, reactor) w1 = wormhole.create(APPID, self.relayurl, reactor)
w2 = wormhole.create(APPID, self.relayurl, reactor) w2 = wormhole.create(APPID, self.relayurl, reactor)
w1.allocate_code() w1.allocate_code()
code = yield w1.when_code() code = yield w1.get_code()
w2.set_code(code) w2.set_code(code)
w1.send(b"data") w1.send_message(b"data")
w2.send(b"data") w2.send_message(b"data")
dataX = yield w1.when_received() dataX = yield w1.get_message()
dataY = yield w2.when_received() dataY = yield w2.get_message()
self.assertEqual(dataX, b"data") self.assertEqual(dataX, b"data")
self.assertEqual(dataY, b"data") self.assertEqual(dataY, b"data")
yield w1.close() yield w1.close()
@ -234,13 +220,13 @@ class Wormholes(ServerBase, unittest.TestCase):
w1 = wormhole.create(APPID, self.relayurl, reactor) w1 = wormhole.create(APPID, self.relayurl, reactor)
w2 = wormhole.create(APPID, self.relayurl, reactor) w2 = wormhole.create(APPID, self.relayurl, reactor)
w1.allocate_code() w1.allocate_code()
code = yield w1.when_code() code = yield w1.get_code()
w2.set_code(code) w2.set_code(code)
w1.send(b"data1") w1.send_message(b"data1")
dataY = yield w2.when_received() dataY = yield w2.get_message()
self.assertEqual(dataY, b"data1") self.assertEqual(dataY, b"data1")
d = w1.when_received() d = w1.get_message()
w2.send(b"data2") w2.send_message(b"data2")
dataX = yield d dataX = yield d
self.assertEqual(dataX, b"data2") self.assertEqual(dataX, b"data2")
yield w1.close() yield w1.close()
@ -251,10 +237,10 @@ class Wormholes(ServerBase, unittest.TestCase):
w1 = wormhole.create(APPID, self.relayurl, reactor) w1 = wormhole.create(APPID, self.relayurl, reactor)
w2 = wormhole.create(APPID, self.relayurl, reactor) w2 = wormhole.create(APPID, self.relayurl, reactor)
w1.allocate_code() w1.allocate_code()
code = yield w1.when_code() code = yield w1.get_code()
w2.set_code(code) w2.set_code(code)
w1.send(b"data1") w1.send_message(b"data1")
dataY = yield w2.when_received() dataY = yield w2.get_message()
self.assertEqual(dataY, b"data1") self.assertEqual(dataY, b"data1")
yield w1.close() yield w1.close()
yield w2.close() yield w2.close()
@ -262,9 +248,9 @@ class Wormholes(ServerBase, unittest.TestCase):
@inlineCallbacks @inlineCallbacks
def test_early(self): def test_early(self):
w1 = wormhole.create(APPID, self.relayurl, reactor) w1 = wormhole.create(APPID, self.relayurl, reactor)
w1.send(b"data1") w1.send_message(b"data1")
w2 = wormhole.create(APPID, self.relayurl, reactor) w2 = wormhole.create(APPID, self.relayurl, reactor)
d = w2.when_received() d = w2.get_message()
w1.set_code("123-abc-def") w1.set_code("123-abc-def")
w2.set_code("123-abc-def") w2.set_code("123-abc-def")
dataY = yield d dataY = yield d
@ -278,8 +264,8 @@ class Wormholes(ServerBase, unittest.TestCase):
w2 = wormhole.create(APPID, self.relayurl, reactor) w2 = wormhole.create(APPID, self.relayurl, reactor)
w1.set_code("123-purple-elephant") w1.set_code("123-purple-elephant")
w2.set_code("123-purple-elephant") w2.set_code("123-purple-elephant")
w1.send(b"data1"), w2.send(b"data2") w1.send_message(b"data1"), w2.send_message(b"data2")
dl = yield self.doBoth(w1.when_received(), w2.when_received()) dl = yield self.doBoth(w1.get_message(), w2.get_message())
(dataX, dataY) = dl (dataX, dataY) = dl
self.assertEqual(dataX, b"data2") self.assertEqual(dataX, b"data2")
self.assertEqual(dataY, b"data1") self.assertEqual(dataY, b"data1")
@ -300,8 +286,8 @@ class Wormholes(ServerBase, unittest.TestCase):
yield poll_until(lambda: w2._boss._K._debug_pake_stashed) yield poll_until(lambda: w2._boss._K._debug_pake_stashed)
h.choose_words("purple-elephant") h.choose_words("purple-elephant")
w1.send(b"data1"), w2.send(b"data2") w1.send_message(b"data1"), w2.send_message(b"data2")
dl = yield self.doBoth(w1.when_received(), w2.when_received()) dl = yield self.doBoth(w1.get_message(), w2.get_message())
(dataX, dataY) = dl (dataX, dataY) = dl
self.assertEqual(dataX, b"data2") self.assertEqual(dataX, b"data2")
self.assertEqual(dataY, b"data1") self.assertEqual(dataY, b"data1")
@ -315,13 +301,13 @@ class Wormholes(ServerBase, unittest.TestCase):
w2 = wormhole.create(APPID, self.relayurl, reactor) w2 = wormhole.create(APPID, self.relayurl, reactor)
w1.set_code("123-purple-elephant") w1.set_code("123-purple-elephant")
w2.set_code("123-purple-elephant") w2.set_code("123-purple-elephant")
w1.send(b"data1"), w2.send(b"data2") w1.send_message(b"data1"), w2.send_message(b"data2")
w1.send(b"data3"), w2.send(b"data4") w1.send_message(b"data3"), w2.send_message(b"data4")
dl = yield self.doBoth(w1.when_received(), w2.when_received()) dl = yield self.doBoth(w1.get_message(), w2.get_message())
(dataX, dataY) = dl (dataX, dataY) = dl
self.assertEqual(dataX, b"data2") self.assertEqual(dataX, b"data2")
self.assertEqual(dataY, b"data1") self.assertEqual(dataY, b"data1")
dl = yield self.doBoth(w1.when_received(), w2.when_received()) dl = yield self.doBoth(w1.get_message(), w2.get_message())
(dataX, dataY) = dl (dataX, dataY) = dl
self.assertEqual(dataX, b"data4") self.assertEqual(dataX, b"data4")
self.assertEqual(dataY, b"data3") self.assertEqual(dataY, b"data3")
@ -337,19 +323,19 @@ class Wormholes(ServerBase, unittest.TestCase):
w2.set_code("123-foo") w2.set_code("123-foo")
# let it connect and become HAPPY # let it connect and become HAPPY
yield w1.when_version() yield w1.get_versions()
yield w2.when_version() yield w2.get_versions()
yield w1.close() yield w1.close()
yield w2.close() yield w2.close()
# once closed, all Deferred-yielding API calls get an immediate error # once closed, all Deferred-yielding API calls get an immediate error
f = self.failureResultOf(w1.when_code(), WormholeClosed) f = self.failureResultOf(w1.get_code(), WormholeClosed)
self.assertEqual(f.value.args[0], "happy") self.assertEqual(f.value.args[0], "happy")
self.failureResultOf(w1.when_key(), WormholeClosed) self.failureResultOf(w1.get_unverified_key(), WormholeClosed)
self.failureResultOf(w1.when_verified(), WormholeClosed) self.failureResultOf(w1.get_verifier(), WormholeClosed)
self.failureResultOf(w1.when_version(), WormholeClosed) self.failureResultOf(w1.get_versions(), WormholeClosed)
self.failureResultOf(w1.when_received(), WormholeClosed) self.failureResultOf(w1.get_message(), WormholeClosed)
@inlineCallbacks @inlineCallbacks
@ -357,20 +343,20 @@ class Wormholes(ServerBase, unittest.TestCase):
w1 = wormhole.create(APPID, self.relayurl, reactor) w1 = wormhole.create(APPID, self.relayurl, reactor)
w2 = wormhole.create(APPID, self.relayurl, reactor) w2 = wormhole.create(APPID, self.relayurl, reactor)
w1.allocate_code() w1.allocate_code()
code = yield w1.when_code() code = yield w1.get_code()
w2.set_code(code+"not") w2.set_code(code+"not")
code2 = yield w2.when_code() code2 = yield w2.get_code()
self.assertNotEqual(code, code2) self.assertNotEqual(code, code2)
# That's enough to allow both sides to discover the mismatch, but # That's enough to allow both sides to discover the mismatch, but
# only after the confirmation message gets through. API calls that # only after the confirmation message gets through. API calls that
# don't wait will appear to work until the mismatched confirmation # don't wait will appear to work until the mismatched confirmation
# message arrives. # message arrives.
w1.send(b"should still work") w1.send_message(b"should still work")
w2.send(b"should still work") w2.send_message(b"should still work")
key2 = yield w2.when_key() # should work key2 = yield w2.get_unverified_key() # should work
# w2 has just received w1.PAKE, and is about to send w2.VERSION # w2 has just received w1.PAKE, and is about to send w2.VERSION
key1 = yield w1.when_key() # should work key1 = yield w1.get_unverified_key() # should work
# w1 has just received w2.PAKE, and is about to send w1.VERSION, and # w1 has just received w2.PAKE, and is about to send w1.VERSION, and
# then will receive w2.VERSION. When it sees w2.VERSION, it will # then will receive w2.VERSION. When it sees w2.VERSION, it will
# learn about the WrongPasswordError. # learn about the WrongPasswordError.
@ -378,54 +364,54 @@ class Wormholes(ServerBase, unittest.TestCase):
# API calls that wait (i.e. get) will errback. We collect all these # API calls that wait (i.e. get) will errback. We collect all these
# Deferreds early to exercise the wait-then-fail path # Deferreds early to exercise the wait-then-fail path
d1_verified = w1.when_verified() d1_verified = w1.get_verifier()
d1_version = w1.when_version() d1_versions = w1.get_versions()
d1_received = w1.when_received() d1_received = w1.get_message()
d2_verified = w2.when_verified() d2_verified = w2.get_verifier()
d2_version = w2.when_version() d2_versions = w2.get_versions()
d2_received = w2.when_received() d2_received = w2.get_message()
# wait for each side to notice the failure # wait for each side to notice the failure
yield self.assertFailure(w1.when_verified(), WrongPasswordError) yield self.assertFailure(w1.get_verifier(), WrongPasswordError)
yield self.assertFailure(w2.when_verified(), WrongPasswordError) yield self.assertFailure(w2.get_verifier(), WrongPasswordError)
# and then wait for the rest of the loops to fire. if we had+used # and then wait for the rest of the loops to fire. if we had+used
# eventual-send, this wouldn't be a problem # eventual-send, this wouldn't be a problem
yield pause_one_tick() yield pause_one_tick()
# now all the rest should have fired already # now all the rest should have fired already
self.failureResultOf(d1_verified, WrongPasswordError) self.failureResultOf(d1_verified, WrongPasswordError)
self.failureResultOf(d1_version, WrongPasswordError) self.failureResultOf(d1_versions, WrongPasswordError)
self.failureResultOf(d1_received, WrongPasswordError) self.failureResultOf(d1_received, WrongPasswordError)
self.failureResultOf(d2_verified, WrongPasswordError) self.failureResultOf(d2_verified, WrongPasswordError)
self.failureResultOf(d2_version, WrongPasswordError) self.failureResultOf(d2_versions, WrongPasswordError)
self.failureResultOf(d2_received, WrongPasswordError) self.failureResultOf(d2_received, WrongPasswordError)
# and at this point, with the failure safely noticed by both sides, # and at this point, with the failure safely noticed by both sides,
# new when_key() calls should signal the failure, even before we # new get_unverified_key() calls should signal the failure, even
# close # before we close
# any new calls in the error state should immediately fail # any new calls in the error state should immediately fail
self.failureResultOf(w1.when_key(), WrongPasswordError) self.failureResultOf(w1.get_unverified_key(), WrongPasswordError)
self.failureResultOf(w1.when_verified(), WrongPasswordError) self.failureResultOf(w1.get_verifier(), WrongPasswordError)
self.failureResultOf(w1.when_version(), WrongPasswordError) self.failureResultOf(w1.get_versions(), WrongPasswordError)
self.failureResultOf(w1.when_received(), WrongPasswordError) self.failureResultOf(w1.get_message(), WrongPasswordError)
self.failureResultOf(w2.when_key(), WrongPasswordError) self.failureResultOf(w2.get_unverified_key(), WrongPasswordError)
self.failureResultOf(w2.when_verified(), WrongPasswordError) self.failureResultOf(w2.get_verifier(), WrongPasswordError)
self.failureResultOf(w2.when_version(), WrongPasswordError) self.failureResultOf(w2.get_versions(), WrongPasswordError)
self.failureResultOf(w2.when_received(), WrongPasswordError) self.failureResultOf(w2.get_message(), WrongPasswordError)
yield self.assertFailure(w1.close(), WrongPasswordError) yield self.assertFailure(w1.close(), WrongPasswordError)
yield self.assertFailure(w2.close(), WrongPasswordError) yield self.assertFailure(w2.close(), WrongPasswordError)
# API calls should still get the error, not WormholeClosed # API calls should still get the error, not WormholeClosed
self.failureResultOf(w1.when_key(), WrongPasswordError) self.failureResultOf(w1.get_unverified_key(), WrongPasswordError)
self.failureResultOf(w1.when_verified(), WrongPasswordError) self.failureResultOf(w1.get_verifier(), WrongPasswordError)
self.failureResultOf(w1.when_version(), WrongPasswordError) self.failureResultOf(w1.get_versions(), WrongPasswordError)
self.failureResultOf(w1.when_received(), WrongPasswordError) self.failureResultOf(w1.get_message(), WrongPasswordError)
self.failureResultOf(w2.when_key(), WrongPasswordError) self.failureResultOf(w2.get_unverified_key(), WrongPasswordError)
self.failureResultOf(w2.when_verified(), WrongPasswordError) self.failureResultOf(w2.get_verifier(), WrongPasswordError)
self.failureResultOf(w2.when_version(), WrongPasswordError) self.failureResultOf(w2.get_versions(), WrongPasswordError)
self.failureResultOf(w2.when_received(), WrongPasswordError) self.failureResultOf(w2.get_message(), WrongPasswordError)
@inlineCallbacks @inlineCallbacks
def test_wrong_password_with_spaces(self): def test_wrong_password_with_spaces(self):
@ -442,21 +428,21 @@ class Wormholes(ServerBase, unittest.TestCase):
w1 = wormhole.create(APPID, self.relayurl, reactor) w1 = wormhole.create(APPID, self.relayurl, reactor)
w2 = wormhole.create(APPID, self.relayurl, reactor) w2 = wormhole.create(APPID, self.relayurl, reactor)
w1.allocate_code() w1.allocate_code()
code = yield w1.when_code() code = yield w1.get_code()
w2.set_code(code) w2.set_code(code)
v1 = yield w1.when_verified() # early v1 = yield w1.get_verifier() # early
v2 = yield w2.when_verified() v2 = yield w2.get_verifier()
self.failUnlessEqual(type(v1), type(b"")) self.failUnlessEqual(type(v1), type(b""))
self.failUnlessEqual(v1, v2) self.failUnlessEqual(v1, v2)
w1.send(b"data1") w1.send_message(b"data1")
w2.send(b"data2") w2.send_message(b"data2")
dataX = yield w1.when_received() dataX = yield w1.get_message()
dataY = yield w2.when_received() dataY = yield w2.get_message()
self.assertEqual(dataX, b"data2") self.assertEqual(dataX, b"data2")
self.assertEqual(dataY, b"data1") self.assertEqual(dataY, b"data1")
# calling when_verified() this late should fire right away # calling get_verifier() this late should fire right away
v1_late = self.successResultOf(w2.when_verified()) v1_late = self.successResultOf(w2.get_verifier())
self.assertEqual(v1_late, v1) self.assertEqual(v1_late, v1)
yield w1.close() yield w1.close()
@ -470,11 +456,11 @@ class Wormholes(ServerBase, unittest.TestCase):
w2 = wormhole.create(APPID, self.relayurl, reactor, w2 = wormhole.create(APPID, self.relayurl, reactor,
versions={"w2": 456}) versions={"w2": 456})
w1.allocate_code() w1.allocate_code()
code = yield w1.when_code() code = yield w1.get_code()
w2.set_code(code) w2.set_code(code)
w1_versions = yield w2.when_version() w1_versions = yield w2.get_versions()
self.assertEqual(w1_versions, {"w1": 123}) self.assertEqual(w1_versions, {"w1": 123})
w2_versions = yield w1.when_version() w2_versions = yield w1.get_versions()
self.assertEqual(w2_versions, {"w2": 456}) self.assertEqual(w2_versions, {"w2": 456})
yield w1.close() yield w1.close()
yield w2.close() yield w2.close()
@ -496,8 +482,8 @@ class Wormholes(ServerBase, unittest.TestCase):
w2 = wormhole.create(APPID, self.relayurl, reactor) w2 = wormhole.create(APPID, self.relayurl, reactor)
w1.set_code("123-purple-elephant") w1.set_code("123-purple-elephant")
w2.set_code("123-purple-elephant") w2.set_code("123-purple-elephant")
w1.send(b"data1"), w2.send(b"data2") w1.send_message(b"data1"), w2.send_message(b"data2")
dl = yield self.doBoth(w1.when_received(), w2.when_received()) dl = yield self.doBoth(w1.get_message(), w2.get_message())
(dataX, dataY) = dl (dataX, dataY) = dl
self.assertEqual(dataX, b"data2") self.assertEqual(dataX, b"data2")
self.assertEqual(dataY, b"data1") self.assertEqual(dataY, b"data1")
@ -537,7 +523,7 @@ class Errors(ServerBase, unittest.TestCase):
def test_allocate_and_set_code(self): def test_allocate_and_set_code(self):
w = wormhole.create(APPID, self.relayurl, reactor) w = wormhole.create(APPID, self.relayurl, reactor)
w.allocate_code() w.allocate_code()
yield w.when_code() yield w.get_code()
with self.assertRaises(OnlyOneCodeError): with self.assertRaises(OnlyOneCodeError):
w.set_code("123-nope") w.set_code("123-nope")
yield self.assertFailure(w.close(), LonelyError) yield self.assertFailure(w.close(), LonelyError)
@ -550,8 +536,8 @@ class Reconnection(ServerBase, unittest.TestCase):
w1._boss._RC._debug_record_inbound_f = w1_in.append w1._boss._RC._debug_record_inbound_f = w1_in.append
#w1.debug_set_trace("W1") #w1.debug_set_trace("W1")
w1.allocate_code() w1.allocate_code()
code = yield w1.when_code() code = yield w1.get_code()
w1.send(b"data1") # will be queued until wormhole is established w1.send_message(b"data1") # queued until wormhole is established
# now wait until we've deposited all our messages on the server # now wait until we've deposited all our messages on the server
def seen_our_pake(): def seen_our_pake():
@ -577,11 +563,11 @@ class Reconnection(ServerBase, unittest.TestCase):
#w2.debug_set_trace(" W2") #w2.debug_set_trace(" W2")
w2.set_code(code) w2.set_code(code)
dataY = yield w2.when_received() dataY = yield w2.get_message()
self.assertEqual(dataY, b"data1") self.assertEqual(dataY, b"data1")
w2.send(b"data2") w2.send_message(b"data2")
dataX = yield w1.when_received() dataX = yield w1.get_message()
self.assertEqual(dataX, b"data2") self.assertEqual(dataX, b"data2")
c1 = yield w1.close() c1 = yield w1.close()

View File

@ -15,18 +15,18 @@ from .util import to_bytes
# We can provide different APIs to different apps: # We can provide different APIs to different apps:
# * Deferreds # * Deferreds
# w.when_code().addCallback(print_code) # w.get_code().addCallback(print_code)
# w.send(data) # w.send_message(data)
# w.when_received().addCallback(got_data) # w.get_message().addCallback(got_data)
# w.close().addCallback(closed) # w.close().addCallback(closed)
# * delegate callbacks (better for journaled environments) # * delegate callbacks (better for journaled environments)
# w = wormhole(delegate=app) # w = wormhole(delegate=app)
# w.send(data) # w.send_message(data)
# app.wormhole_got_code(code) # app.wormhole_got_code(code)
# app.wormhole_got_verifier(verifier) # app.wormhole_got_verifier(verifier)
# app.wormhole_got_version(versions) # app.wormhole_got_versions(versions)
# app.wormhole_receive(data) # app.wormhole_got_message(data)
# w.close() # w.close()
# app.wormhole_closed() # app.wormhole_closed()
# #
@ -34,18 +34,6 @@ from .util import to_bytes
# wormhole(delegate=app, delegate_prefix="wormhole_", # wormhole(delegate=app, delegate_prefix="wormhole_",
# delegate_args=(args, kwargs)) # delegate_args=(args, kwargs))
class _WelcomeHandler:
def __init__(self, url, stderr=sys.stderr):
self.relay_url = url
self.stderr = stderr
def handle_welcome(self, welcome):
if "motd" in welcome:
motd_lines = welcome["motd"].splitlines()
motd_formatted = "\n ".join(motd_lines)
print("Server (at %s) says:\n %s" %
(self.relay_url, motd_formatted), file=self.stderr)
@attrs @attrs
@implementer(IWormhole) @implementer(IWormhole)
class _DelegatedWormhole(object): class _DelegatedWormhole(object):
@ -72,7 +60,7 @@ class _DelegatedWormhole(object):
## } ## }
## return s ## return s
def send(self, plaintext): def send_message(self, plaintext):
self._boss.send(plaintext) self._boss.send(plaintext)
def derive_key(self, purpose, length): def derive_key(self, purpose, length):
@ -94,23 +82,27 @@ class _DelegatedWormhole(object):
self._boss._set_trace(client_name, which, file) self._boss._set_trace(client_name, which, file)
# from below # from below
def got_welcome(self, welcome):
self._delegate.wormhole_got_welcome(welcome)
def got_code(self, code): def got_code(self, code):
self._delegate.wormhole_code(code) self._delegate.wormhole_got_code(code)
def got_key(self, key): def got_key(self, key):
self._delegate.wormhole_key(key) self._delegate.wormhole_got_unverified_key(key)
self._key = key # for derive_key() self._key = key # for derive_key()
def got_verifier(self, verifier): def got_verifier(self, verifier):
self._delegate.wormhole_verified(verifier) self._delegate.wormhole_got_verifier(verifier)
def got_version(self, versions): def got_versions(self, versions):
self._delegate.wormhole_version(versions) self._delegate.wormhole_got_versions(versions)
def received(self, plaintext): def received(self, plaintext):
self._delegate.wormhole_received(plaintext) self._delegate.wormhole_got_message(plaintext)
def closed(self, result): def closed(self, result):
self._delegate.wormhole_closed(result) self._delegate.wormhole_closed(result)
@implementer(IWormhole) @implementer(IWormhole)
class _DeferredWormhole(object): class _DeferredWormhole(object):
def __init__(self): def __init__(self):
self._welcome = None
self._welcome_observers = []
self._code = None self._code = None
self._code_observers = [] self._code_observers = []
self._key = None self._key = None
@ -129,7 +121,7 @@ class _DeferredWormhole(object):
self._boss = boss self._boss = boss
# from above # from above
def when_code(self): def get_code(self):
# TODO: consider throwing error unless one of allocate/set/input_code # TODO: consider throwing error unless one of allocate/set/input_code
# was called first. It's legit to grab the Deferred before triggering # was called first. It's legit to grab the Deferred before triggering
# the process that will cause it to fire, but forbidding that # the process that will cause it to fire, but forbidding that
@ -143,7 +135,16 @@ class _DeferredWormhole(object):
self._code_observers.append(d) self._code_observers.append(d)
return d return d
def when_key(self): def get_welcome(self):
if self._observer_result is not None:
return defer.fail(self._observer_result)
if self._welcome is not None:
return defer.succeed(self._welcome)
d = defer.Deferred()
self._welcome_observers.append(d)
return d
def get_unverified_key(self):
if self._observer_result is not None: if self._observer_result is not None:
return defer.fail(self._observer_result) return defer.fail(self._observer_result)
if self._key is not None: if self._key is not None:
@ -152,7 +153,7 @@ class _DeferredWormhole(object):
self._key_observers.append(d) self._key_observers.append(d)
return d return d
def when_verified(self): def get_verifier(self):
if self._observer_result is not None: if self._observer_result is not None:
return defer.fail(self._observer_result) return defer.fail(self._observer_result)
if self._verifier is not None: if self._verifier is not None:
@ -161,7 +162,7 @@ class _DeferredWormhole(object):
self._verifier_observers.append(d) self._verifier_observers.append(d)
return d return d
def when_version(self): def get_versions(self):
if self._observer_result is not None: if self._observer_result is not None:
return defer.fail(self._observer_result) return defer.fail(self._observer_result)
if self._versions is not None: if self._versions is not None:
@ -170,7 +171,7 @@ class _DeferredWormhole(object):
self._version_observers.append(d) self._version_observers.append(d)
return d return d
def when_received(self): def get_message(self):
if self._observer_result is not None: if self._observer_result is not None:
return defer.fail(self._observer_result) return defer.fail(self._observer_result)
if self._received_data: if self._received_data:
@ -187,7 +188,8 @@ class _DeferredWormhole(object):
self._boss.set_code(code) self._boss.set_code(code)
# no .serialize in Deferred-mode # no .serialize in Deferred-mode
def send(self, plaintext):
def send_message(self, plaintext):
self._boss.send(plaintext) self._boss.send(plaintext)
def derive_key(self, purpose, length): def derive_key(self, purpose, length):
@ -217,6 +219,11 @@ class _DeferredWormhole(object):
self._boss._set_trace(client_name, which, file) self._boss._set_trace(client_name, which, file)
# from below # from below
def got_welcome(self, welcome):
self._welcome = welcome
for d in self._welcome_observers:
d.callback(welcome)
self._welcome_observers[:] = []
def got_code(self, code): def got_code(self, code):
self._code = code self._code = code
for d in self._code_observers: for d in self._code_observers:
@ -227,12 +234,13 @@ class _DeferredWormhole(object):
for d in self._key_observers: for d in self._key_observers:
d.callback(key) d.callback(key)
self._key_observers[:] = [] self._key_observers[:] = []
def got_verifier(self, verifier): def got_verifier(self, verifier):
self._verifier = verifier self._verifier = verifier
for d in self._verifier_observers: for d in self._verifier_observers:
d.callback(verifier) d.callback(verifier)
self._verifier_observers[:] = [] self._verifier_observers[:] = []
def got_version(self, versions): def got_versions(self, versions):
self._versions = versions self._versions = versions
for d in self._version_observers: for d in self._version_observers:
d.callback(versions) d.callback(versions)
@ -245,7 +253,7 @@ class _DeferredWormhole(object):
self._received_data.append(plaintext) self._received_data.append(plaintext)
def closed(self, result): def closed(self, result):
#print("closed", result, type(result)) #print("closed", result, type(result), file=sys.stderr)
if isinstance(result, Exception): if isinstance(result, Exception):
self._observer_result = self._closed_result = failure.Failure(result) self._observer_result = self._closed_result = failure.Failure(result)
else: else:
@ -253,6 +261,8 @@ class _DeferredWormhole(object):
self._observer_result = WormholeClosed(result) self._observer_result = WormholeClosed(result)
# but w.close() only gets error if we're unhappy # but w.close() only gets error if we're unhappy
self._closed_result = result self._closed_result = result
for d in self._welcome_observers:
d.errback(self._observer_result)
for d in self._code_observers: for d in self._code_observers:
d.errback(self._observer_result) d.errback(self._observer_result)
for d in self._key_observers: for d in self._key_observers:
@ -270,13 +280,11 @@ class _DeferredWormhole(object):
def create(appid, relay_url, reactor, # use keyword args for everything else def create(appid, relay_url, reactor, # use keyword args for everything else
versions={}, versions={},
delegate=None, journal=None, tor_manager=None, delegate=None, journal=None, tor_manager=None,
timing=None, welcome_handler=None, timing=None,
stderr=sys.stderr): stderr=sys.stderr):
timing = timing or DebugTiming() timing = timing or DebugTiming()
side = bytes_to_hexstr(os.urandom(5)) side = bytes_to_hexstr(os.urandom(5))
journal = journal or ImmediateJournal() journal = journal or ImmediateJournal()
if not welcome_handler:
welcome_handler = _WelcomeHandler(relay_url).handle_welcome
if delegate: if delegate:
w = _DelegatedWormhole(delegate) w = _DelegatedWormhole(delegate)
else: else:
@ -284,8 +292,7 @@ def create(appid, relay_url, reactor, # use keyword args for everything else
wormhole_versions = {} # will be used to indicate Wormhole capabilities wormhole_versions = {} # will be used to indicate Wormhole capabilities
wormhole_versions["app_versions"] = versions # app-specific capabilities wormhole_versions["app_versions"] = versions # app-specific capabilities
b = Boss(w, side, relay_url, appid, wormhole_versions, b = Boss(w, side, relay_url, appid, wormhole_versions,
welcome_handler, reactor, journal, reactor, journal, tor_manager, timing)
tor_manager, timing)
w._set_boss(b) w._set_boss(b)
b.start() b.start()
return w return w

View File

@ -41,14 +41,14 @@ def receive(reactor, appid, relay_url, code,
wh = wormhole.create(appid, relay_url, reactor, tor_manager=tm) wh = wormhole.create(appid, relay_url, reactor, tor_manager=tm)
if code is None: if code is None:
wh.allocate_code() wh.allocate_code()
code = yield wh.when_code() code = yield wh.get_code()
else: else:
wh.set_code(code) wh.set_code(code)
# we'll call this no matter what, even if you passed in a code -- # we'll call this no matter what, even if you passed in a code --
# maybe it should be only in the 'if' block above? # maybe it should be only in the 'if' block above?
if on_code: if on_code:
on_code(code) on_code(code)
data = yield wh.when_received() data = yield wh.get_message()
data = json.loads(data.decode("utf-8")) data = json.loads(data.decode("utf-8"))
offer = data.get('offer', None) offer = data.get('offer', None)
if not offer: if not offer:
@ -58,7 +58,8 @@ def receive(reactor, appid, relay_url, code,
msg = None msg = None
if 'message' in offer: if 'message' in offer:
msg = offer['message'] msg = offer['message']
wh.send(json.dumps({"answer": {"message_ack": "ok"}}).encode("utf-8")) wh.send_message(json.dumps({"answer":
{"message_ack": "ok"}}).encode("utf-8"))
else: else:
raise Exception( raise Exception(
@ -104,20 +105,20 @@ def send(reactor, appid, relay_url, data, code,
wh = wormhole.create(appid, relay_url, reactor, tor_manager=tm) wh = wormhole.create(appid, relay_url, reactor, tor_manager=tm)
if code is None: if code is None:
wh.allocate_code() wh.allocate_code()
code = yield wh.when_code() code = yield wh.get_code()
else: else:
wh.set_code(code) wh.set_code(code)
if on_code: if on_code:
on_code(code) on_code(code)
wh.send( wh.send_message(
json.dumps({ json.dumps({
"offer": { "offer": {
"message": data "message": data
} }
}).encode("utf-8") }).encode("utf-8")
) )
data = yield wh.when_received() data = yield wh.get_message()
data = json.loads(data.decode("utf-8")) data = json.loads(data.decode("utf-8"))
answer = data.get('answer', None) answer = data.get('answer', None)
yield wh.close() yield wh.close()