diff --git a/src/allmydata/introducer/client.py b/src/allmydata/introducer/client.py index a2998e17d..b042ca2d5 100644 --- a/src/allmydata/introducer/client.py +++ b/src/allmydata/introducer/client.py @@ -13,6 +13,9 @@ from allmydata.util import log from allmydata.util.rrefutil import add_version_to_remote_reference from allmydata.util.keyutil import BadSignatureError +class InvalidCacheError(Exception): + pass + class WrapV2ClientInV1Interface(Referenceable): # for_v1 """I wrap a v2 IntroducerClient to make it look like a v1 client, so it can be attached to an old server.""" @@ -116,23 +119,28 @@ class IntroducerClient(service.Service, Referenceable): d.addErrback(connect_failed) def _load_announcements(self): - if self._cache_filepath.exists(): + # Announcements contain unicode, because they come from JSON. We tell + # PyYAML to give us unicode instead of str/bytes. + def construct_unicode(loader, node): + return node.value + yaml.SafeLoader.add_constructor("tag:yaml.org,2002:str", + construct_unicode) + try: with self._cache_filepath.open() as f: - servers = yaml.load(f) - f.close() - if not isinstance(servers, list): - msg = "Invalid cached storage server announcements. No list encountered." - self.log(msg, - level=log.WEIRD) - raise storage_client.UnknownServerTypeError(msg) - for server_params in servers: - if not isinstance(server_params, dict): - msg = "Invalid cached storage server announcement encountered. No key/values found in %s" % server_params - self.log(msg, - level=log.WEIRD) - raise storage_client.UnknownServerTypeError(msg) - for _, cb, _, _ in self._local_subscribers: - eventually(cb, server_params['key_s'], server_params['ann']) + servers = yaml.safe_load(f) + except EnvironmentError: + return # no cache file + if not isinstance(servers, list): + log.err(InvalidCacheError("not a list"), level=log.WEIRD) + return + self.log("Using server data from cache", level=log.UNUSUAL) + for server_params in servers: + if not isinstance(server_params, dict): + log.err(InvalidCacheError("not a dict: %r" % (server_params,)), + level=log.WEIRD) + continue + self._deliver_announcements(server_params['key_s'], + server_params['ann']) def _save_announcements(self): announcements = [] @@ -143,7 +151,7 @@ class IntroducerClient(service.Service, Referenceable): "key_s" : key_s, } announcements.append(server_params) - announcement_cache_yaml = yaml.dump(announcements) + announcement_cache_yaml = yaml.safe_dump(announcements) self._cache_filepath.setContent(announcement_cache_yaml) def _got_introducer(self, publisher): diff --git a/src/allmydata/test/test_introducer.py b/src/allmydata/test/test_introducer.py index a361fd8af..af04d1577 100644 --- a/src/allmydata/test/test_introducer.py +++ b/src/allmydata/test/test_introducer.py @@ -1009,10 +1009,11 @@ class Announcements(unittest.TestCase): self.failUnlessEqual(a[0].announcement["anonymous-storage-FURL"], furl1) def _load_cache(self, cache_filepath): + def construct_unicode(loader, node): + return node.value + yaml.SafeLoader.add_constructor("tag:yaml.org,2002:str", + construct_unicode) with cache_filepath.open() as f: - def constructor(loader, node): - return node.value - yaml.SafeLoader.add_constructor("tag:yaml.org,2002:python/unicode", constructor) return yaml.safe_load(f) def test_client_cache(self): @@ -1080,6 +1081,18 @@ class Announcements(unittest.TestCase): for a in announcements])) +class YAMLUnicode(unittest.TestCase): + def test_convert(self): + data = yaml.safe_dump(["str", u"unicode", u"\u1234nicode"]) + def construct_unicode(loader, node): + return node.value + yaml.SafeLoader.add_constructor("tag:yaml.org,2002:str", + construct_unicode) + back = yaml.safe_load(data) + self.failUnlessEqual(type(back[0]), unicode) + self.failUnlessEqual(type(back[1]), unicode) + self.failUnlessEqual(type(back[2]), unicode) + class ClientSeqnums(unittest.TestCase): def test_client(self): basedir = "introducer/ClientSeqnums/test_client"