From b83062701d48595853c2b2292934e0662d5a717c Mon Sep 17 00:00:00 2001 From: Brian Warner Date: Tue, 10 Nov 2015 21:02:44 -0800 Subject: [PATCH] server: give old 0.4.0 senders a "you must upgrade" error Without this, old senders will throw a messy 404 traceback when talking to a modern server. Unfortunately 0.4.0 receivers don't make API calls in the right order, so they throw a 404 before seeing our "you need to upgrade" message. --- NEWS.md | 7 ++++++ src/wormhole/servers/relay_server.py | 35 ++++++++++++++++++++++++++++ src/wormhole/test/test_server.py | 29 +++++++++++++++++++++++ 3 files changed, 71 insertions(+) diff --git a/NEWS.md b/NEWS.md index ab8b167..85b97dd 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,6 +1,13 @@ User-visible changes in "magic-wormhole": +## Release ?? (??) + +* Arrange for 0.4.0 senders to print an error message when connecting to a + current (0.5.0) server, instead of an ugly stack trace. Unfortunately 0.4.0 + receivers still display the traceback, since they don't check the welcome + message before using a missing API. + ## Release 0.5.0 (07-Oct-2015) * Change the CLI to merge send-file with send-text, and receive-file with diff --git a/src/wormhole/servers/relay_server.py b/src/wormhole/servers/relay_server.py index c038172..57fbab6 100644 --- a/src/wormhole/servers/relay_server.py +++ b/src/wormhole/servers/relay_server.py @@ -63,6 +63,9 @@ class ChannelLister(resource.Resource): self._relay = relay def render_GET(self, request): + if b"appid" not in request.args: + e = NeedToUpgradeErrorResource(self._relay.welcome) + return e.get_message() appid = request.args[b"appid"][0].decode("utf-8") #print("LIST", appid) app = self._relay.get_app(appid) @@ -95,6 +98,29 @@ class Allocator(resource.Resource): "channelid": channelid} return (json.dumps(data)+"\n").encode("utf-8") + def getChild(self, path, req): + # wormhole-0.4.0 "send" started with "POST /allocate/SIDE". + # wormhole-0.5.0 changed that to "POST /allocate". We catch the old + # URL here to deliver a nicer error message (with upgrade + # instructions) than an ugly 404. + return NeedToUpgradeErrorResource(self._relay.welcome) + +class NeedToUpgradeErrorResource(resource.Resource): + def __init__(self, welcome): + resource.Resource.__init__(self) + w = welcome.copy() + w["error"] = "Sorry, you must upgrade your client to use this server." + message = {"welcome": w} + self._message = (json.dumps(message)+"\n").encode("utf-8") + def get_message(self): + return self._message + def render_POST(self, request): + return self._message + def render_GET(self, request): + return self._message + def getChild(self, path, req): + return self + class Adder(resource.Resource): def __init__(self, relay): resource.Resource.__init__(self) @@ -311,6 +337,15 @@ class Relay(resource.Resource, service.MultiService): self.putChild(b"get", Getter(self)) self.putChild(b"deallocate", Deallocator(self)) + def getChild(self, path, req): + # 0.4.0 used "POST /CID/SIDE/post/MSGNUM" + # 0.5.0 replaced it with "POST /add (json body)" + # give a nicer error message to old clients + if (len(req.postpath) >= 2 + and req.postpath[1] in (b"post", b"poll", b"deallocate")): + return NeedToUpgradeErrorResource(self.welcome) + return resource.NoResource("No such child resource.") + def get_app(self, appid): assert isinstance(appid, type(u"")) if not appid in self._apps: diff --git a/src/wormhole/test/test_server.py b/src/wormhole/test/test_server.py index 9e3f813..7728152 100644 --- a/src/wormhole/test/test_server.py +++ b/src/wormhole/test/test_server.py @@ -165,6 +165,35 @@ class API(ServerBase, unittest.TestCase): return d + UPGRADE_ERROR = "Sorry, you must upgrade your client to use this server." + def test_old_allocate(self): + # 0.4.0 used "POST /allocate/SIDE". + # 0.5.0 replaced it with "POST /allocate". + # test that an old client gets a useful error message, not a 404. + d = self.post("allocate/abc", {}) + def _check(data): + self.failUnlessEqual(data["welcome"]["error"], self.UPGRADE_ERROR) + d.addCallback(_check) + return d + + def test_old_list(self): + # 0.4.0 used "GET /list". + # 0.5.0 replaced it with "GET /list?appid=" + d = self.get("list", {}) # no appid + def _check(data): + self.failUnlessEqual(data["welcome"]["error"], self.UPGRADE_ERROR) + d.addCallback(_check) + return d + + def test_old_post(self): + # 0.4.0 used "POST /CID/SIDE/post/MSGNUM" + # 0.5.0 replaced it with "POST /add (json body)" + d = self.post("1/abc/post/pake", {}) + def _check(data): + self.failUnlessEqual(data["welcome"]["error"], self.UPGRADE_ERROR) + d.addCallback(_check) + return d + def add_message(self, message, side="abc", phase="1"): return self.post("add", {"appid": "app1",