Replacement for #220
This commit is contained in:
parent
54036c231f
commit
ed7962f7d8
|
@ -12,15 +12,24 @@ from wormhole import journal, wormhole
|
|||
# to use for everything. App should only hold objects that are active
|
||||
# (Services, subscribers, etc). App must wire up these objects each time.
|
||||
|
||||
|
||||
def parse(args):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
def update_my_state():
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class State(object):
|
||||
@classmethod
|
||||
def create_empty(klass):
|
||||
self = klass()
|
||||
# to avoid being tripped up by state-mutation side-effect bugs, we
|
||||
# hold the serialized state in RAM, and re-deserialize it each time
|
||||
# someone asks for a piece of it.
|
||||
# someone asks for a piece of it. # iid->invitation_stat
|
||||
empty = {"version": 1,
|
||||
"invitations": {}, # iid->invitation_state
|
||||
"invitations": {},
|
||||
"contacts": [],
|
||||
}
|
||||
self._bytes = json.dumps(empty).encode("utf-8")
|
||||
|
@ -39,7 +48,7 @@ class State(object):
|
|||
return self
|
||||
|
||||
def save_to_filename(self, fn):
|
||||
tmpfn = fn+".tmp"
|
||||
tmpfn = fn + ".tmp"
|
||||
with open(tmpfn, "wb") as f:
|
||||
f.write(self._bytes)
|
||||
os.rename(tmpfn, fn)
|
||||
|
@ -50,18 +59,21 @@ class State(object):
|
|||
@contextlib.contextmanager
|
||||
def _mutate(self):
|
||||
data = self._as_data()
|
||||
yield data # mutable
|
||||
yield data # mutable
|
||||
self._bytes = json.dumps(data).encode("utf-8")
|
||||
|
||||
def get_all_invitations(self):
|
||||
return self._as_data()["invitations"]
|
||||
|
||||
def add_invitation(self, iid, invitation_state):
|
||||
with self._mutate() as data:
|
||||
data["invitations"][iid] = invitation_state
|
||||
|
||||
def update_invitation(self, iid, invitation_state):
|
||||
with self._mutate() as data:
|
||||
assert iid in data["invitations"]
|
||||
data["invitations"][iid] = invitation_state
|
||||
|
||||
def remove_invitation(self, iid):
|
||||
with self._mutate() as data:
|
||||
del data["invitations"][iid]
|
||||
|
@ -71,23 +83,26 @@ class State(object):
|
|||
data["contacts"].append(contact)
|
||||
|
||||
|
||||
|
||||
class Root(resource.Resource):
|
||||
pass
|
||||
|
||||
|
||||
class Status(resource.Resource):
|
||||
def __init__(self, c):
|
||||
resource.Resource.__init__(self)
|
||||
self._call = c
|
||||
|
||||
def render_GET(self, req):
|
||||
data = self._call()
|
||||
req.setHeader(b"content-type", "text/plain")
|
||||
return data
|
||||
|
||||
|
||||
class Action(resource.Resource):
|
||||
def __init__(self, c):
|
||||
resource.Resource.__init__(self)
|
||||
self._call = c
|
||||
|
||||
def render_POST(self, req):
|
||||
req.setHeader(b"content-type", "text/plain")
|
||||
try:
|
||||
|
@ -98,6 +113,7 @@ class Action(resource.Resource):
|
|||
data = self._call(args)
|
||||
return data
|
||||
|
||||
|
||||
class Agent(service.MultiService):
|
||||
def __init__(self, basedir, reactor):
|
||||
service.MultiService.__init__(self)
|
||||
|
@ -113,8 +129,8 @@ class Agent(service.MultiService):
|
|||
|
||||
root.putChild(b"", static.Data("root", "text/plain"))
|
||||
root.putChild(b"list-invitations", Status(self._list_invitations))
|
||||
root.putChild(b"invite", Action(self._invite)) # {petname:}
|
||||
root.putChild(b"accept", Action(self._accept)) # {petname:, code:}
|
||||
root.putChild(b"invite", Action(self._invite)) # {petname:}
|
||||
root.putChild(b"accept", Action(self._accept)) # {petname:, code:}
|
||||
|
||||
self._state_fn = os.path.join(self._basedir, "state.json")
|
||||
self._state = State.from_filename(self._state_fn)
|
||||
|
@ -131,17 +147,16 @@ class Agent(service.MultiService):
|
|||
self._wormholes[iid] = w
|
||||
w.setServiceParent(self)
|
||||
|
||||
|
||||
def _save_state(self):
|
||||
self._state.save_to_filename(self._state_fn)
|
||||
|
||||
def _list_invitations(self):
|
||||
inv = self._state.get_all_invitations()
|
||||
lines = ["%d: %s" % (iid, inv[iid]) for iid in sorted(inv)]
|
||||
return b"\n".join(lines)+b"\n"
|
||||
return b"\n".join(lines) + b"\n"
|
||||
|
||||
def _invite(self, args):
|
||||
print "invite", args
|
||||
print("invite", args)
|
||||
petname = args["petname"]
|
||||
# it'd be better to use a unique object for the event_handler
|
||||
# correlation, but we can't store them into the state database. I'm
|
||||
|
@ -149,15 +164,15 @@ class Agent(service.MultiService):
|
|||
# list instead, and assign lookup keys at runtime. If they really
|
||||
# need to be serializable, they should be allocated rather than
|
||||
# random.
|
||||
iid = random.randint(1,1000)
|
||||
my_pubkey = random.randint(1,1000)
|
||||
iid = random.randint(1, 1000)
|
||||
my_pubkey = random.randint(1, 1000)
|
||||
with self._jm.process():
|
||||
w = wormhole.journaled(reactor=self._reactor, journal=self._jm,
|
||||
event_handler=self,
|
||||
event_handler_args=(iid,))
|
||||
self._wormholes[iid] = w
|
||||
w.setServiceParent(self)
|
||||
w.get_code() # event_handler means code returns via callback
|
||||
w.get_code() # event_handler means code returns via callback
|
||||
invitation_state = {"wormhole": w.to_data(),
|
||||
"petname": petname,
|
||||
"my_pubkey": my_pubkey,
|
||||
|
@ -166,15 +181,15 @@ class Agent(service.MultiService):
|
|||
return b"ok"
|
||||
|
||||
def _accept(self, args):
|
||||
print "accept", args
|
||||
print("accept", args)
|
||||
petname = args["petname"]
|
||||
code = args["code"]
|
||||
iid = random.randint(1,1000)
|
||||
my_pubkey = random.randint(2,2000)
|
||||
iid = random.randint(1, 1000)
|
||||
my_pubkey = random.randint(2, 2000)
|
||||
with self._jm.process():
|
||||
w = wormhole.journaled(reactor=self._reactor, journal=self._jm,
|
||||
event_dispatcher=self,
|
||||
event_dispatcher_args=(iid,))
|
||||
event_dispatcher=self,
|
||||
event_dispatcher_args=(iid,))
|
||||
w.set_code(code)
|
||||
md = {"my_pubkey": my_pubkey}
|
||||
w.send(json.dumps(md).encode("utf-8"))
|
||||
|
@ -209,6 +224,7 @@ class Agent(service.MultiService):
|
|||
|
||||
def wormhole_dispatch_got_verifier(self, verifier, iid):
|
||||
pass
|
||||
|
||||
def wormhole_dispatch_verified(self, _, iid):
|
||||
pass
|
||||
|
||||
|
@ -220,15 +236,14 @@ class Agent(service.MultiService):
|
|||
"their_pubkey": md["my_pubkey"],
|
||||
}
|
||||
self._state.add_contact(contact)
|
||||
self._wormholes[iid].close() # now waiting for "closed"
|
||||
self._wormholes[iid].close() # now waiting for "closed"
|
||||
|
||||
def wormhole_dispatch_closed(self, _, iid):
|
||||
self._wormholes[iid].disownServiceParent()
|
||||
del self._wormholes[iid]
|
||||
self._state.remove_invitation(iid)
|
||||
|
||||
|
||||
def handle_app_event(self, args, ack_f): # sample function
|
||||
def handle_app_event(self, args, ack_f): # sample function
|
||||
# Imagine here that the app has received a message (not
|
||||
# wormhole-related) from some other server, and needs to act on it.
|
||||
# Also imagine that ack_f() is how we tell the sender that they can
|
||||
|
@ -236,26 +251,27 @@ class Agent(service.MultiService):
|
|||
# client to send a DELETE message. If the process dies before ack_f()
|
||||
# delivers whatever it needs to deliver, then in the next launch,
|
||||
# handle_app_event() will be called again.
|
||||
stuff = parse(args)
|
||||
stuff = parse(args) # noqa
|
||||
with self._jm.process():
|
||||
update_my_state()
|
||||
self._jm.queue_outbound(ack_f)
|
||||
|
||||
|
||||
def create(reactor, basedir):
|
||||
os.mkdir(basedir)
|
||||
s = State.create_empty()
|
||||
s.save(os.path.join(basedir, "state.json"))
|
||||
return defer.succeed(None)
|
||||
|
||||
|
||||
def run(reactor, basedir):
|
||||
a = Agent(basedir, reactor)
|
||||
a.startService()
|
||||
print "agent listening on http://localhost:8220/"
|
||||
print("agent listening on http://localhost:8220/")
|
||||
d = defer.Deferred()
|
||||
return d
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
command = sys.argv[1]
|
||||
basedir = sys.argv[2]
|
||||
|
@ -264,7 +280,5 @@ if __name__ == "__main__":
|
|||
elif command == "run":
|
||||
task.react(run, (basedir,))
|
||||
else:
|
||||
print "Unrecognized subcommand '%s'" % command
|
||||
print("Unrecognized subcommand '%s'" % command)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user