2 from zope.interface import Interface
3 from foolscap.api import StringConstraint, TupleOf, SetOf, DictOf, Any, \
4 RemoteInterface, Referenceable
5 from old import RIIntroducerSubscriberClient_v1
6 FURL = StringConstraint(1000)
8 # old introducer protocol (v1):
10 # Announcements are (FURL, service_name, remoteinterface_name,
11 # nickname, my_version, oldest_supported)
12 # the (FURL, service_name, remoteinterface_name) refer to the service being
13 # announced. The (nickname, my_version, oldest_supported) refer to the
14 # client as a whole. The my_version/oldest_supported strings can be parsed
15 # by an allmydata.util.version.Version instance, and then compared. The
16 # first goal is to make sure that nodes are not confused by speaking to an
17 # incompatible peer. The second goal is to enable the development of
18 # backwards-compatibility code.
20 Announcement_v1 = TupleOf(FURL, str, str,
23 # v2 protocol over foolscap: Announcements are 3-tuples of (bytes, str, str)
24 # or (bytes, none, none)
25 Announcement_v2 = Any()
27 class RIIntroducerSubscriberClient_v2(RemoteInterface):
28 __remote_name__ = "RIIntroducerSubscriberClient_v2.tahoe.allmydata.com"
30 def announce_v2(announcements=SetOf(Announcement_v2)):
31 """I accept announcements from the publisher."""
34 SubscriberInfo = DictOf(str, Any())
36 class RIIntroducerPublisherAndSubscriberService_v2(RemoteInterface):
37 """To publish a service to the world, connect to me and give me your
38 announcement message. I will deliver a copy to all connected subscribers.
39 To hear about services, connect to me and subscribe to a specific
41 __remote_name__ = "RIIntroducerPublisherAndSubscriberService_v2.tahoe.allmydata.com"
43 return DictOf(str, Any())
44 def publish(announcement=Announcement_v1):
46 def publish_v2(announcement=Announcement_v2, canary=Referenceable):
48 def subscribe(subscriber=RIIntroducerSubscriberClient_v1, service_name=str):
50 def subscribe_v2(subscriber=RIIntroducerSubscriberClient_v2,
51 service_name=str, subscriber_info=SubscriberInfo):
52 """Give me a subscriber reference, and I will call its announce_v2()
53 method with any announcements that match the desired service name. I
54 will ignore duplicate subscriptions. The subscriber_info dictionary
55 tells me about the subscriber, and is used for diagnostic/status
59 class IIntroducerClient(Interface):
60 """I provide service introduction facilities for a node. I help nodes
61 publish their services to the rest of the world, and I help them learn
62 about services available on other nodes."""
64 def publish(service_name, ann, signing_key=None):
65 """Publish the given announcement dictionary (which must be
66 JSON-serializable), plus some additional keys, to the world.
68 Each announcement is characterized by a (service_name, serverid)
69 pair. When the server sees two announcements with the same pair, the
70 later one will replace the earlier one. The serverid is derived from
71 the signing_key, if present, otherwise it is derived from the
72 'anonymous-storage-FURL' key.
74 If signing_key= is set to an instance of SigningKey, it will be
75 used to sign the announcement."""
77 def subscribe_to(service_name, callback, *args, **kwargs):
78 """Call this if you will eventually want to use services with the
79 given SERVICE_NAME. This will prompt me to subscribe to announcements
80 of those services. Your callback will be invoked with at least two
81 arguments: a pubkey and an announcement dictionary, followed by any
82 additional callback args/kwargs you gave me. The pubkey will be None
83 unless the announcement was signed by the corresponding pubkey, in
84 which case it will be a printable string like 'v0-base32..'.
86 I will run your callback for both new announcements and for
87 announcements that have changed, but you must be prepared to tolerate
90 The announcement that I give you comes from some other client. It
91 will be a JSON-serializable dictionary which (by convention) is
92 expected to have at least the following keys:
100 service-name: str('storage')
101 anonymous-storage-FURL: str(furl)
103 Note that app-version will be an empty dictionary if either the
104 publishing client or the Introducer are running older code.
107 def connected_to_introducer():
108 """Returns a boolean, True if we are currently connected to the
109 introducer, False if not."""