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
212
docs/api.md
212
docs/api.md
|
@ -30,10 +30,10 @@ from twisted.internet.defer import inlineCallbacks
|
||||||
def go():
|
def go():
|
||||||
w = wormhole.create(appid, relay_url, reactor)
|
w = wormhole.create(appid, relay_url, reactor)
|
||||||
w.generate_code()
|
w.generate_code()
|
||||||
code = yield w.when_code()
|
code = yield w.get_code()
|
||||||
print "code:", code
|
print "code:", code
|
||||||
w.send(b"outbound data")
|
w.send_message(b"outbound data")
|
||||||
inbound = yield w.when_received()
|
inbound = yield w.get_message()
|
||||||
yield w.close()
|
yield w.close()
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -55,8 +55,8 @@ Delegated mode:
|
||||||
class MyDelegate:
|
class MyDelegate:
|
||||||
def wormhole_got_code(self, code):
|
def wormhole_got_code(self, code):
|
||||||
print("code: %s" % code)
|
print("code: %s" % code)
|
||||||
def wormhole_received(self, data): # called for each message
|
def wormhole_got_message(self, msg): # called for each message
|
||||||
print("got data, %d bytes" % len(data))
|
print("got data, %d bytes" % len(msg))
|
||||||
|
|
||||||
w = wormhole.create(appid, relay_url, reactor, delegate=MyDelegate())
|
w = wormhole.create(appid, relay_url, reactor, delegate=MyDelegate())
|
||||||
w.generate_code()
|
w.generate_code()
|
||||||
|
@ -69,10 +69,10 @@ w = wormhole.create(appid, relay_url, reactor)
|
||||||
w.generate_code()
|
w.generate_code()
|
||||||
def print_code(code):
|
def print_code(code):
|
||||||
print("code: %s" % code)
|
print("code: %s" % code)
|
||||||
w.when_code().addCallback(print_code)
|
w.get_code().addCallback(print_code)
|
||||||
def received(data):
|
def received(msg):
|
||||||
print("got data, %d bytes" % len(data))
|
print("got data, %d bytes" % len(msg))
|
||||||
w.when_received().addCallback(received) # gets exactly one message
|
w.get_message().addCallback(received) # gets exactly one message
|
||||||
```
|
```
|
||||||
|
|
||||||
## Application Identifier
|
## 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`
|
far. A convenience wrapper is provided to attach this to the `rlcompleter`
|
||||||
function of libreadline.
|
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
|
`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
|
other way to learn what code was created, but it may be useful in other modes
|
||||||
for consistency.
|
for consistency.
|
||||||
|
|
||||||
|
@ -224,7 +224,7 @@ The code-entry Helper object has the following API:
|
||||||
before display.
|
before display.
|
||||||
* `h.choose_words(words)`: call this when the user is finished typing in the
|
* `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
|
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",
|
wormhole connection process. This accepts a string like "purple-sausages",
|
||||||
without the nameplate. It must be called after `h.choose_nameplate()` or
|
without the nameplate. It must be called after `h.choose_nameplate()` or
|
||||||
`MustChooseNameplateFirstError` will be raised. May only be called once,
|
`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
|
from wormhole import create, input_with_completion
|
||||||
w = create(appid, relay_url, reactor)
|
w = create(appid, relay_url, reactor)
|
||||||
input_with_completion("Wormhole code:", w.input_code(), 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
|
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
|
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".
|
"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
|
## Welcome Messages
|
||||||
|
|
||||||
The first message sent by the rendezvous server is a "welcome" message (a
|
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,
|
dictionary). This is sent as soon as the client connects to the server,
|
||||||
they should process the keys it contains.
|
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:
|
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)
|
should deliver errors in a WELCOME message if at all possible)
|
||||||
|
|
||||||
The `error` field is handled internally by the Wormhole object. The other
|
The `error` field is handled internally by the Wormhole object. The other
|
||||||
fields are processed by an application-supplied "welcome handler" function,
|
fields can be processed by application, by using `d=w.get_welcome()`. The
|
||||||
supplied as an argument to the `wormhole()` constructor. This function will
|
Deferred will fire with the full welcome dictionary, so any other keys that a
|
||||||
be called with the full welcome dictionary, so any other keys that a future
|
future server might send will be available to it.
|
||||||
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.
|
|
||||||
|
|
||||||
The default welcome handler will print `motd` to stderr, and will ignore
|
Applications which need to display `motd` or an upgrade message, and wish to
|
||||||
`current_cli_version`.
|
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
|
## 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
|
Deferred objects from the wormhole, and event dispatch is performed by firing
|
||||||
those Deferreds.
|
those Deferreds.
|
||||||
|
|
||||||
* got_code (`yield w.when_code()` / `dg.wormhole_code(code)`): fired when the
|
Most applications will only use `code`, `received`, and `close`.
|
||||||
wormhole code is established, either after `w.generate_code()` finishes the
|
|
||||||
generation process, or when the Input Helper returned by `w.input_code()`
|
* code (`code = yield w.get_code()` / `dg.wormhole_got_code(code)`): fired
|
||||||
has been told `h.set_words()`, or immediately after `w.set_code(code)` is
|
when the wormhole code is established, either after `w.generate_code()`
|
||||||
called. This is most useful after calling `w.generate_code()`, to show the
|
finishes the generation process, or when the Input Helper returned by
|
||||||
generated code to the user so they can transcribe it to their peer.
|
`w.input_code()` has been told `h.set_words()`, or immediately after
|
||||||
* key (`yield w.when_key()` / `dg.wormhole_key()`): fired when the
|
`w.set_code(code)` is called. This is most useful after calling
|
||||||
key-exchange process has completed and a purported shared key is
|
`w.generate_code()`, to show the generated code to the user so they can
|
||||||
established. At this point we do not know that anyone else actually shares
|
transcribe it to their peer.
|
||||||
this key: the peer may have used the wrong code, or may have disappeared
|
* key (`yield w.get_unverified_key()` /
|
||||||
altogether. To wait for proof that the key is shared, wait for
|
`dg.wormhole_got_unverified_key(key)`): fired (with the raw master SPAKE2
|
||||||
`when_verified` instead. This event is really only useful for detecting
|
key) when the key-exchange process has completed and a purported shared key
|
||||||
that the initiating peer has disconnected after leaving the initial PAKE
|
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.
|
message, to display a pacifying message to the user.
|
||||||
* verified (`verifier = yield w.when_verified()` /
|
* verifier (`verifier = yield w.get_verifier()` /
|
||||||
`dg.wormhole_verified(verifier)`: fired when the key-exchange process has
|
`dg.wormhole_got_verifier(verifier)`: fired when the key-exchange process
|
||||||
completed and a valid VERSION message has arrived. The "verifier" is a byte
|
has completed and a valid VERSION message has arrived. The "verifier" is a
|
||||||
string with a hash of the shared session key; clients can compare them
|
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
|
(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
|
that *someone* has used the correct wormhole code; if someone used the
|
||||||
wrong code, the VERSION message cannot be decrypted, and the wormhole will
|
wrong code, the VERSION message cannot be decrypted, and the wormhole will
|
||||||
be closed instead.
|
be closed instead.
|
||||||
* version (`yield w.when_version()` / `dg.wormhole_version(versions)`: fired
|
* versions (`versions = yield w.get_versions()` /
|
||||||
when the VERSION message arrives from the peer. This fires at the same time
|
`dg.wormhole_got_versions(versions)`: fired when the VERSION message
|
||||||
as `verified`, but delivers the "app_versions" data (as passed into
|
arrives from the peer. This fires just after `verified`, but delivers the
|
||||||
`wormhole.create(versions=)`) instead of the verifier string.
|
"app_versions" data (as passed into `wormhole.create(versions=)`) instead
|
||||||
* received (`yield w.when_received()` / `dg.wormhole_received(data)`: fired
|
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
|
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
|
* closed (`yield w.close()` / `dg.wormhole_closed(result)`: fired when
|
||||||
`w.close()` has finished shutting down the wormhole, which means all
|
`w.close()` has finished shutting down the wormhole, which means all
|
||||||
nameplates and mailboxes have been deallocated, and the WebSocket
|
nameplates and mailboxes have been deallocated, and the WebSocket
|
||||||
|
@ -419,27 +436,28 @@ those Deferreds.
|
||||||
## Sending Data
|
## Sending Data
|
||||||
|
|
||||||
The main purpose of a Wormhole is to send data. At any point after
|
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
|
construction, callers can invoke `w.send_message(msg)`. This will queue the
|
||||||
if necessary, but (if all goes well) will eventually result in the peer
|
message if necessary, but (if all goes well) will eventually result in the
|
||||||
getting a `received` event and the data being delivered to the application.
|
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
|
Since Wormhole provides an ordered record pipe, each call to `w.send_message`
|
||||||
result in exactly one `received` event on the far side. Records are not
|
will result in exactly one `received` event on the far side. Records are not
|
||||||
split, merged, dropped, or reordered.
|
split, merged, dropped, or reordered.
|
||||||
|
|
||||||
Each side can do an arbitrary number of `send()` calls. The Wormhole is not
|
Each side can do an arbitrary number of `send_message()` calls. The Wormhole
|
||||||
meant as a long-term communication channel, but some protocols work better if
|
is not meant as a long-term communication channel, but some protocols work
|
||||||
they can exchange an initial pair of messages (perhaps offering some set of
|
better if they can exchange an initial pair of messages (perhaps offering
|
||||||
negotiable capabilities), and then follow up with a second pair (to reveal
|
some set of negotiable capabilities), and then follow up with a second pair
|
||||||
the results of the negotiation). The Rendezvous Server does not currently
|
(to reveal the results of the negotiation). The Rendezvous Server does not
|
||||||
enforce any particular limits on number of messages, size of messages, or
|
currently enforce any particular limits on number of messages, size of
|
||||||
rate of transmission, but in general clients are expected to send fewer than
|
messages, or rate of transmission, but in general clients are expected to
|
||||||
a dozen messages, of no more than perhaps 20kB in size (remember that all
|
send fewer than a dozen messages, of no more than perhaps 20kB in size
|
||||||
these messages are temporarily stored in a SQLite database on the server). A
|
(remember that all these messages are temporarily stored in a SQLite database
|
||||||
future version of the protocol may make these limits more explicit, and will
|
on the server). A future version of the protocol may make these limits more
|
||||||
allow clients to ask for greater capacity when they connect (probably by
|
explicit, and will allow clients to ask for greater capacity when they
|
||||||
passing additional "mailbox attribute" parameters with the
|
connect (probably by passing additional "mailbox attribute" parameters with
|
||||||
`allocate`/`claim`/`open` messages).
|
the `allocate`/`claim`/`open` messages).
|
||||||
|
|
||||||
For bulk data transfer, see "transit.md", or the "Dilation" section below.
|
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
|
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
|
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
|
Dilated wormholes can provide multiple "channels": these are multiplexed
|
||||||
through the single (encrypted) TCP connection. Each channel is a separate
|
through the single (encrypted) TCP connection. Each channel is a separate
|
||||||
|
@ -571,16 +589,18 @@ in python3):
|
||||||
## Full API list
|
## Full API list
|
||||||
|
|
||||||
action | Deferred-Mode | Delegated-Mode
|
action | Deferred-Mode | Delegated-Mode
|
||||||
------------------ | -------------------- | --------------
|
------------------ | ------------------ | --------------
|
||||||
|
. | d=w.get_welcome() | dg.wormhole_got_welcome(welcome)
|
||||||
w.generate_code() | |
|
w.generate_code() | |
|
||||||
w.set_code(code) | |
|
w.set_code(code) | |
|
||||||
h=w.input_code() | |
|
h=w.input_code() | |
|
||||||
. | d=w.when_code() | dg.wormhole_code(code)
|
. | d=w.get_code() | dg.wormhole_got_code(code)
|
||||||
. | d=w.when_verified() | dg.wormhole_verified(verifier)
|
. | d=w.get_unverified_key() | dg.wormhole_got_unverified_key(key)
|
||||||
. | d=w.when_version() | dg.wormhole_version(version)
|
. | d=w.get_verifier() | dg.wormhole_got_verifier(verifier)
|
||||||
w.send(data) | |
|
. | d=w.get_versions() | dg.wormhole_got_versions(versions)
|
||||||
. | d=w.when_received() | dg.wormhole_received(data)
|
|
||||||
key=w.derive_key(purpose, length) | |
|
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)
|
w.close() | | dg.wormhole_closed(result)
|
||||||
. | d=w.close() |
|
. | d=w.close() |
|
||||||
|
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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
|
# Only warn if we're running a release version (e.g. 0.0.6, not
|
||||||
# 0.0.6+DISTANCE.gHASH). Only warn once.
|
# 0.0.6+DISTANCE.gHASH). Only warn once.
|
||||||
if ("current_cli_version" in welcome
|
if ("current_cli_version" in welcome
|
||||||
and "+" not in self._current_version
|
and "+" not in my_version
|
||||||
and not self._version_warning_displayed
|
and welcome["current_cli_version"] != my_version):
|
||||||
and welcome["current_cli_version"] != self._current_version):
|
print("Warning: errors may occur unless both sides are running the same version", file=stderr)
|
||||||
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"
|
print("Server claims %s is current, but ours is %s"
|
||||||
% (welcome["current_cli_version"], self._current_version),
|
% (welcome["current_cli_version"], my_version),
|
||||||
file=self.stderr)
|
file=stderr)
|
||||||
self._version_warning_displayed = True
|
|
||||||
_WelcomeHandler.handle_welcome(self, welcome)
|
|
||||||
|
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
|
|
Loading…
Reference in New Issue
Block a user