add demo of twisted flow, update docs

python -m wormhole.twisted.demo send-text TEXT -> CODE
python -m wormhole.twisted.demo receive-text CODE -> TEXT
This commit is contained in:
Brian Warner 2015-06-20 19:03:10 -07:00
parent 25472423c6
commit 6ee09f5316
2 changed files with 74 additions and 28 deletions

View File

@ -50,39 +50,45 @@ theirdata = r.get_data(mydata)
print("Their data: %s" % theirdata.decode("ascii")) print("Their data: %s" % theirdata.decode("ascii"))
``` ```
## Twisted (TODO) ## Twisted
The Twisted-friendly flow, which is not yet implemented, may look like this: The Twisted-friendly flow looks like this:
```python ```python
from twisted.internet import reactor from twisted.internet import reactor
from wormhole.transcribe import TwistedInitiator from wormhole.public_relay import RENDEZVOUS_RELAY
data = b"initiator's data" from wormhole.twisted.transcribe import SymmetricWormhole
ti = TwistedInitiator("appid", data, reactor) outbound_message = b"outbound data"
ti.startService() w1 = SymmetricWormhole("appid", RENDEZVOUS_RELAY)
d1 = ti.when_get_code() d = w1.get_code()
d1.addCallback(lambda code: print("Invitation Code: %s" % code)) def _got_code(code):
d2 = ti.when_get_data() print "Invitation Code:", code
d2.addCallback(lambda theirdata: return w1.get_data(outbound_message)
print("Their data: %s" % theirdata.decode("ascii"))) d.addCallback(_got_code)
d2.addCallback(labmda _: reactor.stop()) def _got_data(inbound_message):
print "Inbound message:", inbound_message
d.addCallback(_got_data)
d.addBoth(lambda _: reactor.stop())
reactor.run() reactor.run()
``` ```
On the other side, you call `set_code()` instead of waiting for `get_code()`:
```python ```python
from twisted.internet import reactor w2 = SymmetricWormhole("appid", RENDEZVOUS_RELAY)
from wormhole.transcribe import TwistedReceiver w2.set_code(code)
data = b"receiver's data" d = w2.get_data(my_message)
code = sys.argv[1]
tr = TwistedReceiver("appid", code, data, reactor)
tr.startService()
d = tr.when_get_data()
d.addCallback(lambda theirdata:
print("Their data: %s" % theirdata.decode("ascii")))
d.addCallback(lambda _: reactor.stop())
reactor.run()
``` ```
You can call `d=w.get_verifier()` before `get_data()`: this will perform the
first half of the PAKE negotiation, then fire the Deferred with a verifier
object (bytes) which can be converted into a printable representation and
manually compared. When the users are convinced that `get_verifier()` from
both sides are the same, call `d=get_data()` to continue the transfer. If you
call `get_data()` first, it will perform the complete transfer without
pausing.
## Application Identifier ## Application Identifier
Applications using this library must provide an "application identifier", a Applications using this library must provide an "application identifier", a
@ -135,17 +141,27 @@ sync will be abandoned, and all callbacks will errback with a TimeoutError.
Both have defaults suitable for face-to-face realtime setup environments. Both have defaults suitable for face-to-face realtime setup environments.
## Serialization (TODO) ## Serialization
TODO: only the Twisted form supports serialization so far
You may not be able to hold the Initiator/Receiver object in memory for the 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 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 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 the state of the object by calling `data = w.serialize()`, which will return
a printable bytestring (the JSON-encoding of a small dictionary). To restore, a printable bytestring (the JSON-encoding of a small dictionary). To restore,
call `Initiator.from_serialized(data)`. use the `from_serialized(data)` classmethod (e.g. `w =
SymmetricWormhole.from_serialized(data)`).
Note that callbacks are not serialized: they must be restored after There is exactly one point at which you can serialize the wormhole: *after*
deserialization. establishing the invitation code, but before waiting for `get_verifier()` or
`get_data()`. If you are creating a new code, the correct time is during the
callback fired by `get_code()`. If you are accepting a pre-generated code,
the time is just after calling `set_code()`.
To properly checkpoint the process, you should store the first message
(returned by `start()`) next to the serialized wormhole instance, so you can
re-send it if necessary.
## Detailed Example ## Detailed Example

View File

@ -0,0 +1,30 @@
import sys
from twisted.internet import reactor
from .transcribe import SymmetricWormhole
from .. import public_relay
APPID = "lothar.com/wormhole/text-xfer"
w = SymmetricWormhole(APPID, public_relay.RENDEZVOUS_RELAY)
if sys.argv[1] == "send-text":
message = sys.argv[2]
d = w.get_code()
def _got_code(code):
print "code is:", code
return w.get_data(message)
d.addCallback(_got_code)
def _got_data(their_data):
print "ack:", their_data
d.addCallback(_got_data)
elif sys.argv[1] == "receive-text":
code = sys.argv[2]
w.set_code(code)
d = w.get_data("ok")
def _got_data(their_data):
print their_data
d.addCallback(_got_data)
else:
raise ValueError("bad command")
d.addCallback(lambda _: reactor.stop())
reactor.run()