From 781d791e03fcf829b9d77b7d4df77b6f7f4d46d5 Mon Sep 17 00:00:00 2001 From: Brian Warner Date: Fri, 7 Apr 2017 09:48:48 -0700 Subject: [PATCH] test server-reconnection: it worked already Cool, it actually worked without changes. Closes #42 --- src/wormhole/_rendezvous.py | 5 ++++ src/wormhole/test/test_wormhole.py | 47 ++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/src/wormhole/_rendezvous.py b/src/wormhole/_rendezvous.py index 4029e3f..ec30b45 100644 --- a/src/wormhole/_rendezvous.py +++ b/src/wormhole/_rendezvous.py @@ -77,6 +77,7 @@ class RendezvousConnector(object): ep = self._make_endpoint(p.hostname, p.port or 80) # TODO: change/wrap ClientService to fail if the first attempt fails self._connector = internet.ClientService(ep, f) + self._debug_record_inbound_f = None def set_trace(self, f): self._trace = f @@ -120,6 +121,8 @@ class RendezvousConnector(object): self._tx("close", mailbox=mailbox, mood=mood) def stop(self): + # ClientService.stopService is defined to "Stop attempting to + # reconnect and close any existing connections" d = defer.maybeDeferred(self._connector.stopService) d.addErrback(log.err) # TODO: deliver error upstairs? d.addBoth(self._stopped) @@ -157,6 +160,8 @@ class RendezvousConnector(object): )) self._timing.add("ws_receive", _side=self._side, message=msg) + if self._debug_record_inbound_f: + self._debug_record_inbound_f(msg) mtype = msg["type"] meth = getattr(self, "_response_handle_"+mtype, None) if not meth: diff --git a/src/wormhole/test/test_wormhole.py b/src/wormhole/test/test_wormhole.py index 0dc8eef..bf3a87e 100644 --- a/src/wormhole/test/test_wormhole.py +++ b/src/wormhole/test/test_wormhole.py @@ -493,3 +493,50 @@ class Errors(ServerBase, unittest.TestCase): yield w.when_code() self.assertRaises(OnlyOneCodeError, w.set_code, "123-nope") yield self.assertFailure(w.close(), LonelyError) + +class Reconnection(ServerBase, unittest.TestCase): + @inlineCallbacks + def test_basic(self): + w1 = wormhole.create(APPID, self.relayurl, reactor) + w1_in = [] + w1._boss._RC._debug_record_inbound_f = w1_in.append + #w1.debug_set_trace("W1") + w1.allocate_code() + code = yield w1.when_code() + w1.send(b"data1") # will be queued until wormhole is established + + # now wait until we've deposited all our messages on the server + def seen_our_pake(): + for m in w1_in: + if m["type"] == "message" and m["phase"] == "pake": + return True + return False + yield poll_until(seen_our_pake) + + w1_in[:] = [] + # drop the connection + w1._boss._RC._ws.transport.loseConnection() + # wait for it to reconnect and redeliver all the messages. The server + # sends mtype=message messages in random order, but we've only sent + # one of them, so it's safe to wait for just the PAKE phase. + yield poll_until(seen_our_pake) + + # now let the second side proceed. this simulates the most common + # case: the server is bounced while the sender is waiting, before the + # receiver has started + + w2 = wormhole.create(APPID, self.relayurl, reactor) + #w2.debug_set_trace(" W2") + w2.set_code(code) + + dataY = yield w2.when_received() + self.assertEqual(dataY, b"data1") + + w2.send(b"data2") + dataX = yield w1.when_received() + self.assertEqual(dataX, b"data2") + + c1 = yield w1.close() + self.assertEqual(c1, "happy") + c2 = yield w2.close() + self.assertEqual(c2, "happy")