From 84852f26f57b0a7ca2246407c8c435d787600f1e Mon Sep 17 00:00:00 2001 From: Brian Warner Date: Tue, 10 Feb 2015 16:50:32 -0800 Subject: [PATCH] start on sample clients --- docs/api.md | 45 ++++++++++++++++++++++++++++++++++++ src/wormhole/receive_file.py | 23 ++++++++++++++++++ src/wormhole/receive_text.py | 14 +++++++++++ src/wormhole/send_file.py | 31 +++++++++++++++++++++++++ src/wormhole/send_text.py | 24 +++++++++++++++++++ 5 files changed, 137 insertions(+) create mode 100644 src/wormhole/receive_file.py create mode 100644 src/wormhole/receive_text.py create mode 100644 src/wormhole/send_file.py create mode 100644 src/wormhole/send_text.py diff --git a/docs/api.md b/docs/api.md index e16ae9b..8e998a8 100644 --- a/docs/api.md +++ b/docs/api.md @@ -88,3 +88,48 @@ thousands of concurrent sessions. The library uses a baked-in rendezvous server hostname. This must be the same for both clients. To use a different hostname provide it as the `rendezvous=` argument to the `Initiator`/`Receiver` constructor. + +## Polling and Shutdown + +The reactor-based (Twisted-style) forms of these objects need to establish +TCP connections, re-establish them if they are lost, and sometimes (for +transports that don't support long-running connections) poll for new +messages. They may also time out eventually. Longer delays mean less network +traffic, but higher latency. + +These timers should be matched to the expectations, and expected behavior, of +your users. In a file-transfer application, where the users are sitting next +to each other, it is appropriate to poll very frequently (perhaps every +500ms) for a few minutes, then give up. In an email-like messaging program +where the introduction is establishing a long-term relationship, and the +program can store any outgoing messages until the connection is established, +it is probably better to poll once a minute for the first few minutes, then +back off to once an hour, and not give up for several days. + +The `schedule=` constructor argument establishes the polling schedule. It +should contain a sorted list of (when, interval) tuples (both floats). At +`when` seconds after the first `start()` call, the polling interval will be +set to `interval`. + +The `timeout=` argument provides a hard timeout. After this many seconds, the +sync will be abandoned, and all callbacks will errback with a TimeoutError. + +Both have defaults suitable for face-to-face realtime setup environments. + +## Serialization + +You may not be able to hold the Initiator/Receiver object in memory for the +whole sync process: maybe you allow it to wait for several days, but the +program will be restarted during that time. To support this, you can persist +the state of the object by calling `data = i.serialize()`, which will return +a printable bytestring (the JSON-encoding of a small dictionary). To restore, +call `Initiator.from_serialized(data)`. + +Note that callbacks are not serialized: they must be restored after +deserialization. + +## Detailed Example + +```python + +``` diff --git a/src/wormhole/receive_file.py b/src/wormhole/receive_file.py new file mode 100644 index 0000000..845572a --- /dev/null +++ b/src/wormhole/receive_file.py @@ -0,0 +1,23 @@ + +import sys, json +from binascii import unhexlify +from nacl.secret import SecretBox +from nacl import utils +from . import api + +APPID = "lothar.com/wormhole/file-xfer" +RELAY = "example.com" + +# we're receiving +code = sys.argv[1] +blob = b"" +r = api.Receiver(APPID, blob, code) +them_bytes = r.finish() +them_d = json.loads(them_bytes.decode("utf-8")) +print("them: %r" % (them_d,)) +xfer_key = unhexlify(them_d["xfer_key"].encode("ascii")) +filename = them_d["filename"] # unicode +filesize = them_d["filesize"] +relay = them_d["relay"].encode("ascii") + +# now receive the rest of the owl diff --git a/src/wormhole/receive_text.py b/src/wormhole/receive_text.py new file mode 100644 index 0000000..9bf6fdd --- /dev/null +++ b/src/wormhole/receive_text.py @@ -0,0 +1,14 @@ + +import sys, json +from . import api + +APPID = "lothar.com/wormhole/text-xfer" +RELAY = "example.com" + +# we're receiving +code = sys.argv[1] +blob = b"" +r = api.Receiver(APPID, blob, code) +them_bytes = r.finish() +them_d = json.loads(them_bytes.decode("utf-8")) +print(them_d["message"]) diff --git a/src/wormhole/send_file.py b/src/wormhole/send_file.py new file mode 100644 index 0000000..a3a26cc --- /dev/null +++ b/src/wormhole/send_file.py @@ -0,0 +1,31 @@ + +import os, sys, json +from binascii import hexlify +from nacl.secret import SecretBox +from nacl import utils +from . import api + +APPID = "lothar.com/wormhole/file-xfer" +RELAY = "example.com" + +# we're sending +filename = sys.argv[1] +assert os.path.isfile(filename) +xfer_key = utils.random(SecretBox.KEY_SIZE) +blob = json.dumps({"xfer_key": hexlify(xfer_key), + "filename": os.path.basename(filename), + "filesize": os.stat(filename).st_size, + "relay": RELAY, + }).encode("utf-8") +i = api.Initiator(APPID, blob) +code = i.start() +print("Wormhole code is '%s'" % code) +print("On the other computer, please run:") +print() +print(" wormhole-receive-file %s" % code) +print() +them_bytes = i.finish() +them_d = json.loads(them_bytes.decode("utf-8")) +print("them: %r" % (them_d,)) + +# now draw the rest of the owl diff --git a/src/wormhole/send_text.py b/src/wormhole/send_text.py new file mode 100644 index 0000000..486db88 --- /dev/null +++ b/src/wormhole/send_text.py @@ -0,0 +1,24 @@ + +import sys, json +from nacl.secret import SecretBox +from nacl import utils +from . import api + +APPID = "lothar.com/wormhole/text-xfer" +RELAY = "example.com" + +# we're sending +message = sys.argv[1] +xfer_key = utils.random(SecretBox.KEY_SIZE) +blob = json.dumps({"message": message, + }).encode("utf-8") +i = api.Initiator(APPID, blob) +code = i.start() +print("Wormhole code is '%s'" % code) +print("On the other computer, please run:") +print() +print(" wormhole-receive-text %s" % code) +print() +them_bytes = i.finish() +them_d = json.loads(them_bytes.decode("utf-8")) +print("them: %r" % (them_d,))