Merge branch 'new-api-welcome'
This fixes #145. I thought it would add a roundtrip, but it turns out that websockets were already adding the same delay, so I can't avoid it without switching from websockets to raw TCP. Closes #145.
This commit is contained in:
commit
d0b3e11950
226
docs/api.md
226
docs/api.md
|
@ -30,10 +30,10 @@ from twisted.internet.defer import inlineCallbacks
|
|||
def go():
|
||||
w = wormhole.create(appid, relay_url, reactor)
|
||||
w.generate_code()
|
||||
code = yield w.when_code()
|
||||
code = yield w.get_code()
|
||||
print "code:", code
|
||||
w.send(b"outbound data")
|
||||
inbound = yield w.when_received()
|
||||
w.send_message(b"outbound data")
|
||||
inbound = yield w.get_message()
|
||||
yield w.close()
|
||||
```
|
||||
|
||||
|
@ -55,8 +55,8 @@ Delegated mode:
|
|||
class MyDelegate:
|
||||
def wormhole_got_code(self, code):
|
||||
print("code: %s" % code)
|
||||
def wormhole_received(self, data): # called for each message
|
||||
print("got data, %d bytes" % len(data))
|
||||
def wormhole_got_message(self, msg): # called for each message
|
||||
print("got data, %d bytes" % len(msg))
|
||||
|
||||
w = wormhole.create(appid, relay_url, reactor, delegate=MyDelegate())
|
||||
w.generate_code()
|
||||
|
@ -69,10 +69,10 @@ w = wormhole.create(appid, relay_url, reactor)
|
|||
w.generate_code()
|
||||
def print_code(code):
|
||||
print("code: %s" % code)
|
||||
w.when_code().addCallback(print_code)
|
||||
def received(data):
|
||||
print("got data, %d bytes" % len(data))
|
||||
w.when_received().addCallback(received) # gets exactly one message
|
||||
w.get_code().addCallback(print_code)
|
||||
def received(msg):
|
||||
print("got data, %d bytes" % len(msg))
|
||||
w.get_message().addCallback(received) # gets exactly one message
|
||||
```
|
||||
|
||||
## Application Identifier
|
||||
|
@ -168,9 +168,9 @@ The Wormhole object has three APIs for generating or accepting a code:
|
|||
far. A convenience wrapper is provided to attach this to the `rlcompleter`
|
||||
function of libreadline.
|
||||
|
||||
No matter which mode is used, the `w.when_code()` Deferred (or
|
||||
No matter which mode is used, the `w.get_code()` Deferred (or
|
||||
`delegate.wormhole_got_code(code)` callback) will fire when the code is
|
||||
known. `when_code` is clearly necessary for `generate_code`, since there's no
|
||||
known. `get_code` is clearly necessary for `generate_code`, since there's no
|
||||
other way to learn what code was created, but it may be useful in other modes
|
||||
for consistency.
|
||||
|
||||
|
@ -224,7 +224,7 @@ The code-entry Helper object has the following API:
|
|||
before display.
|
||||
* `h.choose_words(words)`: call this when the user is finished typing in the
|
||||
code. It does not return anything, but will cause the Wormhole's
|
||||
`w.when_code()` (or corresponding delegate) to fire, and triggers the
|
||||
`w.get_code()` (or corresponding delegate) to fire, and triggers the
|
||||
wormhole connection process. This accepts a string like "purple-sausages",
|
||||
without the nameplate. It must be called after `h.choose_nameplate()` or
|
||||
`MustChooseNameplateFirstError` will be raised. May only be called once,
|
||||
|
@ -237,7 +237,7 @@ code-entry helper to do tab completion of wormhole codes:
|
|||
from wormhole import create, input_with_completion
|
||||
w = create(appid, relay_url, reactor)
|
||||
input_with_completion("Wormhole code:", w.input_code(), reactor)
|
||||
d = w.when_code()
|
||||
d = w.get_code()
|
||||
```
|
||||
|
||||
This helper runs python's (raw) `input()` function inside a thread, since
|
||||
|
@ -276,33 +276,13 @@ random values (e.g. by using the Diceware wordlist) unless that makes it
|
|||
easier to transcribe: e.g. rolling 6 dice could result in a code like
|
||||
"913-166532", and flipping 16 coins could result in "123-HTTHHHTTHTTHHTHH".
|
||||
|
||||
## Verifier
|
||||
|
||||
For extra protection against guessing attacks, Wormhole can provide a
|
||||
"Verifier". This is a moderate-length series of bytes (a SHA256 hash) that is
|
||||
derived from the supposedly-shared session key. If desired, both sides can
|
||||
display this value, and the humans can manually compare them before allowing
|
||||
the rest of the protocol to proceed. If they do not match, then the two
|
||||
programs are not talking to each other (they may both be talking to a
|
||||
man-in-the-middle attacker), and the protocol should be abandoned.
|
||||
|
||||
Deferred-mode applications can wait for `d=w.when_verified()`: the Deferred
|
||||
it returns will fire with the verifier. You can turn this into hex or Base64
|
||||
to print it, or render it as ASCII-art, etc.
|
||||
|
||||
Asking the wormhole object for the verifier does not affect the flow of the
|
||||
protocol. To benefit from verification, applications must refrain from
|
||||
sending any data (with `w.send(data)`) until after the verifiers are approved
|
||||
by the user. In addition, applications must queue or otherwise ignore
|
||||
incoming (received) messages until that point. However once the verifiers are
|
||||
confirmed, previously-received messages can be considered valid and processed
|
||||
as usual.
|
||||
|
||||
## Welcome Messages
|
||||
|
||||
The first message sent by the rendezvous server is a "welcome" message (a
|
||||
dictionary). Clients should not wait for this message, but when it arrives,
|
||||
they should process the keys it contains.
|
||||
dictionary). This is sent as soon as the client connects to the server,
|
||||
generally before the code is established. Clients should use
|
||||
`d=get_welcome()` to get and process the `motd` key (and maybe
|
||||
`current_cli_version`) inside the welcome message.
|
||||
|
||||
The welcome message serves three main purposes:
|
||||
|
||||
|
@ -357,17 +337,45 @@ the library did not pay attention to the ERROR message, hence the server
|
|||
should deliver errors in a WELCOME message if at all possible)
|
||||
|
||||
The `error` field is handled internally by the Wormhole object. The other
|
||||
fields are processed by an application-supplied "welcome handler" function,
|
||||
supplied as an argument to the `wormhole()` constructor. This function will
|
||||
be called with the full welcome dictionary, so any other keys that a future
|
||||
server might send will be available to it. If the welcome handler raises
|
||||
`WelcomeError`, the connection will be closed just as if an `error` key had
|
||||
been received. The handler may be called multiple times (once per connection,
|
||||
if the rendezvous connection is lost and then reestablished), so applications
|
||||
should avoid presenting the user with redundant messages.
|
||||
fields can be processed by application, by using `d=w.get_welcome()`. The
|
||||
Deferred will fire with the full welcome dictionary, so any other keys that a
|
||||
future server might send will be available to it.
|
||||
|
||||
The default welcome handler will print `motd` to stderr, and will ignore
|
||||
`current_cli_version`.
|
||||
Applications which need to display `motd` or an upgrade message, and wish to
|
||||
do so before using stdin/stdout for interactive code entry (`w.input_code()`)
|
||||
should wait for `get_welcome()` before starting the entry process:
|
||||
|
||||
```python
|
||||
@inlineCallbacks
|
||||
def go():
|
||||
w = wormhole.create(appid, relay_url, reactor)
|
||||
welcome = yield w.get_welcome()
|
||||
if "motd" in welcome: print welcome["motd"]
|
||||
input_with_completion("Wormhole code:", w.input_code(), reactor)
|
||||
...
|
||||
```
|
||||
|
||||
## Verifier
|
||||
|
||||
For extra protection against guessing attacks, Wormhole can provide a
|
||||
"Verifier". This is a moderate-length series of bytes (a SHA256 hash) that is
|
||||
derived from the supposedly-shared session key. If desired, both sides can
|
||||
display this value, and the humans can manually compare them before allowing
|
||||
the rest of the protocol to proceed. If they do not match, then the two
|
||||
programs are not talking to each other (they may both be talking to a
|
||||
man-in-the-middle attacker), and the protocol should be abandoned.
|
||||
|
||||
Deferred-mode applications can wait for `d=w.get_verifier()`: the Deferred
|
||||
it returns will fire with the verifier. You can turn this into hex or Base64
|
||||
to print it, or render it as ASCII-art, etc.
|
||||
|
||||
Asking the wormhole object for the verifier does not affect the flow of the
|
||||
protocol. To benefit from verification, applications must refrain from
|
||||
sending any data (with `w.send_message(data)`) until after the verifiers are
|
||||
approved by the user. In addition, applications must queue or otherwise
|
||||
ignore incoming (received) messages until that point. However once the
|
||||
verifiers are confirmed, previously-received messages can be considered valid
|
||||
and processed as usual.
|
||||
|
||||
## Events
|
||||
|
||||
|
@ -377,36 +385,45 @@ functions on the delegate object. In Deferred mode, the application retrieves
|
|||
Deferred objects from the wormhole, and event dispatch is performed by firing
|
||||
those Deferreds.
|
||||
|
||||
* got_code (`yield w.when_code()` / `dg.wormhole_code(code)`): fired when the
|
||||
wormhole code is established, either after `w.generate_code()` finishes the
|
||||
generation process, or when the Input Helper returned by `w.input_code()`
|
||||
has been told `h.set_words()`, or immediately after `w.set_code(code)` is
|
||||
called. This is most useful after calling `w.generate_code()`, to show the
|
||||
generated code to the user so they can transcribe it to their peer.
|
||||
* key (`yield w.when_key()` / `dg.wormhole_key()`): fired when the
|
||||
key-exchange process has completed and a purported shared key is
|
||||
established. At this point we do not know that anyone else actually shares
|
||||
this key: the peer may have used the wrong code, or may have disappeared
|
||||
altogether. To wait for proof that the key is shared, wait for
|
||||
`when_verified` instead. This event is really only useful for detecting
|
||||
that the initiating peer has disconnected after leaving the initial PAKE
|
||||
Most applications will only use `code`, `received`, and `close`.
|
||||
|
||||
* code (`code = yield w.get_code()` / `dg.wormhole_got_code(code)`): fired
|
||||
when the wormhole code is established, either after `w.generate_code()`
|
||||
finishes the generation process, or when the Input Helper returned by
|
||||
`w.input_code()` has been told `h.set_words()`, or immediately after
|
||||
`w.set_code(code)` is called. This is most useful after calling
|
||||
`w.generate_code()`, to show the generated code to the user so they can
|
||||
transcribe it to their peer.
|
||||
* key (`yield w.get_unverified_key()` /
|
||||
`dg.wormhole_got_unverified_key(key)`): fired (with the raw master SPAKE2
|
||||
key) when the key-exchange process has completed and a purported shared key
|
||||
is established. At this point we do not know that anyone else actually
|
||||
shares this key: the peer may have used the wrong code, or may have
|
||||
disappeared altogether. To wait for proof that the key is shared, wait for
|
||||
`get_verifier` instead. This event is really only useful for detecting that
|
||||
the initiating peer has disconnected after leaving the initial PAKE
|
||||
message, to display a pacifying message to the user.
|
||||
* verified (`verifier = yield w.when_verified()` /
|
||||
`dg.wormhole_verified(verifier)`: fired when the key-exchange process has
|
||||
completed and a valid VERSION message has arrived. The "verifier" is a byte
|
||||
string with a hash of the shared session key; clients can compare them
|
||||
* verifier (`verifier = yield w.get_verifier()` /
|
||||
`dg.wormhole_got_verifier(verifier)`: fired when the key-exchange process
|
||||
has completed and a valid VERSION message has arrived. The "verifier" is a
|
||||
byte string with a hash of the shared session key; clients can compare them
|
||||
(probably as hex) to ensure that they're really talking to each other, and
|
||||
not to a man-in-the-middle. When `got_verifier` happens, this side knows
|
||||
not to a man-in-the-middle. When `get_verifier` happens, this side knows
|
||||
that *someone* has used the correct wormhole code; if someone used the
|
||||
wrong code, the VERSION message cannot be decrypted, and the wormhole will
|
||||
be closed instead.
|
||||
* version (`yield w.when_version()` / `dg.wormhole_version(versions)`: fired
|
||||
when the VERSION message arrives from the peer. This fires at the same time
|
||||
as `verified`, but delivers the "app_versions" data (as passed into
|
||||
`wormhole.create(versions=)`) instead of the verifier string.
|
||||
* received (`yield w.when_received()` / `dg.wormhole_received(data)`: fired
|
||||
* versions (`versions = yield w.get_versions()` /
|
||||
`dg.wormhole_got_versions(versions)`: fired when the VERSION message
|
||||
arrives from the peer. This fires just after `verified`, but delivers the
|
||||
"app_versions" data (as passed into `wormhole.create(versions=)`) instead
|
||||
of the verifier string. This is mostly a hack to make room for
|
||||
forwards-compatible changes to the CLI file-transfer protocol, which sends
|
||||
a request in the first message (rather than merely sending the abilities of
|
||||
each side).
|
||||
* received (`yield w.get_message()` / `dg.wormhole_got_message(msg)`: fired
|
||||
each time a data message arrives from the peer, with the bytestring that
|
||||
the peer passed into `w.send(data)`.
|
||||
the peer passed into `w.send_message(msg)`. This is the primary
|
||||
data-transfer API.
|
||||
* closed (`yield w.close()` / `dg.wormhole_closed(result)`: fired when
|
||||
`w.close()` has finished shutting down the wormhole, which means all
|
||||
nameplates and mailboxes have been deallocated, and the WebSocket
|
||||
|
@ -419,27 +436,28 @@ those Deferreds.
|
|||
## Sending Data
|
||||
|
||||
The main purpose of a Wormhole is to send data. At any point after
|
||||
construction, callers can invoke `w.send(data)`. This will queue the message
|
||||
if necessary, but (if all goes well) will eventually result in the peer
|
||||
getting a `received` event and the data being delivered to the application.
|
||||
construction, callers can invoke `w.send_message(msg)`. This will queue the
|
||||
message if necessary, but (if all goes well) will eventually result in the
|
||||
peer getting a `received` event and the data being delivered to the
|
||||
application.
|
||||
|
||||
Since Wormhole provides an ordered record pipe, each call to `w.send` will
|
||||
result in exactly one `received` event on the far side. Records are not
|
||||
Since Wormhole provides an ordered record pipe, each call to `w.send_message`
|
||||
will result in exactly one `received` event on the far side. Records are not
|
||||
split, merged, dropped, or reordered.
|
||||
|
||||
Each side can do an arbitrary number of `send()` calls. The Wormhole is not
|
||||
meant as a long-term communication channel, but some protocols work better if
|
||||
they can exchange an initial pair of messages (perhaps offering some set of
|
||||
negotiable capabilities), and then follow up with a second pair (to reveal
|
||||
the results of the negotiation). The Rendezvous Server does not currently
|
||||
enforce any particular limits on number of messages, size of messages, or
|
||||
rate of transmission, but in general clients are expected to send fewer than
|
||||
a dozen messages, of no more than perhaps 20kB in size (remember that all
|
||||
these messages are temporarily stored in a SQLite database on the server). A
|
||||
future version of the protocol may make these limits more explicit, and will
|
||||
allow clients to ask for greater capacity when they connect (probably by
|
||||
passing additional "mailbox attribute" parameters with the
|
||||
`allocate`/`claim`/`open` messages).
|
||||
Each side can do an arbitrary number of `send_message()` calls. The Wormhole
|
||||
is not meant as a long-term communication channel, but some protocols work
|
||||
better if they can exchange an initial pair of messages (perhaps offering
|
||||
some set of negotiable capabilities), and then follow up with a second pair
|
||||
(to reveal the results of the negotiation). The Rendezvous Server does not
|
||||
currently enforce any particular limits on number of messages, size of
|
||||
messages, or rate of transmission, but in general clients are expected to
|
||||
send fewer than a dozen messages, of no more than perhaps 20kB in size
|
||||
(remember that all these messages are temporarily stored in a SQLite database
|
||||
on the server). A future version of the protocol may make these limits more
|
||||
explicit, and will allow clients to ask for greater capacity when they
|
||||
connect (probably by passing additional "mailbox attribute" parameters with
|
||||
the `allocate`/`claim`/`open` messages).
|
||||
|
||||
For bulk data transfer, see "transit.md", or the "Dilation" section below.
|
||||
|
||||
|
@ -531,7 +549,7 @@ What's good about dilated wormholes?:
|
|||
|
||||
Use non-dilated wormholes when your application only needs to exchange a
|
||||
couple of messages, for example to set up public keys or provision access
|
||||
tokens. Use a dilated wormhole to move large files.
|
||||
tokens. Use a dilated wormhole to move files.
|
||||
|
||||
Dilated wormholes can provide multiple "channels": these are multiplexed
|
||||
through the single (encrypted) TCP connection. Each channel is a separate
|
||||
|
@ -570,17 +588,19 @@ in python3):
|
|||
|
||||
## Full API list
|
||||
|
||||
action | Deferred-Mode | Delegated-Mode
|
||||
------------------ | -------------------- | --------------
|
||||
w.generate_code() | |
|
||||
w.set_code(code) | |
|
||||
h=w.input_code() | |
|
||||
. | d=w.when_code() | dg.wormhole_code(code)
|
||||
. | d=w.when_verified() | dg.wormhole_verified(verifier)
|
||||
. | d=w.when_version() | dg.wormhole_version(version)
|
||||
w.send(data) | |
|
||||
. | d=w.when_received() | dg.wormhole_received(data)
|
||||
key=w.derive_key(purpose, length) | |
|
||||
w.close() | | dg.wormhole_closed(result)
|
||||
. | d=w.close() |
|
||||
action | Deferred-Mode | Delegated-Mode
|
||||
------------------ | ------------------ | --------------
|
||||
. | d=w.get_welcome() | dg.wormhole_got_welcome(welcome)
|
||||
w.generate_code() | |
|
||||
w.set_code(code) | |
|
||||
h=w.input_code() | |
|
||||
. | d=w.get_code() | dg.wormhole_got_code(code)
|
||||
. | d=w.get_unverified_key() | dg.wormhole_got_unverified_key(key)
|
||||
. | d=w.get_verifier() | dg.wormhole_got_verifier(verifier)
|
||||
. | d=w.get_versions() | dg.wormhole_got_versions(versions)
|
||||
key=w.derive_key(purpose, length) | |
|
||||
w.send_message(msg) | |
|
||||
. | d=w.get_message() | dg.wormhole_got_message(msg)
|
||||
w.close() | | dg.wormhole_closed(result)
|
||||
. | d=w.close() |
|
||||
|
||||
|
|
|
@ -33,7 +33,6 @@ class Boss(object):
|
|||
_url = attrib(validator=instance_of(type(u"")))
|
||||
_appid = attrib(validator=instance_of(type(u"")))
|
||||
_versions = attrib(validator=instance_of(dict))
|
||||
_welcome_handler = attrib() # TODO: validator: callable
|
||||
_reactor = attrib()
|
||||
_journal = attrib(validator=provides(_interfaces.IJournal))
|
||||
_tor_manager = attrib() # TODO: ITorManager or None
|
||||
|
@ -185,11 +184,11 @@ class Boss(object):
|
|||
raise WelcomeError(welcome["error"])
|
||||
# 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
|
||||
# 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
|
||||
# middle of processing the rx_welcome input, and I wasn't sure
|
||||
# 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:
|
||||
self.rx_unwelcome(welcome_error)
|
||||
@m.input()
|
||||
|
@ -244,7 +243,7 @@ class Boss(object):
|
|||
self._their_versions = bytes_to_dict(plaintext)
|
||||
# but this part is app-to-app
|
||||
app_versions = self._their_versions.get("app_versions", {})
|
||||
self._W.got_version(app_versions)
|
||||
self._W.got_versions(app_versions)
|
||||
|
||||
@m.output()
|
||||
def S_send(self, plaintext):
|
||||
|
|
|
@ -10,7 +10,7 @@ from ..transit import TransitReceiver
|
|||
from ..errors import TransferError, WormholeClosedError, NoTorError
|
||||
from ..util import (dict_to_bytes, bytes_to_dict, bytes_to_hexstr,
|
||||
estimate_free_space)
|
||||
from .welcome import CLIWelcomeHandler
|
||||
from .welcome import handle_welcome
|
||||
|
||||
APPID = u"lothar.com/wormhole/text-or-file-xfer"
|
||||
|
||||
|
@ -68,13 +68,10 @@ class Receiver:
|
|||
# with the user handing off the wormhole code
|
||||
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,
|
||||
self._reactor,
|
||||
tor_manager=self._tor_manager,
|
||||
timing=self.args.timing,
|
||||
welcome_handler=wh.handle_welcome)
|
||||
timing=self.args.timing)
|
||||
self._w = w # so tests can wait on events too
|
||||
|
||||
# I wanted to do this instead:
|
||||
|
@ -112,6 +109,10 @@ class Receiver:
|
|||
|
||||
@inlineCallbacks
|
||||
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)
|
||||
|
||||
def on_slow_key():
|
||||
|
@ -125,7 +126,7 @@ class Receiver:
|
|||
# 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()
|
||||
yield w.get_unverified_key()
|
||||
finally:
|
||||
if not notify.called:
|
||||
notify.cancel()
|
||||
|
@ -147,7 +148,7 @@ class Receiver:
|
|||
|
||||
# It would be reasonable to give up after waiting here for too
|
||||
# long.
|
||||
verifier_bytes = yield w.when_verified()
|
||||
verifier_bytes = yield w.get_verifier()
|
||||
finally:
|
||||
if not notify.called:
|
||||
notify.cancel()
|
||||
|
@ -183,12 +184,12 @@ class Receiver:
|
|||
|
||||
def _send_data(self, data, w):
|
||||
data_bytes = dict_to_bytes(data)
|
||||
w.send(data_bytes)
|
||||
w.send_message(data_bytes)
|
||||
|
||||
@inlineCallbacks
|
||||
def _get_data(self, w):
|
||||
# this may raise WrongPasswordError
|
||||
them_bytes = yield w.when_received()
|
||||
them_bytes = yield w.get_message()
|
||||
them_d = bytes_to_dict(them_bytes)
|
||||
if "error" in them_d:
|
||||
raise TransferError(them_d["error"])
|
||||
|
@ -210,7 +211,7 @@ class Receiver:
|
|||
if not used_completion:
|
||||
print(" (note: you can use <Tab> to complete words)",
|
||||
file=self.args.stderr)
|
||||
yield w.when_code()
|
||||
yield w.get_code()
|
||||
|
||||
def _show_verifier(self, verifier_bytes):
|
||||
verifier_hex = bytes_to_hexstr(verifier_bytes)
|
||||
|
|
|
@ -10,7 +10,7 @@ from ..errors import TransferError, WormholeClosedError, NoTorError
|
|||
from wormhole import create, __version__
|
||||
from ..transit import TransitSender
|
||||
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"
|
||||
VERIFY_TIMER = 1
|
||||
|
@ -53,13 +53,10 @@ class Sender:
|
|||
# with the user handing off the wormhole code
|
||||
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,
|
||||
self._reactor,
|
||||
tor_manager=self._tor_manager,
|
||||
timing=self._timing,
|
||||
welcome_handler=wh.handle_welcome)
|
||||
timing=self._timing)
|
||||
d = self._go(w)
|
||||
|
||||
# if we succeed, we should close and return the w.close results
|
||||
|
@ -85,10 +82,14 @@ class Sender:
|
|||
|
||||
def _send_data(self, data, w):
|
||||
data_bytes = dict_to_bytes(data)
|
||||
w.send(data_bytes)
|
||||
w.send_message(data_bytes)
|
||||
|
||||
@inlineCallbacks
|
||||
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
|
||||
# wormhole exchange happen in parallel
|
||||
offer, self._fd_to_send = self._build_offer()
|
||||
|
@ -110,21 +111,21 @@ class Sender:
|
|||
else:
|
||||
w.allocate_code(args.code_length)
|
||||
|
||||
code = yield w.when_code()
|
||||
code = yield w.get_code()
|
||||
if not args.zeromode:
|
||||
print(u"Wormhole code is: %s" % code, file=args.stderr)
|
||||
# flush stderr so the code is displayed immediately
|
||||
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()
|
||||
# We don't print a "waiting" message for get_unverified_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
|
||||
# get_unverified_key() until the receiver has typed in the code and
|
||||
# their PAKE message makes it to us.
|
||||
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():
|
||||
print(u"Key established, waiting for confirmation...",
|
||||
file=args.stderr)
|
||||
|
@ -132,13 +133,13 @@ class Sender:
|
|||
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
|
||||
# w.get_verifier() 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
|
||||
verifier_bytes = yield w.get_verifier() # might WrongPasswordError
|
||||
finally:
|
||||
if not notify.called:
|
||||
notify.cancel()
|
||||
|
@ -162,7 +163,7 @@ class Sender:
|
|||
}
|
||||
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",
|
||||
ts.TRANSIT_KEY_LENGTH)
|
||||
ts.set_transit_key(transit_key)
|
||||
|
@ -174,13 +175,13 @@ class Sender:
|
|||
|
||||
while True:
|
||||
try:
|
||||
them_d_bytes = yield w.when_received()
|
||||
them_d_bytes = yield w.get_message()
|
||||
except WormholeClosedError:
|
||||
if done:
|
||||
returnValue(None)
|
||||
raise TransferError("unexpected close")
|
||||
# TODO: when_received() fired, so now it's safe to use
|
||||
# w.derive_key()
|
||||
# TODO: get_message() fired, so get_verifier must have fired, so
|
||||
# now it's safe to use w.derive_key()
|
||||
them_d = bytes_to_dict(them_d_bytes)
|
||||
#print("GOT", them_d)
|
||||
recognized = False
|
||||
|
@ -209,7 +210,7 @@ class Sender:
|
|||
if ok.lower() == "no":
|
||||
err = "sender rejected verification check, abandoned transfer"
|
||||
reject_data = dict_to_bytes({"error": err})
|
||||
w.send(reject_data)
|
||||
w.send_message(reject_data)
|
||||
raise TransferError(err)
|
||||
|
||||
def _handle_transit(self, receiver_transit):
|
||||
|
|
|
@ -1,24 +1,18 @@
|
|||
from __future__ import print_function, absolute_import, unicode_literals
|
||||
import sys
|
||||
from ..wormhole import _WelcomeHandler
|
||||
|
||||
class CLIWelcomeHandler(_WelcomeHandler):
|
||||
def __init__(self, url, cli_version, stderr=sys.stderr):
|
||||
_WelcomeHandler.__init__(self, url, stderr)
|
||||
self._current_version = cli_version
|
||||
self._version_warning_displayed = False
|
||||
|
||||
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)
|
||||
def handle_welcome(welcome, relay_url, my_version, stderr):
|
||||
if "motd" in welcome:
|
||||
motd_lines = welcome["motd"].splitlines()
|
||||
motd_formatted = "\n ".join(motd_lines)
|
||||
print("Server (at %s) says:\n %s" % (relay_url, motd_formatted),
|
||||
file=stderr)
|
||||
|
||||
# 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)
|
||||
|
|
|
@ -433,9 +433,16 @@ class PregeneratedCode(ServerBase, ScriptsBase, unittest.TestCase):
|
|||
receive_d = cmd_receive.receive(recv_cfg)
|
||||
else:
|
||||
KEY_TIMER = 0 if mode == "slow-sender-text" else 1.0
|
||||
rxw = []
|
||||
with mock.patch.object(cmd_receive, "KEY_TIMER", KEY_TIMER):
|
||||
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
|
||||
# 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):
|
||||
|
||||
def setUp(self):
|
||||
d = super(Cleanup, self).setUp()
|
||||
self.cfg = cfg = config("send")
|
||||
def make_config(self):
|
||||
cfg = config("send")
|
||||
# common options for all tests in this suite
|
||||
cfg.hide_progress = True
|
||||
cfg.relay_url = self.relayurl
|
||||
cfg.transit_helper = ""
|
||||
cfg.stdout = io.StringIO()
|
||||
cfg.stderr = io.StringIO()
|
||||
return d
|
||||
return cfg
|
||||
|
||||
@inlineCallbacks
|
||||
@mock.patch('sys.stdout')
|
||||
def test_text(self, stdout):
|
||||
# the rendezvous channel should be deleted after success
|
||||
self.cfg.text = "hello"
|
||||
self.cfg.code = "1-abc"
|
||||
cfg = self.make_config()
|
||||
cfg.text = "hello"
|
||||
cfg.code = "1-abc"
|
||||
|
||||
send_d = cmd_send.send(self.cfg)
|
||||
receive_d = cmd_receive.receive(self.cfg)
|
||||
send_d = cmd_send.send(cfg)
|
||||
receive_d = cmd_receive.receive(cfg)
|
||||
|
||||
yield send_d
|
||||
yield receive_d
|
||||
|
@ -863,12 +870,14 @@ class Cleanup(ServerBase, unittest.TestCase):
|
|||
def test_text_wrong_password(self):
|
||||
# if the password was wrong, the rendezvous channel should still be
|
||||
# deleted
|
||||
self.cfg.text = "secret message"
|
||||
self.cfg.code = "1-abc"
|
||||
send_d = cmd_send.send(self.cfg)
|
||||
send_cfg = self.make_config()
|
||||
send_cfg.text = "secret message"
|
||||
send_cfg.code = "1-abc"
|
||||
send_d = cmd_send.send(send_cfg)
|
||||
|
||||
self.cfg.code = "1-WRONG"
|
||||
receive_d = cmd_receive.receive(self.cfg)
|
||||
rx_cfg = self.make_config()
|
||||
rx_cfg.code = "1-WRONG"
|
||||
receive_d = cmd_receive.receive(rx_cfg)
|
||||
|
||||
# both sides should be capable of detecting the mismatch
|
||||
yield self.assertFailure(send_d, WrongPasswordError)
|
||||
|
@ -951,12 +960,9 @@ class AppID(ServerBase, unittest.TestCase):
|
|||
self.assertEqual(used[0]["app_id"], u"appid2")
|
||||
|
||||
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()
|
||||
h = welcome.CLIWelcomeHandler("url", my_version, stderr)
|
||||
h.handle_welcome(welcome_message)
|
||||
if twice:
|
||||
h.handle_welcome(welcome_message)
|
||||
welcome.handle_welcome(welcome_message, "url", my_version, stderr)
|
||||
return stderr.getvalue()
|
||||
|
||||
def test_empty(self):
|
||||
|
@ -973,15 +979,6 @@ class Welcome(unittest.TestCase):
|
|||
"Server claims 3.0 is current, but ours is 2.0\n")
|
||||
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):
|
||||
stderr = self.do({"current_cli_version": "3.0"},
|
||||
my_version="2.5+middle.something")
|
||||
|
|
|
@ -1152,15 +1152,15 @@ class Boss(unittest.TestCase):
|
|||
def build(self):
|
||||
events = []
|
||||
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")
|
||||
self._welcome_handler = mock.Mock()
|
||||
versions = {"app": "version1"}
|
||||
reactor = None
|
||||
journal = ImmediateJournal()
|
||||
tor_manager = None
|
||||
b = MockBoss(wormhole, "side", "url", "appid", versions,
|
||||
self._welcome_handler, reactor, journal, tor_manager,
|
||||
reactor, journal, tor_manager,
|
||||
timing.DebugTiming())
|
||||
b._T = Dummy("t", events, ITerminator, "close")
|
||||
b._S = Dummy("s", events, ISend, "send")
|
||||
|
@ -1179,8 +1179,11 @@ class Boss(unittest.TestCase):
|
|||
self.assertEqual(events, [("w.got_code", "1-code")])
|
||||
events[:] = []
|
||||
|
||||
b.rx_welcome("welcome")
|
||||
self.assertEqual(self._welcome_handler.mock_calls, [mock.call("welcome")])
|
||||
welcome = {"howdy": "how are ya"}
|
||||
b.rx_welcome(welcome)
|
||||
self.assertEqual(events, [("w.got_welcome", welcome),
|
||||
])
|
||||
events[:] = []
|
||||
|
||||
# pretend a peer message was correctly decrypted
|
||||
b.got_key(b"key")
|
||||
|
@ -1190,7 +1193,7 @@ class Boss(unittest.TestCase):
|
|||
b.got_message("0", b"msg1")
|
||||
self.assertEqual(events, [("w.got_key", b"key"),
|
||||
("w.got_verifier", b"verifier"),
|
||||
("w.got_version", {}),
|
||||
("w.got_versions", {}),
|
||||
("w.received", b"msg1"),
|
||||
])
|
||||
events[:] = []
|
||||
|
@ -1206,6 +1209,12 @@ class Boss(unittest.TestCase):
|
|||
b.closed()
|
||||
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):
|
||||
b, events = self.build()
|
||||
b.set_code("1-code")
|
||||
|
|
|
@ -12,23 +12,6 @@ from ..errors import (WrongPasswordError,
|
|||
|
||||
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:
|
||||
#
|
||||
# * normal sender: set_code, send_phase1, connected, claimed, learn_msg2,
|
||||
|
@ -42,21 +25,24 @@ class Welcome(unittest.TestCase):
|
|||
|
||||
class Delegate:
|
||||
def __init__(self):
|
||||
self.welcome = None
|
||||
self.code = None
|
||||
self.key = None
|
||||
self.verifier = None
|
||||
self.version = None
|
||||
self.versions = None
|
||||
self.messages = []
|
||||
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
|
||||
def wormhole_key(self, key):
|
||||
def wormhole_got_unverified_key(self, key):
|
||||
self.key = key
|
||||
def wormhole_verified(self, verifier):
|
||||
def wormhole_got_verifier(self, verifier):
|
||||
self.verifier = verifier
|
||||
def wormhole_version(self, version):
|
||||
self.version = version
|
||||
def wormhole_received(self, data):
|
||||
def wormhole_got_versions(self, versions):
|
||||
self.versions = versions
|
||||
def wormhole_got_message(self, data):
|
||||
self.messages.append(data)
|
||||
def wormhole_closed(self, result):
|
||||
self.closed = result
|
||||
|
@ -76,12 +62,12 @@ class Delegated(ServerBase, unittest.TestCase):
|
|||
w2.set_code(dg.code)
|
||||
yield poll_until(lambda: dg.key 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")
|
||||
got = yield w2.when_received()
|
||||
w1.send_message(b"ping")
|
||||
got = yield w2.get_message()
|
||||
self.assertEqual(got, b"ping")
|
||||
w2.send(b"pong")
|
||||
w2.send_message(b"pong")
|
||||
yield poll_until(lambda: dg.messages)
|
||||
self.assertEqual(dg.messages[0], b"pong")
|
||||
|
||||
|
@ -124,7 +110,7 @@ class Wormholes(ServerBase, unittest.TestCase):
|
|||
def test_allocate_default(self):
|
||||
w1 = wormhole.create(APPID, self.relayurl, reactor)
|
||||
w1.allocate_code()
|
||||
code = yield w1.when_code()
|
||||
code = yield w1.get_code()
|
||||
mo = re.search(r"^\d+-\w+-\w+$", code)
|
||||
self.assert_(mo, code)
|
||||
# w.close() fails because we closed before connecting
|
||||
|
@ -134,7 +120,7 @@ class Wormholes(ServerBase, unittest.TestCase):
|
|||
def test_allocate_more_words(self):
|
||||
w1 = wormhole.create(APPID, self.relayurl, reactor)
|
||||
w1.allocate_code(3)
|
||||
code = yield w1.when_code()
|
||||
code = yield w1.get_code()
|
||||
mo = re.search(r"^\d+-\w+-\w+-\w+$", code)
|
||||
self.assert_(mo, code)
|
||||
yield self.assertFailure(w1.close(), LonelyError)
|
||||
|
@ -149,11 +135,11 @@ class Wormholes(ServerBase, unittest.TestCase):
|
|||
w2 = wormhole.create(APPID, self.relayurl, reactor)
|
||||
#w2.debug_set_trace(" W2")
|
||||
w1.allocate_code()
|
||||
code = yield w1.when_code()
|
||||
code = yield w1.get_code()
|
||||
w2.set_code(code)
|
||||
|
||||
yield w1.when_key()
|
||||
yield w2.when_key()
|
||||
yield w1.get_unverified_key()
|
||||
yield w2.get_unverified_key()
|
||||
|
||||
key1 = w1.derive_key("purpose", 16)
|
||||
self.assertEqual(len(key1), 16)
|
||||
|
@ -163,29 +149,29 @@ class Wormholes(ServerBase, unittest.TestCase):
|
|||
with self.assertRaises(TypeError):
|
||||
w1.derive_key(12345, 16)
|
||||
|
||||
verifier1 = yield w1.when_verified()
|
||||
verifier2 = yield w2.when_verified()
|
||||
verifier1 = yield w1.get_verifier()
|
||||
verifier2 = yield w2.get_verifier()
|
||||
self.assertEqual(verifier1, verifier2)
|
||||
|
||||
self.successResultOf(w1.when_key())
|
||||
self.successResultOf(w2.when_key())
|
||||
self.successResultOf(w1.get_unverified_key())
|
||||
self.successResultOf(w2.get_unverified_key())
|
||||
|
||||
version1 = yield w1.when_version()
|
||||
version2 = yield w2.when_version()
|
||||
versions1 = yield w1.get_versions()
|
||||
versions2 = yield w2.get_versions()
|
||||
# app-versions are exercised properly in test_versions, this just
|
||||
# tests the defaults
|
||||
self.assertEqual(version1, {})
|
||||
self.assertEqual(version2, {})
|
||||
self.assertEqual(versions1, {})
|
||||
self.assertEqual(versions2, {})
|
||||
|
||||
w1.send(b"data1")
|
||||
w2.send(b"data2")
|
||||
dataX = yield w1.when_received()
|
||||
dataY = yield w2.when_received()
|
||||
w1.send_message(b"data1")
|
||||
w2.send_message(b"data2")
|
||||
dataX = yield w1.get_message()
|
||||
dataY = yield w2.get_message()
|
||||
self.assertEqual(dataX, b"data2")
|
||||
self.assertEqual(dataY, b"data1")
|
||||
|
||||
version1_again = yield w1.when_version()
|
||||
self.assertEqual(version1, version1_again)
|
||||
versions1_again = yield w1.get_versions()
|
||||
self.assertEqual(versions1, versions1_again)
|
||||
|
||||
c1 = yield w1.close()
|
||||
self.assertEqual(c1, "happy")
|
||||
|
@ -193,19 +179,19 @@ class Wormholes(ServerBase, unittest.TestCase):
|
|||
self.assertEqual(c2, "happy")
|
||||
|
||||
@inlineCallbacks
|
||||
def test_when_code_early(self):
|
||||
def test_get_code_early(self):
|
||||
w1 = wormhole.create(APPID, self.relayurl, reactor)
|
||||
d = w1.when_code()
|
||||
d = w1.get_code()
|
||||
w1.set_code("1-abc")
|
||||
code = self.successResultOf(d)
|
||||
self.assertEqual(code, "1-abc")
|
||||
yield self.assertFailure(w1.close(), LonelyError)
|
||||
|
||||
@inlineCallbacks
|
||||
def test_when_code_late(self):
|
||||
def test_get_code_late(self):
|
||||
w1 = wormhole.create(APPID, self.relayurl, reactor)
|
||||
w1.set_code("1-abc")
|
||||
d = w1.when_code()
|
||||
d = w1.get_code()
|
||||
code = self.successResultOf(d)
|
||||
self.assertEqual(code, "1-abc")
|
||||
yield self.assertFailure(w1.close(), LonelyError)
|
||||
|
@ -218,12 +204,12 @@ class Wormholes(ServerBase, unittest.TestCase):
|
|||
w1 = wormhole.create(APPID, self.relayurl, reactor)
|
||||
w2 = wormhole.create(APPID, self.relayurl, reactor)
|
||||
w1.allocate_code()
|
||||
code = yield w1.when_code()
|
||||
code = yield w1.get_code()
|
||||
w2.set_code(code)
|
||||
w1.send(b"data")
|
||||
w2.send(b"data")
|
||||
dataX = yield w1.when_received()
|
||||
dataY = yield w2.when_received()
|
||||
w1.send_message(b"data")
|
||||
w2.send_message(b"data")
|
||||
dataX = yield w1.get_message()
|
||||
dataY = yield w2.get_message()
|
||||
self.assertEqual(dataX, b"data")
|
||||
self.assertEqual(dataY, b"data")
|
||||
yield w1.close()
|
||||
|
@ -234,13 +220,13 @@ class Wormholes(ServerBase, unittest.TestCase):
|
|||
w1 = wormhole.create(APPID, self.relayurl, reactor)
|
||||
w2 = wormhole.create(APPID, self.relayurl, reactor)
|
||||
w1.allocate_code()
|
||||
code = yield w1.when_code()
|
||||
code = yield w1.get_code()
|
||||
w2.set_code(code)
|
||||
w1.send(b"data1")
|
||||
dataY = yield w2.when_received()
|
||||
w1.send_message(b"data1")
|
||||
dataY = yield w2.get_message()
|
||||
self.assertEqual(dataY, b"data1")
|
||||
d = w1.when_received()
|
||||
w2.send(b"data2")
|
||||
d = w1.get_message()
|
||||
w2.send_message(b"data2")
|
||||
dataX = yield d
|
||||
self.assertEqual(dataX, b"data2")
|
||||
yield w1.close()
|
||||
|
@ -251,10 +237,10 @@ class Wormholes(ServerBase, unittest.TestCase):
|
|||
w1 = wormhole.create(APPID, self.relayurl, reactor)
|
||||
w2 = wormhole.create(APPID, self.relayurl, reactor)
|
||||
w1.allocate_code()
|
||||
code = yield w1.when_code()
|
||||
code = yield w1.get_code()
|
||||
w2.set_code(code)
|
||||
w1.send(b"data1")
|
||||
dataY = yield w2.when_received()
|
||||
w1.send_message(b"data1")
|
||||
dataY = yield w2.get_message()
|
||||
self.assertEqual(dataY, b"data1")
|
||||
yield w1.close()
|
||||
yield w2.close()
|
||||
|
@ -262,9 +248,9 @@ class Wormholes(ServerBase, unittest.TestCase):
|
|||
@inlineCallbacks
|
||||
def test_early(self):
|
||||
w1 = wormhole.create(APPID, self.relayurl, reactor)
|
||||
w1.send(b"data1")
|
||||
w1.send_message(b"data1")
|
||||
w2 = wormhole.create(APPID, self.relayurl, reactor)
|
||||
d = w2.when_received()
|
||||
d = w2.get_message()
|
||||
w1.set_code("123-abc-def")
|
||||
w2.set_code("123-abc-def")
|
||||
dataY = yield d
|
||||
|
@ -278,8 +264,8 @@ class Wormholes(ServerBase, unittest.TestCase):
|
|||
w2 = wormhole.create(APPID, self.relayurl, reactor)
|
||||
w1.set_code("123-purple-elephant")
|
||||
w2.set_code("123-purple-elephant")
|
||||
w1.send(b"data1"), w2.send(b"data2")
|
||||
dl = yield self.doBoth(w1.when_received(), w2.when_received())
|
||||
w1.send_message(b"data1"), w2.send_message(b"data2")
|
||||
dl = yield self.doBoth(w1.get_message(), w2.get_message())
|
||||
(dataX, dataY) = dl
|
||||
self.assertEqual(dataX, b"data2")
|
||||
self.assertEqual(dataY, b"data1")
|
||||
|
@ -300,8 +286,8 @@ class Wormholes(ServerBase, unittest.TestCase):
|
|||
yield poll_until(lambda: w2._boss._K._debug_pake_stashed)
|
||||
h.choose_words("purple-elephant")
|
||||
|
||||
w1.send(b"data1"), w2.send(b"data2")
|
||||
dl = yield self.doBoth(w1.when_received(), w2.when_received())
|
||||
w1.send_message(b"data1"), w2.send_message(b"data2")
|
||||
dl = yield self.doBoth(w1.get_message(), w2.get_message())
|
||||
(dataX, dataY) = dl
|
||||
self.assertEqual(dataX, b"data2")
|
||||
self.assertEqual(dataY, b"data1")
|
||||
|
@ -315,13 +301,13 @@ class Wormholes(ServerBase, unittest.TestCase):
|
|||
w2 = wormhole.create(APPID, self.relayurl, reactor)
|
||||
w1.set_code("123-purple-elephant")
|
||||
w2.set_code("123-purple-elephant")
|
||||
w1.send(b"data1"), w2.send(b"data2")
|
||||
w1.send(b"data3"), w2.send(b"data4")
|
||||
dl = yield self.doBoth(w1.when_received(), w2.when_received())
|
||||
w1.send_message(b"data1"), w2.send_message(b"data2")
|
||||
w1.send_message(b"data3"), w2.send_message(b"data4")
|
||||
dl = yield self.doBoth(w1.get_message(), w2.get_message())
|
||||
(dataX, dataY) = dl
|
||||
self.assertEqual(dataX, b"data2")
|
||||
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
|
||||
self.assertEqual(dataX, b"data4")
|
||||
self.assertEqual(dataY, b"data3")
|
||||
|
@ -337,19 +323,19 @@ class Wormholes(ServerBase, unittest.TestCase):
|
|||
w2.set_code("123-foo")
|
||||
|
||||
# let it connect and become HAPPY
|
||||
yield w1.when_version()
|
||||
yield w2.when_version()
|
||||
yield w1.get_versions()
|
||||
yield w2.get_versions()
|
||||
|
||||
yield w1.close()
|
||||
yield w2.close()
|
||||
|
||||
# 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.failureResultOf(w1.when_key(), WormholeClosed)
|
||||
self.failureResultOf(w1.when_verified(), WormholeClosed)
|
||||
self.failureResultOf(w1.when_version(), WormholeClosed)
|
||||
self.failureResultOf(w1.when_received(), WormholeClosed)
|
||||
self.failureResultOf(w1.get_unverified_key(), WormholeClosed)
|
||||
self.failureResultOf(w1.get_verifier(), WormholeClosed)
|
||||
self.failureResultOf(w1.get_versions(), WormholeClosed)
|
||||
self.failureResultOf(w1.get_message(), WormholeClosed)
|
||||
|
||||
|
||||
@inlineCallbacks
|
||||
|
@ -357,20 +343,20 @@ class Wormholes(ServerBase, unittest.TestCase):
|
|||
w1 = wormhole.create(APPID, self.relayurl, reactor)
|
||||
w2 = wormhole.create(APPID, self.relayurl, reactor)
|
||||
w1.allocate_code()
|
||||
code = yield w1.when_code()
|
||||
code = yield w1.get_code()
|
||||
w2.set_code(code+"not")
|
||||
code2 = yield w2.when_code()
|
||||
code2 = yield w2.get_code()
|
||||
self.assertNotEqual(code, code2)
|
||||
# That's enough to allow both sides to discover the mismatch, but
|
||||
# only after the confirmation message gets through. API calls that
|
||||
# don't wait will appear to work until the mismatched confirmation
|
||||
# message arrives.
|
||||
w1.send(b"should still work")
|
||||
w2.send(b"should still work")
|
||||
w1.send_message(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
|
||||
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
|
||||
# then will receive w2.VERSION. When it sees w2.VERSION, it will
|
||||
# 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
|
||||
# Deferreds early to exercise the wait-then-fail path
|
||||
d1_verified = w1.when_verified()
|
||||
d1_version = w1.when_version()
|
||||
d1_received = w1.when_received()
|
||||
d2_verified = w2.when_verified()
|
||||
d2_version = w2.when_version()
|
||||
d2_received = w2.when_received()
|
||||
d1_verified = w1.get_verifier()
|
||||
d1_versions = w1.get_versions()
|
||||
d1_received = w1.get_message()
|
||||
d2_verified = w2.get_verifier()
|
||||
d2_versions = w2.get_versions()
|
||||
d2_received = w2.get_message()
|
||||
|
||||
# wait for each side to notice the failure
|
||||
yield self.assertFailure(w1.when_verified(), WrongPasswordError)
|
||||
yield self.assertFailure(w2.when_verified(), WrongPasswordError)
|
||||
yield self.assertFailure(w1.get_verifier(), WrongPasswordError)
|
||||
yield self.assertFailure(w2.get_verifier(), WrongPasswordError)
|
||||
# and then wait for the rest of the loops to fire. if we had+used
|
||||
# eventual-send, this wouldn't be a problem
|
||||
yield pause_one_tick()
|
||||
|
||||
# now all the rest should have fired already
|
||||
self.failureResultOf(d1_verified, WrongPasswordError)
|
||||
self.failureResultOf(d1_version, WrongPasswordError)
|
||||
self.failureResultOf(d1_versions, WrongPasswordError)
|
||||
self.failureResultOf(d1_received, WrongPasswordError)
|
||||
self.failureResultOf(d2_verified, WrongPasswordError)
|
||||
self.failureResultOf(d2_version, WrongPasswordError)
|
||||
self.failureResultOf(d2_versions, WrongPasswordError)
|
||||
self.failureResultOf(d2_received, WrongPasswordError)
|
||||
|
||||
# and at this point, with the failure safely noticed by both sides,
|
||||
# new when_key() calls should signal the failure, even before we
|
||||
# close
|
||||
# new get_unverified_key() calls should signal the failure, even
|
||||
# before we close
|
||||
|
||||
# any new calls in the error state should immediately fail
|
||||
self.failureResultOf(w1.when_key(), WrongPasswordError)
|
||||
self.failureResultOf(w1.when_verified(), WrongPasswordError)
|
||||
self.failureResultOf(w1.when_version(), WrongPasswordError)
|
||||
self.failureResultOf(w1.when_received(), WrongPasswordError)
|
||||
self.failureResultOf(w2.when_key(), WrongPasswordError)
|
||||
self.failureResultOf(w2.when_verified(), WrongPasswordError)
|
||||
self.failureResultOf(w2.when_version(), WrongPasswordError)
|
||||
self.failureResultOf(w2.when_received(), WrongPasswordError)
|
||||
self.failureResultOf(w1.get_unverified_key(), WrongPasswordError)
|
||||
self.failureResultOf(w1.get_verifier(), WrongPasswordError)
|
||||
self.failureResultOf(w1.get_versions(), WrongPasswordError)
|
||||
self.failureResultOf(w1.get_message(), WrongPasswordError)
|
||||
self.failureResultOf(w2.get_unverified_key(), WrongPasswordError)
|
||||
self.failureResultOf(w2.get_verifier(), WrongPasswordError)
|
||||
self.failureResultOf(w2.get_versions(), WrongPasswordError)
|
||||
self.failureResultOf(w2.get_message(), WrongPasswordError)
|
||||
|
||||
yield self.assertFailure(w1.close(), WrongPasswordError)
|
||||
yield self.assertFailure(w2.close(), WrongPasswordError)
|
||||
|
||||
# API calls should still get the error, not WormholeClosed
|
||||
self.failureResultOf(w1.when_key(), WrongPasswordError)
|
||||
self.failureResultOf(w1.when_verified(), WrongPasswordError)
|
||||
self.failureResultOf(w1.when_version(), WrongPasswordError)
|
||||
self.failureResultOf(w1.when_received(), WrongPasswordError)
|
||||
self.failureResultOf(w2.when_key(), WrongPasswordError)
|
||||
self.failureResultOf(w2.when_verified(), WrongPasswordError)
|
||||
self.failureResultOf(w2.when_version(), WrongPasswordError)
|
||||
self.failureResultOf(w2.when_received(), WrongPasswordError)
|
||||
self.failureResultOf(w1.get_unverified_key(), WrongPasswordError)
|
||||
self.failureResultOf(w1.get_verifier(), WrongPasswordError)
|
||||
self.failureResultOf(w1.get_versions(), WrongPasswordError)
|
||||
self.failureResultOf(w1.get_message(), WrongPasswordError)
|
||||
self.failureResultOf(w2.get_unverified_key(), WrongPasswordError)
|
||||
self.failureResultOf(w2.get_verifier(), WrongPasswordError)
|
||||
self.failureResultOf(w2.get_versions(), WrongPasswordError)
|
||||
self.failureResultOf(w2.get_message(), WrongPasswordError)
|
||||
|
||||
@inlineCallbacks
|
||||
def test_wrong_password_with_spaces(self):
|
||||
|
@ -442,21 +428,21 @@ class Wormholes(ServerBase, unittest.TestCase):
|
|||
w1 = wormhole.create(APPID, self.relayurl, reactor)
|
||||
w2 = wormhole.create(APPID, self.relayurl, reactor)
|
||||
w1.allocate_code()
|
||||
code = yield w1.when_code()
|
||||
code = yield w1.get_code()
|
||||
w2.set_code(code)
|
||||
v1 = yield w1.when_verified() # early
|
||||
v2 = yield w2.when_verified()
|
||||
v1 = yield w1.get_verifier() # early
|
||||
v2 = yield w2.get_verifier()
|
||||
self.failUnlessEqual(type(v1), type(b""))
|
||||
self.failUnlessEqual(v1, v2)
|
||||
w1.send(b"data1")
|
||||
w2.send(b"data2")
|
||||
dataX = yield w1.when_received()
|
||||
dataY = yield w2.when_received()
|
||||
w1.send_message(b"data1")
|
||||
w2.send_message(b"data2")
|
||||
dataX = yield w1.get_message()
|
||||
dataY = yield w2.get_message()
|
||||
self.assertEqual(dataX, b"data2")
|
||||
self.assertEqual(dataY, b"data1")
|
||||
|
||||
# calling when_verified() this late should fire right away
|
||||
v1_late = self.successResultOf(w2.when_verified())
|
||||
# calling get_verifier() this late should fire right away
|
||||
v1_late = self.successResultOf(w2.get_verifier())
|
||||
self.assertEqual(v1_late, v1)
|
||||
|
||||
yield w1.close()
|
||||
|
@ -470,11 +456,11 @@ class Wormholes(ServerBase, unittest.TestCase):
|
|||
w2 = wormhole.create(APPID, self.relayurl, reactor,
|
||||
versions={"w2": 456})
|
||||
w1.allocate_code()
|
||||
code = yield w1.when_code()
|
||||
code = yield w1.get_code()
|
||||
w2.set_code(code)
|
||||
w1_versions = yield w2.when_version()
|
||||
w1_versions = yield w2.get_versions()
|
||||
self.assertEqual(w1_versions, {"w1": 123})
|
||||
w2_versions = yield w1.when_version()
|
||||
w2_versions = yield w1.get_versions()
|
||||
self.assertEqual(w2_versions, {"w2": 456})
|
||||
yield w1.close()
|
||||
yield w2.close()
|
||||
|
@ -496,8 +482,8 @@ class Wormholes(ServerBase, unittest.TestCase):
|
|||
w2 = wormhole.create(APPID, self.relayurl, reactor)
|
||||
w1.set_code("123-purple-elephant")
|
||||
w2.set_code("123-purple-elephant")
|
||||
w1.send(b"data1"), w2.send(b"data2")
|
||||
dl = yield self.doBoth(w1.when_received(), w2.when_received())
|
||||
w1.send_message(b"data1"), w2.send_message(b"data2")
|
||||
dl = yield self.doBoth(w1.get_message(), w2.get_message())
|
||||
(dataX, dataY) = dl
|
||||
self.assertEqual(dataX, b"data2")
|
||||
self.assertEqual(dataY, b"data1")
|
||||
|
@ -537,7 +523,7 @@ class Errors(ServerBase, unittest.TestCase):
|
|||
def test_allocate_and_set_code(self):
|
||||
w = wormhole.create(APPID, self.relayurl, reactor)
|
||||
w.allocate_code()
|
||||
yield w.when_code()
|
||||
yield w.get_code()
|
||||
with self.assertRaises(OnlyOneCodeError):
|
||||
w.set_code("123-nope")
|
||||
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.debug_set_trace("W1")
|
||||
w1.allocate_code()
|
||||
code = yield w1.when_code()
|
||||
w1.send(b"data1") # will be queued until wormhole is established
|
||||
code = yield w1.get_code()
|
||||
w1.send_message(b"data1") # queued until wormhole is established
|
||||
|
||||
# now wait until we've deposited all our messages on the server
|
||||
def seen_our_pake():
|
||||
|
@ -577,11 +563,11 @@ class Reconnection(ServerBase, unittest.TestCase):
|
|||
#w2.debug_set_trace(" W2")
|
||||
w2.set_code(code)
|
||||
|
||||
dataY = yield w2.when_received()
|
||||
dataY = yield w2.get_message()
|
||||
self.assertEqual(dataY, b"data1")
|
||||
|
||||
w2.send(b"data2")
|
||||
dataX = yield w1.when_received()
|
||||
w2.send_message(b"data2")
|
||||
dataX = yield w1.get_message()
|
||||
self.assertEqual(dataX, b"data2")
|
||||
|
||||
c1 = yield w1.close()
|
||||
|
|
|
@ -15,18 +15,18 @@ from .util import to_bytes
|
|||
|
||||
# We can provide different APIs to different apps:
|
||||
# * Deferreds
|
||||
# w.when_code().addCallback(print_code)
|
||||
# w.send(data)
|
||||
# w.when_received().addCallback(got_data)
|
||||
# w.get_code().addCallback(print_code)
|
||||
# w.send_message(data)
|
||||
# w.get_message().addCallback(got_data)
|
||||
# w.close().addCallback(closed)
|
||||
|
||||
# * delegate callbacks (better for journaled environments)
|
||||
# w = wormhole(delegate=app)
|
||||
# w.send(data)
|
||||
# w.send_message(data)
|
||||
# app.wormhole_got_code(code)
|
||||
# app.wormhole_got_verifier(verifier)
|
||||
# app.wormhole_got_version(versions)
|
||||
# app.wormhole_receive(data)
|
||||
# app.wormhole_got_versions(versions)
|
||||
# app.wormhole_got_message(data)
|
||||
# w.close()
|
||||
# app.wormhole_closed()
|
||||
#
|
||||
|
@ -34,18 +34,6 @@ from .util import to_bytes
|
|||
# wormhole(delegate=app, delegate_prefix="wormhole_",
|
||||
# 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
|
||||
@implementer(IWormhole)
|
||||
class _DelegatedWormhole(object):
|
||||
|
@ -72,7 +60,7 @@ class _DelegatedWormhole(object):
|
|||
## }
|
||||
## return s
|
||||
|
||||
def send(self, plaintext):
|
||||
def send_message(self, plaintext):
|
||||
self._boss.send(plaintext)
|
||||
|
||||
def derive_key(self, purpose, length):
|
||||
|
@ -94,23 +82,27 @@ class _DelegatedWormhole(object):
|
|||
self._boss._set_trace(client_name, which, file)
|
||||
|
||||
# from below
|
||||
def got_welcome(self, welcome):
|
||||
self._delegate.wormhole_got_welcome(welcome)
|
||||
def got_code(self, code):
|
||||
self._delegate.wormhole_code(code)
|
||||
self._delegate.wormhole_got_code(code)
|
||||
def got_key(self, key):
|
||||
self._delegate.wormhole_key(key)
|
||||
self._delegate.wormhole_got_unverified_key(key)
|
||||
self._key = key # for derive_key()
|
||||
def got_verifier(self, verifier):
|
||||
self._delegate.wormhole_verified(verifier)
|
||||
def got_version(self, versions):
|
||||
self._delegate.wormhole_version(versions)
|
||||
self._delegate.wormhole_got_verifier(verifier)
|
||||
def got_versions(self, versions):
|
||||
self._delegate.wormhole_got_versions(versions)
|
||||
def received(self, plaintext):
|
||||
self._delegate.wormhole_received(plaintext)
|
||||
self._delegate.wormhole_got_message(plaintext)
|
||||
def closed(self, result):
|
||||
self._delegate.wormhole_closed(result)
|
||||
|
||||
@implementer(IWormhole)
|
||||
class _DeferredWormhole(object):
|
||||
def __init__(self):
|
||||
self._welcome = None
|
||||
self._welcome_observers = []
|
||||
self._code = None
|
||||
self._code_observers = []
|
||||
self._key = None
|
||||
|
@ -129,7 +121,7 @@ class _DeferredWormhole(object):
|
|||
self._boss = boss
|
||||
|
||||
# from above
|
||||
def when_code(self):
|
||||
def get_code(self):
|
||||
# TODO: consider throwing error unless one of allocate/set/input_code
|
||||
# was called first. It's legit to grab the Deferred before triggering
|
||||
# the process that will cause it to fire, but forbidding that
|
||||
|
@ -143,7 +135,16 @@ class _DeferredWormhole(object):
|
|||
self._code_observers.append(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:
|
||||
return defer.fail(self._observer_result)
|
||||
if self._key is not None:
|
||||
|
@ -152,7 +153,7 @@ class _DeferredWormhole(object):
|
|||
self._key_observers.append(d)
|
||||
return d
|
||||
|
||||
def when_verified(self):
|
||||
def get_verifier(self):
|
||||
if self._observer_result is not None:
|
||||
return defer.fail(self._observer_result)
|
||||
if self._verifier is not None:
|
||||
|
@ -161,7 +162,7 @@ class _DeferredWormhole(object):
|
|||
self._verifier_observers.append(d)
|
||||
return d
|
||||
|
||||
def when_version(self):
|
||||
def get_versions(self):
|
||||
if self._observer_result is not None:
|
||||
return defer.fail(self._observer_result)
|
||||
if self._versions is not None:
|
||||
|
@ -170,7 +171,7 @@ class _DeferredWormhole(object):
|
|||
self._version_observers.append(d)
|
||||
return d
|
||||
|
||||
def when_received(self):
|
||||
def get_message(self):
|
||||
if self._observer_result is not None:
|
||||
return defer.fail(self._observer_result)
|
||||
if self._received_data:
|
||||
|
@ -187,7 +188,8 @@ class _DeferredWormhole(object):
|
|||
self._boss.set_code(code)
|
||||
|
||||
# no .serialize in Deferred-mode
|
||||
def send(self, plaintext):
|
||||
|
||||
def send_message(self, plaintext):
|
||||
self._boss.send(plaintext)
|
||||
|
||||
def derive_key(self, purpose, length):
|
||||
|
@ -217,6 +219,11 @@ class _DeferredWormhole(object):
|
|||
self._boss._set_trace(client_name, which, file)
|
||||
|
||||
# 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):
|
||||
self._code = code
|
||||
for d in self._code_observers:
|
||||
|
@ -227,12 +234,13 @@ class _DeferredWormhole(object):
|
|||
for d in self._key_observers:
|
||||
d.callback(key)
|
||||
self._key_observers[:] = []
|
||||
|
||||
def got_verifier(self, verifier):
|
||||
self._verifier = verifier
|
||||
for d in self._verifier_observers:
|
||||
d.callback(verifier)
|
||||
self._verifier_observers[:] = []
|
||||
def got_version(self, versions):
|
||||
def got_versions(self, versions):
|
||||
self._versions = versions
|
||||
for d in self._version_observers:
|
||||
d.callback(versions)
|
||||
|
@ -245,7 +253,7 @@ class _DeferredWormhole(object):
|
|||
self._received_data.append(plaintext)
|
||||
|
||||
def closed(self, result):
|
||||
#print("closed", result, type(result))
|
||||
#print("closed", result, type(result), file=sys.stderr)
|
||||
if isinstance(result, Exception):
|
||||
self._observer_result = self._closed_result = failure.Failure(result)
|
||||
else:
|
||||
|
@ -253,6 +261,8 @@ class _DeferredWormhole(object):
|
|||
self._observer_result = WormholeClosed(result)
|
||||
# but w.close() only gets error if we're unhappy
|
||||
self._closed_result = result
|
||||
for d in self._welcome_observers:
|
||||
d.errback(self._observer_result)
|
||||
for d in self._code_observers:
|
||||
d.errback(self._observer_result)
|
||||
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
|
||||
versions={},
|
||||
delegate=None, journal=None, tor_manager=None,
|
||||
timing=None, welcome_handler=None,
|
||||
timing=None,
|
||||
stderr=sys.stderr):
|
||||
timing = timing or DebugTiming()
|
||||
side = bytes_to_hexstr(os.urandom(5))
|
||||
journal = journal or ImmediateJournal()
|
||||
if not welcome_handler:
|
||||
welcome_handler = _WelcomeHandler(relay_url).handle_welcome
|
||||
if delegate:
|
||||
w = _DelegatedWormhole(delegate)
|
||||
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["app_versions"] = versions # app-specific capabilities
|
||||
b = Boss(w, side, relay_url, appid, wormhole_versions,
|
||||
welcome_handler, reactor, journal,
|
||||
tor_manager, timing)
|
||||
reactor, journal, tor_manager, timing)
|
||||
w._set_boss(b)
|
||||
b.start()
|
||||
return w
|
||||
|
|
|
@ -41,14 +41,14 @@ def receive(reactor, appid, relay_url, code,
|
|||
wh = wormhole.create(appid, relay_url, reactor, tor_manager=tm)
|
||||
if code is None:
|
||||
wh.allocate_code()
|
||||
code = yield wh.when_code()
|
||||
code = yield wh.get_code()
|
||||
else:
|
||||
wh.set_code(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?
|
||||
if on_code:
|
||||
on_code(code)
|
||||
data = yield wh.when_received()
|
||||
data = yield wh.get_message()
|
||||
data = json.loads(data.decode("utf-8"))
|
||||
offer = data.get('offer', None)
|
||||
if not offer:
|
||||
|
@ -58,7 +58,8 @@ def receive(reactor, appid, relay_url, code,
|
|||
msg = None
|
||||
if 'message' in offer:
|
||||
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:
|
||||
raise Exception(
|
||||
|
@ -104,20 +105,20 @@ def send(reactor, appid, relay_url, data, code,
|
|||
wh = wormhole.create(appid, relay_url, reactor, tor_manager=tm)
|
||||
if code is None:
|
||||
wh.allocate_code()
|
||||
code = yield wh.when_code()
|
||||
code = yield wh.get_code()
|
||||
else:
|
||||
wh.set_code(code)
|
||||
if on_code:
|
||||
on_code(code)
|
||||
|
||||
wh.send(
|
||||
wh.send_message(
|
||||
json.dumps({
|
||||
"offer": {
|
||||
"message": data
|
||||
}
|
||||
}).encode("utf-8")
|
||||
)
|
||||
data = yield wh.when_received()
|
||||
data = yield wh.get_message()
|
||||
data = json.loads(data.decode("utf-8"))
|
||||
answer = data.get('answer', None)
|
||||
yield wh.close()
|
||||
|
|
Loading…
Reference in New Issue
Block a user