3 from zope.interface import implements
4 from twisted.application import service
5 from twisted.python import log
6 from foolscap import Referenceable
7 from allmydata.interfaces import RIIntroducer, RIIntroducerClient
8 from allmydata.util import idlib, observer
10 class Introducer(service.MultiService, Referenceable):
11 implements(RIIntroducer)
14 service.MultiService.__init__(self)
18 def remote_hello(self, node, furl):
19 log.msg("introducer: new contact at %s, node is %s" % (furl, node))
21 log.msg(" introducer: removing %s %s" % (node, furl))
22 self.nodes.remove(node)
23 self.furls.remove(furl)
24 node.notifyOnDisconnect(_remove)
26 node.callRemote("new_peers", self.furls)
27 for othernode in self.nodes:
28 othernode.callRemote("new_peers", set([furl]))
32 class IntroducerClient(service.Service, Referenceable):
33 implements(RIIntroducerClient)
35 def __init__(self, tub, introducer_furl, my_furl):
37 self.introducer_furl = introducer_furl
38 self.my_furl = my_furl
40 self.connections = {} # k: nodeid, v: ref
41 self.reconnectors = {} # k: FURL, v: reconnector
43 self.connection_observers = observer.ObserverList()
45 def startService(self):
46 self.introducer_reconnector = self.tub.connectTo(self.introducer_furl,
52 def remote_new_peers(self, furls):
56 def stopService(self):
57 service.Service.stopService(self)
58 self.introducer_reconnector.stopConnecting()
59 for reconnector in self.reconnectors.itervalues():
60 reconnector.stopConnecting()
62 def _new_peer(self, furl):
63 if furl in self.reconnectors:
65 # TODO: rather than using the TubID as a nodeid, we should use
66 # something else. The thing that requires the least additional
67 # mappings is to use the foolscap "identifier" (the last component of
68 # the furl), since these are unguessable. Before we can do that,
69 # though, we need a way to conveniently make these identifiers
70 # persist from one run of the client program to the next. Also, using
71 # the foolscap identifier would mean that anyone who knows the name
72 # of the node also has all the secrets they need to contact and use
73 # them, which may or may not be what we want.
74 m = re.match(r'pb://(\w+)@', furl)
76 nodeid = idlib.a2b(m.group(1))
78 self.log(" connected to(%s)" % idlib.b2a(nodeid))
79 self.connection_observers.notify(nodeid, rref)
80 self.connections[nodeid] = rref
82 # TODO: notifyOnDisconnect uses eventually(), but connects do
83 # not. Could this cause a problem?
84 del self.connections[nodeid]
85 rref.notifyOnDisconnect(_lost)
86 self.log(" connecting to(%s)" % furl)
87 self.reconnectors[furl] = self.tub.connectTo(furl, _got_peer)
89 def _got_introducer(self, introducer):
90 self.log(" introducing ourselves: %s, %s" % (self, self.my_furl))
91 d = introducer.callRemote("hello",
95 def notify_on_new_connection(self, cb):
96 """Register a callback that will be fired (with nodeid, rref) when
97 a new connection is established."""
98 self.connection_observers.subscribe(cb)