def test_unreachable_client(self): # A "client-only" Tub has no location set. It should still be # possible to connect to objects in other (location-bearing) server # Tubs, and objects in the client Tub can still be sent to (and used # by) the server Tub. client_tub = Tub() client_tub.setServiceParent(self.s) server_tub = Tub() server_tub.setServiceParent(self.s) portnum = allocate_tcp_port() server_tub.listenOn("tcp:%d:interface=127.0.0.1" % portnum) server_tub.setLocation("tcp:127.0.0.1:%d" % portnum) s = Receiver() # no FURL, not directly reachable r = Receiver() furl = server_tub.registerReference(r) d = client_tub.getReference(furl) d.addCallback(lambda rref: rref.callRemote("call", s)) d.addCallback(lambda res: self.assertEqual(res, 1)) d.addCallback(lambda _: self.assertNotEqual(r.obj, None)) def _inspect_obj(_): self.assertEqual(r.obj.getSturdyRef().getURL(), None) d.addCallback(_inspect_obj) d.addCallback(lambda _: r.obj.callRemote("call", 2)) d.addCallback(lambda _: self.assertEqual(s.obj, 2)) return d
def test_unreachable_gift(self): client_tub = Tub() client_tub.setServiceParent(self.s) server_tub = Tub() server_tub.setServiceParent(self.s) recipient_tub = Tub() recipient_tub.setServiceParent(self.s) portnum = allocate_tcp_port() server_tub.listenOn("tcp:%d:interface=127.0.0.1" % portnum) server_tub.setLocation("tcp:127.0.0.1:%d" % portnum) s = Receiver() # no FURL, not directly reachable r = Receiver() furl = server_tub.registerReference(r) d = client_tub.getReference(furl) d.addCallback(lambda rref: rref.callRemote("call", s)) d.addCallback(lambda res: self.assertEqual(res, 1)) d.addCallback(lambda _: recipient_tub.getReference(furl)) # when server_tub tries to send the lame 's' rref to recipient_tub, # the RemoteReferenceTracker won't have a FURL, so it will be # serialized as a (their-reference furl="") sequence. Then # recipient_tub will try to resolve it, and will throw a # NoLocationHintsError. It might be more natural to send # (their-reference furl=None), but the constraint schema on # their-references forbids non-strings. It might also seem # appropriate to raise a Violation (i.e. server_tub is bad for trying # to send it, rather than foisting the problem off to recipient_tub), # but that causes the connection explode and fall out of sync. d.addCallback(lambda rref: self.shouldFail( NoLocationHintsError, "gift_me", None, rref.callRemote, "gift_me")) return d
def test_retry(self): tubC = Tub(certData=self.tubB.getCertData()) connects = [] target = HelperTarget("bob") url = self.tubB.registerReference(target, "target") portb = self.tub_ports[1] d1 = defer.Deferred() notifiers = [d1] self.services.remove(self.tubB) # This will fail, since tubB is not listening anymore. Wait until it's # moved to the "waiting" state. yield self.tubB.stopService() rc = self.tubA.connectTo(url, self._connected, notifiers, connects) yield self.poll(lambda: rc.getReconnectionInfo().state == "waiting") self.failUnlessEqual(len(connects), 0) # now start tubC listening on the same port that tubB used to, which # should allow the connection to complete (since they both use the same # certData) self.services.append(tubC) tubC.startService() tubC.listenOn("tcp:%d:interface=127.0.0.1" % portb) tubC.setLocation("tcp:127.0.0.1:%d" % portb) url2 = tubC.registerReference(target, "target") assert url2 == url yield d1 self.failUnlessEqual(len(connects), 1) rc.stopConnecting()
class StatsGathererService(service.MultiService): furl_file = "stats_gatherer.furl" def __init__(self, basedir=".", verbose=False): service.MultiService.__init__(self) self.basedir = basedir self.tub = Tub(certFile=os.path.join(self.basedir, "stats_gatherer.pem")) self.tub.setServiceParent(self) self.tub.setOption("logLocalFailures", True) self.tub.setOption("logRemoteFailures", True) self.tub.setOption("expose-remote-exception-types", False) self.stats_gatherer = JSONStatsGatherer(self.basedir, verbose) self.stats_gatherer.setServiceParent(self) try: with open(os.path.join(self.basedir, "location")) as f: location = f.read().strip() except EnvironmentError: raise ValueError("Unable to find 'location' in BASEDIR, please rebuild your stats-gatherer") try: with open(os.path.join(self.basedir, "port")) as f: port = f.read().strip() except EnvironmentError: raise ValueError("Unable to find 'port' in BASEDIR, please rebuild your stats-gatherer") self.tub.listenOn(port) self.tub.setLocation(location) ff = os.path.join(self.basedir, self.furl_file) self.gatherer_furl = self.tub.registerReference(self.stats_gatherer, furlFile=ff)
def test_unreachable_gift(self): client_tub = Tub() client_tub.setServiceParent(self.s) server_tub = Tub() server_tub.setServiceParent(self.s) recipient_tub = Tub() recipient_tub.setServiceParent(self.s) portnum = allocate_tcp_port() server_tub.listenOn("tcp:%d:interface=127.0.0.1" % portnum) server_tub.setLocation("tcp:127.0.0.1:%d" % portnum) s = Receiver() # no FURL, not directly reachable r = Receiver() furl = server_tub.registerReference(r) d = client_tub.getReference(furl) d.addCallback(lambda rref: rref.callRemote("call", s)) d.addCallback(lambda res: self.failUnlessEqual(res, 1)) d.addCallback(lambda _: recipient_tub.getReference(furl)) # when server_tub tries to send the lame 's' rref to recipient_tub, # the RemoteReferenceTracker won't have a FURL, so it will be # serialized as a (their-reference furl="") sequence. Then # recipient_tub will try to resolve it, and will throw a # NoLocationHintsError. It might be more natural to send # (their-reference furl=None), but the constraint schema on # their-references forbids non-strings. It might also seem # appropriate to raise a Violation (i.e. server_tub is bad for trying # to send it, rather than foisting the problem off to recipient_tub), # but that causes the connection explode and fall out of sync. d.addCallback(lambda rref: self.shouldFail(NoLocationHintsError, "gift_me", None, rref.callRemote, "gift_me")) return d
def test_unreachable_client(self): # A "client-only" Tub has no location set. It should still be # possible to connect to objects in other (location-bearing) server # Tubs, and objects in the client Tub can still be sent to (and used # by) the server Tub. client_tub = Tub() client_tub.setServiceParent(self.s) server_tub = Tub() server_tub.setServiceParent(self.s) portnum = allocate_tcp_port() server_tub.listenOn("tcp:%d:interface=127.0.0.1" % portnum) server_tub.setLocation("tcp:127.0.0.1:%d" % portnum) s = Receiver() # no FURL, not directly reachable r = Receiver() furl = server_tub.registerReference(r) d = client_tub.getReference(furl) d.addCallback(lambda rref: rref.callRemote("call", s)) d.addCallback(lambda res: self.failUnlessEqual(res, 1)) d.addCallback(lambda _: self.failIfEqual(r.obj, None)) def _inspect_obj(_): self.failUnlessEqual(r.obj.getSturdyRef().getURL(), None) d.addCallback(_inspect_obj) d.addCallback(lambda _: r.obj.callRemote("call", 2)) d.addCallback(lambda _: self.failUnlessEqual(s.obj, 2)) return d
def test_set_location(self): t = Tub() t.listenOn("tcp:0") t.setServiceParent(self.s) t.setLocation("127.0.0.1:12345") # setLocation may only be called once self.failUnlessRaises(PBError, t.setLocation, "127.0.0.1:12345")
def test_furlfile(self): cfn = "test_tub.FurlFile.test_furlfile.certfile" t1 = Tub(certFile=cfn) t1.setServiceParent(self.s) portnum = allocate_tcp_port() port1 = "tcp:%d:interface=127.0.0.1" % portnum t1.listenOn(port1) t1.setLocation("127.0.0.1:%d" % portnum) r1 = Referenceable() ffn = "test_tub.FurlFile.test_furlfile.furlfile" furl1 = t1.registerReference(r1, furlFile=ffn) d = defer.maybeDeferred(t1.disownServiceParent) self.failUnless(os.path.exists(ffn)) self.failUnlessEqual(furl1, open(ffn,"r").read().strip()) def _take2(res): t2 = Tub(certFile=cfn) t2.setServiceParent(self.s) t2.listenOn(port1) t2.setLocation("127.0.0.1:%d" % portnum) r2 = Referenceable() furl2 = t2.registerReference(r2, furlFile=ffn) self.failUnlessEqual(furl1, furl2) return t2.disownServiceParent() d.addCallback(_take2) return d
def test_tubid_check(self): t1 = Tub() # gets a new key t1.setServiceParent(self.s) portnum = allocate_tcp_port() port1 = "tcp:%d:interface=127.0.0.1" % portnum t1.listenOn(port1) t1.setLocation("127.0.0.1:%d" % portnum) r1 = Referenceable() ffn = "test_tub.FurlFile.test_tubid_check.furlfile" furl1 = t1.registerReference(r1, furlFile=ffn) d = defer.maybeDeferred(t1.disownServiceParent) self.assertTrue(os.path.exists(ffn)) self.assertEqual(furl1, open(ffn, "r").read().strip()) def _take2(res): t2 = Tub() # gets a different key t2.setServiceParent(self.s) t2.listenOn(port1) t2.setLocation("127.0.0.1:%d" % portnum) r2 = Referenceable() self.assertRaises(WrongTubIdError, t2.registerReference, r2, furlFile=ffn) return t2.disownServiceParent() d.addCallback(_take2) return d
def test_furlfile(self): cfn = "test_tub.FurlFile.test_furlfile.certfile" t1 = Tub(certFile=cfn) t1.setServiceParent(self.s) portnum = allocate_tcp_port() port1 = "tcp:%d:interface=127.0.0.1" % portnum t1.listenOn(port1) t1.setLocation("127.0.0.1:%d" % portnum) r1 = Referenceable() ffn = "test_tub.FurlFile.test_furlfile.furlfile" furl1 = t1.registerReference(r1, furlFile=ffn) d = defer.maybeDeferred(t1.disownServiceParent) self.assertTrue(os.path.exists(ffn)) self.assertEqual(furl1, open(ffn, "r").read().strip()) def _take2(res): t2 = Tub(certFile=cfn) t2.setServiceParent(self.s) t2.listenOn(port1) t2.setLocation("127.0.0.1:%d" % portnum) r2 = Referenceable() furl2 = t2.registerReference(r2, furlFile=ffn) self.assertEqual(furl1, furl2) return t2.disownServiceParent() d.addCallback(_take2) return d
def test_referenceable(self): t1 = Tub() t1.setServiceParent(self.s) portnum = allocate_tcp_port() t1.listenOn("tcp:%d:interface=127.0.0.1" % portnum) t1.setLocation("127.0.0.1:%d" % portnum) r1 = Referenceable() # the serialized blob can't keep the reference alive, so you must # arrange for that separately t1.registerReference(r1) t2 = Tub() t2.setServiceParent(self.s) obj = ("graph tangly", r1) d = t1.serialize(obj) del r1; del obj def _done(data): self.failUnless("their-reference" in data) return data d.addCallback(_done) d.addCallback(lambda data: t2.unserialize(data)) def _check(obj2): self.failUnlessEqual(obj2[0], "graph tangly") self.failUnless(isinstance(obj2[1], RemoteReference)) d.addCallback(_check) return d
def test_referenceable(self): t1 = Tub() t1.setServiceParent(self.s) portnum = allocate_tcp_port() t1.listenOn("tcp:%d:interface=127.0.0.1" % portnum) t1.setLocation("127.0.0.1:%d" % portnum) r1 = Referenceable() # the serialized blob can't keep the reference alive, so you must # arrange for that separately t1.registerReference(r1) t2 = Tub() t2.setServiceParent(self.s) obj = ("graph tangly", r1) d = t1.serialize(obj) del r1 del obj def _done(data): self.assertTrue(b"their-reference" in data) return data d.addCallback(_done) d.addCallback(lambda data: t2.unserialize(data)) def _check(obj2): self.assertEqual(obj2[0], "graph tangly") self.assertTrue(isinstance(obj2[1], RemoteReference)) d.addCallback(_check) return d
def get_tub(application): myserver = RemoteControl(application) tub = Tub(certFile=os.path.join(consenso.directories.foolscap_dir, "pb2server.pem")) tub.listenOn("tcp:12345") tub.setLocation("localhost:12345") tub.registerReference(myserver, "remote-control", furlFile=furlfile) return tub
def test_tubid_check(self): t1 = Tub() # gets a new key t1.setServiceParent(self.s) portnum = allocate_tcp_port() port1 = "tcp:%d:interface=127.0.0.1" % portnum t1.listenOn(port1) t1.setLocation("127.0.0.1:%d" % portnum) r1 = Referenceable() ffn = "test_tub.FurlFile.test_tubid_check.furlfile" furl1 = t1.registerReference(r1, furlFile=ffn) d = defer.maybeDeferred(t1.disownServiceParent) self.failUnless(os.path.exists(ffn)) self.failUnlessEqual(furl1, open(ffn,"r").read().strip()) def _take2(res): t2 = Tub() # gets a different key t2.setServiceParent(self.s) t2.listenOn(port1) t2.setLocation("127.0.0.1:%d" % portnum) r2 = Referenceable() self.failUnlessRaises(WrongTubIdError, t2.registerReference, r2, furlFile=ffn) return t2.disownServiceParent() d.addCallback(_take2) return d
def test_set_location(self): t = Tub() portnum = allocate_tcp_port() t.listenOn("tcp:%d:interface=127.0.0.1" % portnum) t.setServiceParent(self.s) t.setLocation("127.0.0.1:12345") # setLocation may only be called once self.failUnlessRaises(PBError, t.setLocation, "127.0.0.1:12345")
def test_set_location(self): t = Tub() portnum = allocate_tcp_port() t.listenOn("tcp:%d:interface=127.0.0.1" % portnum) t.setServiceParent(self.s) t.setLocation("127.0.0.1:12345") # setLocation may only be called once self.assertRaises(PBError, t.setLocation, "127.0.0.1:12345")
def _take2(res): t2 = Tub(certFile=cfn) t2.setServiceParent(self.s) t2.listenOn(port1) t2.setLocation("127.0.0.1:%d" % portnum) r2 = Referenceable() furl2 = t2.registerReference(r2, furlFile=ffn) self.assertEqual(furl1, furl2) return t2.disownServiceParent()
def _take2(res): t2 = Tub(certFile=cfn) t2.setServiceParent(self.s) t2.listenOn(port1) t2.setLocation("127.0.0.1:%d" % portnum) r2 = Referenceable() furl2 = t2.registerReference(r2, furlFile=ffn) self.failUnlessEqual(furl1, furl2) return t2.disownServiceParent()
def _take2(res): t2 = Tub() # gets a different key t2.setServiceParent(self.s) t2.listenOn(port1) t2.setLocation("127.0.0.1:%d" % portnum) r2 = Referenceable() self.assertRaises(WrongTubIdError, t2.registerReference, r2, furlFile=ffn) return t2.disownServiceParent()
def _take2(res): t2 = Tub() # gets a different key t2.setServiceParent(self.s) t2.listenOn(port1) t2.setLocation("127.0.0.1:%d" % portnum) r2 = Referenceable() self.failUnlessRaises(WrongTubIdError, t2.registerReference, r2, furlFile=ffn) return t2.disownServiceParent()
def makeTub(self, hint_type): tubA = Tub(certData=certData_low) tubA.setServiceParent(self.s) tubB = Tub(certData=certData_high) tubB.setServiceParent(self.s) portnum = util.allocate_tcp_port() tubA.listenOn("tcp:%d:interface=127.0.0.1" % portnum) tubA.setLocation("%s:127.0.0.1:%d" % (hint_type, portnum)) furl = tubA.registerReference(Target()) return furl, tubB
class GatheringBase(service.MultiService, Referenceable): # requires self.furlFile and self.tacFile to be set on the class, both of # which should be relative to the basedir. use_local_addresses = True def __init__(self, basedir): service.MultiService.__init__(self) if basedir is None: # This instance was created by a gatherer.tac file. Confirm that # we're running from the right directory (the one with the .tac # file), otherwise we'll put the logfiles in the wrong place. basedir = os.getcwd() tac = os.path.join(basedir, self.tacFile) if not os.path.exists(tac): raise RuntimeError("running in the wrong directory") self.basedir = basedir certFile = os.path.join(self.basedir, "gatherer.pem") portfile = os.path.join(self.basedir, "port") locationfile = os.path.join(self.basedir, "location") furlFile = os.path.join(self.basedir, self.furlFile) # Foolscap-0.11.0 was the last release that used # automatically-determined listening addresses and ports. New ones # (created with "flogtool create-gatherer" or # "create-incident-gathererer" now require --location and --port # arguments to provide these values. If you really don't want to # create a new one, you can write "tcp:3117" (or some other port # number of your choosing) to BASEDIR/port, and "tcp:$HOSTNAME:3117" # (with your hostname or IP address) to BASEDIR/location if (not os.path.exists(portfile) or not os.path.exists(locationfile)): raise ObsoleteGatherer("Please create a new gatherer, with both " "--port and --location") try: with open(portfile, "r") as f: port = f.read().strip() except EnvironmentError: raise ObsoleteGatherer("Please create a new gatherer, with both " "--port and --location") try: with open(locationfile, "r") as f: location = f.read().strip() except EnvironmentError: raise ObsoleteGatherer("Please create a new gatherer, with both " "--port and --location") self._tub = Tub(certFile=certFile) self._tub.setServiceParent(self) self._tub.listenOn(port) self._tub.setLocation(location) self.my_furl = self._tub.registerReference(self, furlFile=furlFile) if self.verbose: print "Gatherer waiting at:", self.my_furl
def createSpecificServer(self, certData, negotiationClass=negotiate.Negotiation): tub = Tub(certData=certData) tub.negotiationClass = negotiationClass tub.startService() self.services.append(tub) portnum = allocate_tcp_port() tub.listenOn("tcp:%d:interface=127.0.0.1" % portnum) tub.setLocation("127.0.0.1:%d" % portnum) target = Target() return tub, target, tub.registerReference(target), portnum
class AppServer(service.MultiService): def __init__(self, basedir=".", stdout=sys.stdout): service.MultiService.__init__(self) self.basedir = os.path.abspath(basedir) try: umask = open(os.path.join(basedir, "umask")).read().strip() self.umask = int(umask, 8) # octal string like 0022 except EnvironmentError: self.umask = None port = open(os.path.join(basedir, "port")).read().strip() self.tub = Tub(certFile=os.path.join(basedir, "tub.pem")) self.tub.listenOn(port) self.tub.setServiceParent(self) self.tub.registerNameLookupHandler(self.lookup) print >> stdout, "Server Running" self.ready_observers = OneShotObserverList() # make sure we log any problems self.when_ready().addErrback(log.err) def when_ready(self): # return a Deferred that fires (with this AppServer instance) when # the service is running and the location is set. return self.ready_observers.whenFired() def startService(self): if self.umask is not None: os.umask(self.umask) service.MultiService.startService(self) d = self.setMyLocation() d.addBoth(self.ready_observers.fire) def setMyLocation(self): location = open(os.path.join(self.basedir, "location")).read().strip() if location: self.tub.setLocation(location) return defer.succeed(self) d = self.tub.setLocationAutomatically() d.addCallback(lambda ign: self) return d def lookup(self, name): # walk through our configured services, see if we know about this one services = load_service_data(self.basedir)["services"] s = services.get(name) if not s: return None service_basedir = os.path.join(self.basedir, s["relative_basedir"]) service_type = s["type"] service_args = s["args"] s = build_service(service_basedir, self.tub, service_type, service_args) s.setServiceParent(self) return s
class AppServer(service.MultiService): def __init__(self, basedir=".", stdout=sys.stdout): service.MultiService.__init__(self) self.basedir = os.path.abspath(basedir) try: umask = open(os.path.join(basedir, "umask")).read().strip() self.umask = int(umask, 8) # octal string like 0022 except EnvironmentError: self.umask = None self.port = open(os.path.join(basedir, "port")).read().strip() self.tub = Tub(certFile=os.path.join(basedir, "tub.pem")) self.tub.listenOn(self.port) self.tub.setServiceParent(self) self.tub.registerNameLookupHandler(self.lookup) self.setMyLocation() print("Server Running", file=stdout) def startService(self): if self.umask is not None: os.umask(self.umask) service.MultiService.startService(self) def setMyLocation(self): location_fn = os.path.join(self.basedir, "location") location = open(location_fn).read().strip() if not location: raise ValueError("This flappserver was created without " "'--location=', and Foolscap no longer uses " "IP-address autodetection. Please edit '%s' " "to contain e.g. 'tcp:example.org:12345', with " "a hostname and port number that match this " "server (we're listening on %s)" % (location_fn, self.port)) self.tub.setLocation(location) def lookup(self, name): # walk through our configured services, see if we know about this one services = load_service_data(self.basedir)["services"] service_desc = services.get(name) if service_desc: service_basedir = os.path.join(self.basedir, service_desc["relative_basedir"]) service_type = service_desc["type"] service_args = [arg for arg in service_desc["args"]] service = build_service(service_basedir, self.tub, service_type, service_args) service.setServiceParent(self) return service
class AppServer(service.MultiService): def __init__(self, basedir=".", stdout=sys.stdout): service.MultiService.__init__(self) self.basedir = os.path.abspath(basedir) try: umask = open(os.path.join(basedir, "umask")).read().strip() self.umask = int(umask, 8) # octal string like 0022 except EnvironmentError: self.umask = None port = open(os.path.join(basedir, "port")).read().strip() self.tub = Tub(certFile=os.path.join(basedir, "tub.pem")) self.tub.listenOn(port) self.tub.setServiceParent(self) self.tub.registerNameLookupHandler(self.lookup) print >>stdout, "Server Running" self.ready_observers = OneShotObserverList() # make sure we log any problems self.when_ready().addErrback(log.err) def when_ready(self): # return a Deferred that fires (with this AppServer instance) when # the service is running and the location is set. return self.ready_observers.whenFired() def startService(self): if self.umask is not None: os.umask(self.umask) service.MultiService.startService(self) d = self.setMyLocation() d.addBoth(self.ready_observers.fire) def setMyLocation(self): location = open(os.path.join(self.basedir, "location")).read().strip() if location: self.tub.setLocation(location) return defer.succeed(self) d = self.tub.setLocationAutomatically() d.addCallback(lambda ign: self) return d def lookup(self, name): # walk through our configured services, see if we know about this one service_basedir = os.path.join(self.basedir, "services", name) if not os.path.exists(service_basedir): return None service_type_f = os.path.join(service_basedir, "service_type") service_type = open(service_type_f).read().strip() service_args_f = os.path.join(service_basedir, "service_args") service_args = eval(open(service_args_f).read().strip()) s = build_service(service_basedir, self.tub, service_type, service_args) s.setServiceParent(self) return s
def createDuplicateServer(self, oldtub): tub = Tub(certData=oldtub.getCertData()) tub.startService() self.services.append(tub) tub.incarnation = oldtub.incarnation tub.incarnation_string = oldtub.incarnation_string tub.slave_table = oldtub.slave_table.copy() tub.master_table = oldtub.master_table.copy() portnum = allocate_tcp_port() tub.listenOn("tcp:%d:interface=127.0.0.1" % portnum) tub.setLocation("127.0.0.1:%d" % portnum) target = Target() return tub, target, tub.registerReference(target), portnum
def create_control_tub(): """ Creates a Foolscap Tub for use by the control port. This is a localhost-only ephemeral Tub, with no control over the listening port or location """ control_tub = Tub() portnum = iputil.allocate_tcp_port() port = "tcp:%d:interface=127.0.0.1" % portnum location = "tcp:127.0.0.1:%d" % portnum control_tub.listenOn(port) control_tub.setLocation(location) log.msg("Control Tub location set to %s" % (location,)) return control_tub
def makeTubs(self, numTubs, mangleLocation=None): self.services = [] self.tub_ports = [] for i in range(numTubs): t = Tub() t.startService() self.services.append(t) portnum = allocate_tcp_port() self.tub_ports.append(portnum) t.listenOn("tcp:%d:interface=127.0.0.1" % portnum) location = "127.0.0.1:%d" % portnum if mangleLocation: location = mangleLocation(portnum) t.setLocation(location) return self.services
class AppServer(service.MultiService): def __init__(self, basedir=".", stdout=sys.stdout): service.MultiService.__init__(self) self.basedir = os.path.abspath(basedir) try: umask = open(os.path.join(basedir, "umask")).read().strip() self.umask = int(umask, 8) # octal string like 0022 except EnvironmentError: self.umask = None self.port = open(os.path.join(basedir, "port")).read().strip() self.tub = Tub(certFile=os.path.join(basedir, "tub.pem")) self.tub.listenOn(self.port) self.tub.setServiceParent(self) self.tub.registerNameLookupHandler(self.lookup) self.setMyLocation() print >>stdout, "Server Running" def startService(self): if self.umask is not None: os.umask(self.umask) service.MultiService.startService(self) def setMyLocation(self): location_fn = os.path.join(self.basedir, "location") location = open(location_fn).read().strip() if not location: raise ValueError("This flappserver was created without " "'--location=', and Foolscap no longer uses " "IP-address autodetection. Please edit '%s' " "to contain e.g. 'example.org:12345', with a " "hostname and port number that match this " "server (we're listening on %s)" % (location_fn, self.port)) self.tub.setLocation(location) def lookup(self, name): # walk through our configured services, see if we know about this one services = load_service_data(self.basedir)["services"] s = services.get(name) if not s: return None service_basedir = os.path.join(self.basedir, s["relative_basedir"].encode("utf-8")) service_type = s["type"] service_args = [arg.encode("utf-8") for arg in s["args"]] s = build_service(service_basedir, self.tub, service_type, service_args) s.setServiceParent(self) return s
def test_failure(self): self.basedir = "introducer/NonV1Server/failure" os.makedirs(self.basedir) self.create_tub() i = TooNewServer() i.setServiceParent(self.parent) self.introducer_furl = self.central_tub.registerReference(i) tub = Tub() tub.setOption("expose-remote-exception-types", False) tub.setServiceParent(self.parent) l = tub.listenOn("tcp:0") portnum = l.getPortnum() tub.setLocation("localhost:%d" % portnum) c = IntroducerClient(tub, self.introducer_furl, u"nickname-client", "version", "oldest") announcements = {} def got(serverid, ann_d): announcements[serverid] = ann_d c.subscribe_to("storage", got) c.setServiceParent(self.parent) # now we wait for it to connect and notice the bad version def _got_bad(): return bool(c._introducer_error) or bool(c._publisher) d = self.poll(_got_bad) def _done(res): self.failUnless(c._introducer_error) self.failUnless(c._introducer_error.check(InsufficientVersionError)) d.addCallback(_done) return d
class KeyGeneratorService(service.MultiService): furl_file = 'key_generator.furl' def __init__(self, basedir='.', display_furl=True, default_key_size=2048): service.MultiService.__init__(self) self.basedir = basedir self.tub = Tub(certFile=os.path.join(self.basedir, 'key_generator.pem')) self.tub.setOption("expose-remote-exception-types", False) self.tub.setServiceParent(self) self.key_generator = KeyGenerator(default_key_size=default_key_size) self.key_generator.setServiceParent(self) portnum = self.get_portnum() self.listener = self.tub.listenOn(portnum or 'tcp:0') d = self.tub.setLocationAutomatically() if portnum is None: d.addCallback(self.save_portnum) d.addCallback(self.tub_ready, display_furl) d.addErrback(log.err) def get_portnum(self): portnumfile = os.path.join(self.basedir, 'portnum') if os.path.exists(portnumfile): return file(portnumfile, 'rb').read().strip() def save_portnum(self, junk): portnum = self.listener.getPortnum() portnumfile = os.path.join(self.basedir, 'portnum') file(portnumfile, 'wb').write('%d\n' % (portnum,)) def tub_ready(self, junk, display_furl): kgf = os.path.join(self.basedir, self.furl_file) self.keygen_furl = self.tub.registerReference(self.key_generator, furlFile=kgf) if display_furl: print 'key generator at:', self.keygen_furl
class Failed(unittest.TestCase): def setUp(self): self.services = [] def tearDown(self): d = defer.DeferredList([s.stopService() for s in self.services]) d.addCallback(flushEventualQueue) return d @defer.inlineCallbacks def test_bad_hints(self): self.tubA = Tub() self.tubA.startService() self.services.append(self.tubA) self.tubB = Tub() self.tubB.startService() self.services.append(self.tubB) portnum = allocate_tcp_port() self.tubB.listenOn("tcp:%d:interface=127.0.0.1" % portnum) bad1 = "no-colon" bad2 = "unknown:foo" bad3 = "tcp:300.300.300.300:333" self.tubB.setLocation(bad1, bad2, bad3) target = HelperTarget("bob") url = self.tubB.registerReference(target) rc = self.tubA.connectTo(url, None) ri = rc.getReconnectionInfo() self.assertEqual(ri.state, "connecting") d = defer.Deferred() reactor.callLater(1.0, d.callback, None) yield d # now look at the details ri = rc.getReconnectionInfo() self.assertEqual(ri.state, "waiting") ci = ri.connectionInfo self.assertEqual(ci.connected, False) self.assertEqual(ci.winningHint, None) s = ci.connectorStatuses self.assertEqual(set(s.keys()), set([bad1, bad2, bad3])) self.assertEqual(s[bad1], "bad hint: no colon") self.assertEqual(s[bad2], "bad hint: no handler registered") self.assertIn("DNS lookup failed", s[bad3]) ch = ci.connectionHandlers self.assertEqual(ch, {bad2: None, bad3: "tcp"})
def _testPersist_1(self, res, s1, s2, t1, public_url, port): self.services.remove(s1) s3 = Tub(certData=s1.getCertData()) s3.startService() self.services.append(s3) t2 = Target() newport = allocate_tcp_port() s3.listenOn("tcp:%d:interface=127.0.0.1" % newport) s3.setLocation("127.0.0.1:%d" % newport) s3.registerReference(t2, "name") # now patch the URL to replace the port number newurl = re.sub(":%d/" % port, ":%d/" % newport, public_url) d = s2.getReference(newurl) d.addCallback(lambda rr: rr.callRemote("add", a=1, b=2)) d.addCallback(self.failUnlessEqual, 3) d.addCallback(self._testPersist_2, t1, t2) return d
def makeTub(self, hint_type, listener_test_options={}, extra_hint=None): tubA = Tub(certData=certData_low) tubA.setServiceParent(self.s) tubB = Tub(certData=certData_high) tubB.setServiceParent(self.s) self._tubA, self._tubB = tubA, tubB portnum = util.allocate_tcp_port() self._portnum = portnum port = "tcp:%d:interface=127.0.0.1" % portnum hint = "%s:127.0.0.1:%d" % (hint_type, portnum) if extra_hint: hint = hint + "," + extra_hint tubA.listenOn(port, _test_options=listener_test_options) tubA.setLocation(hint) self._target = Target() furl = tubA.registerReference(self._target) return furl, tubB, hint
def test_json(self): """ The JSON response includes totals for the number of subscriptions and announcements of each service type. """ config = node.config_from_string(self.mktemp(), "", "") config.get_private_path = lambda ignored: self.mktemp() main_tub = Tub() main_tub.listenOn(b"tcp:0") main_tub.setLocation(b"tcp:127.0.0.1:1") introducer_node = _IntroducerNode(config, main_tub, None, None, None) introducer_service = introducer_node.getServiceNamed("introducer") for n in range(2): introducer_service.add_subscriber( FakeCanary(), "arbitrary", {"info": "info"}, ) # It would be nice to use the publish method but then we have to # generate a correctly signed message which I don't feel like doing. ann_t = ("msg", "sig", "key") ann = {"service-name": "arbitrary"} introducer_service._announcements[("arbitrary", "key")] = ( ann_t, FakeCanary(), ann, 0, ) resource = IntroducerRoot(introducer_node) response = json.loads( self.successResultOf(render(resource, {"t": [b"json"]}), ), ) self.assertEqual( response, { u"subscription_summary": { "arbitrary": 2 }, u"announcement_summary": { "arbitrary": 1 }, }, )
class GatheringBase(service.MultiService, Referenceable): # requires self.furlFile and self.tacFile to be set on the class, both of # which should be relative to the basedir. use_local_addresses = True def __init__(self, basedir): if basedir is None: # This instance was created by a gatherer.tac file. Confirm that # we're running from the right directory (the one with the .tac # file), otherwise we'll put the logfiles in the wrong place. basedir = os.getcwd() tac = os.path.join(basedir, self.tacFile) if not os.path.exists(tac): raise RuntimeError("running in the wrong directory") self.basedir = basedir service.MultiService.__init__(self) def startService(self): service.MultiService.startService(self) certFile = os.path.join(self.basedir, "gatherer.pem") self._tub = Tub(certFile=certFile) self._tub.setServiceParent(self) local_addresses = ["127.0.0.1"] local_address = get_local_ip_for() if self.use_local_addresses and local_address: local_addresses.insert(0, local_address) portnumfile = os.path.join(self.basedir, "portnum") try: desired_portnum = int(open(portnumfile, "r").read()) except (EnvironmentError, ValueError): desired_portnum = 0 l = self._tub.listenOn("tcp:%d" % desired_portnum) got_portnum = l.getPortnum() f = open(portnumfile, "w") f.write("%d\n" % got_portnum) f.close() # we can't do setLocation until we have two things: # portnum: this requires listenOn, if portnum=0 also startService # localip: this just requires a call to get_local_ip_for # so it is safe to do everything in startService, after the upcall local_addresses = [ "%s:%d" % (addr, got_portnum,) for addr in local_addresses ] assert len(local_addresses) >= 1 location = ",".join(local_addresses) self._tub.setLocation(location) self.tub_ready() def tub_ready(self): furlFile = os.path.join(self.basedir, self.furlFile) self.my_furl = self._tub.registerReference(self, furlFile=furlFile) if self.verbose: print "Gatherer waiting at:", self.my_furl
def makeService(config, reactor=reactor): parent = MultiService() basedir = FilePath(os.path.expanduser(config["basedir"])) basedir.makedirs(ignoreExistingDirectory=True) basedir.chmod(0o700) data = Data(basedir.child("config.json")) dns_server = DNSServerFactory(verbose=0) s1 = UDPServer(int(config["dns-port"]), dns.DNSDatagramProtocol(dns_server), interface=config["dns-interface"]) s1.setServiceParent(parent) s2 = TCPServer(int(config["dns-port"]), dns_server, interface=config["dns-interface"]) s2.setServiceParent(parent) s = Server(data, dns_server) s.update_records() certFile = basedir.child("tub.data").path #furlFile = basedir.child("server.furl").path t = Tub(certFile=certFile) t.setOption("keepaliveTimeout", 60) # ping after 60s of idle t.setOption("disconnectTimeout", 5*60) # disconnect/reconnect after 5m #t.setOption("logLocalFailures", True) #t.setOption("logRemoteFailures", True) #t.unsafeTracebacks = True fp = config["foolscap-port"] if not fp.startswith("tcp:"): raise usage.UsageError("I don't know how to handle non-tcp foolscap-port=") port = int(fp.split(":")[1]) assert port > 1 t.listenOn(fp) t.setLocation("tcp:%s:%d" % (config["hostname"], port)) c = Controller(data, s) cf = t.registerReference(c, furlFile=basedir.child("controller.furl").path) furl_prefix = cf[:cf.rfind("/")+1] c.set_furl_prefix(furl_prefix) t.registerNameLookupHandler(c.lookup) t.setServiceParent(parent) return parent
def createSpecificServer(self, certData, negotiationClass=negotiate.Negotiation): tub = Tub(certData=certData) tub.negotiationClass = negotiationClass tub.startService() self.services.append(tub) l = tub.listenOn("tcp:0") tub.setLocation("127.0.0.1:%d" % l.getPortnum()) target = Target() return tub, target, tub.registerReference(target), l.getPortnum()
def test_referenceables_die(self): # serialized data will not keep the referenceable alive t1 = Tub() t1.setServiceParent(self.s) portnum = allocate_tcp_port() t1.listenOn("tcp:%d:interface=127.0.0.1" % portnum) t1.setLocation("127.0.0.1:%d" % portnum) r1 = Referenceable() t2 = Tub() t2.setServiceParent(self.s) obj = ("graph tangly", r1) d = t1.serialize(obj) del r1; del obj gc.collect() d.addCallback(lambda data: self.shouldFail(KeyError, "test_referenceables_die", "unable to find reference for name", t2.unserialize, data)) return d
def test_referenceables_die(self): # serialized data will not keep the referenceable alive t1 = Tub() t1.setServiceParent(self.s) portnum = allocate_tcp_port() t1.listenOn("tcp:%d:interface=127.0.0.1" % portnum) t1.setLocation("127.0.0.1:%d" % portnum) r1 = Referenceable() t2 = Tub() t2.setServiceParent(self.s) obj = ("graph tangly", r1) d = t1.serialize(obj) del r1 del obj gc.collect() d.addCallback(lambda data: self.shouldFail( KeyError, "test_referenceables_die", "unable to find reference for name", t2.unserialize, data)) return d
def test_failure(self): self.basedir = "introducer/NonV1Server/failure" os.makedirs(self.basedir) self.create_tub() i = TooNewServer() i.setServiceParent(self.parent) self.introducer_furl = self.central_tub.registerReference(i) tub = Tub() tub.setOption("expose-remote-exception-types", False) tub.setServiceParent(self.parent) portnum = iputil.allocate_tcp_port() tub.listenOn("tcp:%d" % portnum) tub.setLocation("localhost:%d" % portnum) c = IntroducerClient(tub, self.introducer_furl, u"nickname-client", "version", "oldest", {}, fakeseq, FilePath(self.mktemp())) announcements = {} def got(key_s, ann): announcements[key_s] = ann c.subscribe_to("storage", got) c.setServiceParent(self.parent) # now we wait for it to connect and notice the bad version def _got_bad(): return bool(c._introducer_error) or bool(c._publisher) d = self.poll(_got_bad) def _done(res): self.failUnless(c._introducer_error) self.failUnless( c._introducer_error.check(InsufficientVersionError), c._introducer_error) d.addCallback(_done) return d
def makeService(config, reactor=reactor): parent = MultiService() basedir = FilePath(os.path.expanduser(config["basedir"])) basedir.makedirs(ignoreExistingDirectory=True) basedir.chmod(0o700) data = Data(basedir.child("config.json")) certFile = basedir.child("tub.data").path tub = Tub(certFile=certFile) tub.setOption("keepaliveTimeout", 60) # ping after 60s of idle tub.setOption("disconnectTimeout", 5*60) # disconnect/reconnect after 5m tub.listenOn("tcp:6319:interface=127.0.0.1") tub.setLocation("tcp:127.0.0.1:6319") tub.setServiceParent(parent) acme_path = basedir.asTextMode() acme_key = maybe_key(acme_path) cert_store = FlancerCertificateStore(data, basedir) staging = not config["really"] if staging: print("STAGING mode") le_url = LETSENCRYPT_STAGING_DIRECTORY else: print("REAL CERTIFICATE mode") le_url = LETSENCRYPT_DIRECTORY client_creator = partial(Client.from_url, reactor=reactor, url=le_url, key=acme_key, alg=RS256) r = FlancerResponder(tub, data) issuer = AcmeIssuingService(cert_store, client_creator, reactor, [r]) issuer.setServiceParent(parent) if "dyndns_furl" in data: start_dyndns_canary(tub, data["dyndns_furl"].encode("ascii")) c = Controller(tub, data, issuer) tub.registerReference(c, furlFile=basedir.child("controller.furl").path) #TimerService(5*60.0, f.timerUpdateStats).setServiceParent(parent) return parent
def test_lose_and_retry(self): tubC = Tub(self.tubB.getCertData()) connects = [] d1 = defer.Deferred() d2 = defer.Deferred() notifiers = [d1, d2] target = HelperTarget("bob") url = self.tubB.registerReference(target, "target") portb = self.tub_ports[1] rc = self.tubA.connectTo(url, self._connected, notifiers, connects) yield d1 self.assertEqual(rc.getReconnectionInfo().state, "connected") # we are now connected to tubB. Shut it down to force a disconnect. self.services.remove(self.tubB) yield self.tubB.stopService() # wait for at least one retry yield self.poll(lambda: rc.getReconnectionInfo().state == "waiting") # wait a few seconds more to give the Reconnector a chance to try and # fail a few times. It isn't easy to catch the "connecting" state since # the target is local and the kernel knows that it's not listening. # TODO: add an internal retry counter to the Reconnector that we can # poll for tests. yield self.stall(2) # now start tubC listening on the same port that tubB used to, # which should allow the connection to complete (since they both # use the same certData) self.services.append(tubC) tubC.startService() tubC.listenOn("tcp:%d:interface=127.0.0.1" % portb) tubC.setLocation("tcp:127.0.0.1:%d" % portb) url2 = tubC.registerReference(target, "target") assert url2 == url # this will fire when the second connection has been made yield d2 self.failUnlessEqual(len(connects), 2) rc.stopConnecting()
def test_set_location_automatically(self): t = Tub() l = t.listenOn("tcp:0") t.setServiceParent(self.s) d = t.setLocationAutomatically() d.addCallback(lambda res: t.registerReference(Referenceable())) def _check(furl): sr = SturdyRef(furl) portnum = l.getPortnum() for lh in sr.locationHints: self.failUnlessEqual(lh.split(":")[1], str(portnum), lh) self.failUnless("127.0.0.1:%d" % portnum in sr.locationHints) d.addCallback(_check) return d
class CancelPendingDeliveries(unittest.TestCase, StallMixin): def tearDown(self): dl = [defer.succeed(None)] if self.tubA.running: dl.append(defer.maybeDeferred(self.tubA.stopService)) if self.tubB.running: dl.append(defer.maybeDeferred(self.tubB.stopService)) d = defer.DeferredList(dl) d.addCallback(flushEventualQueue) return d def test_cancel_pending_deliveries(self): # when a Tub is stopped, any deliveries that were pending should be # discarded. TubA sends remote_one+remote_two (and we hope they # arrive in the same chunk). TubB responds to remote_one by shutting # down. remote_two should be discarded. The bug was that remote_two # would cause an unhandled error on the TubB side. self.tubA = Tub() self.tubB = Tub() self.tubA.startService() self.tubB.startService() self.tubB.listenOn("tcp:0") d = self.tubB.setLocationAutomatically() r = Receiver(self.tubB) d.addCallback(lambda res: self.tubB.registerReference(r)) d.addCallback(lambda furl: self.tubA.getReference(furl)) def _go(rref): # we want these two to get sent and received in the same hunk rref.callRemoteOnly("one") rref.callRemoteOnly("two") return r.done_d d.addCallback(_go) # let remote_two do its log.err before we move on to the next test d.addCallback(self.stall, 1.0) return d